From f97d8372311b63c086675063674f54d6fb312903 Mon Sep 17 00:00:00 2001 From: Sam Hatchett Date: Tue, 9 Jan 2018 16:56:42 -0500 Subject: [PATCH] Feature wrapper (#136) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit this quite sizable commit does several things, but is primarily focussed on building a toolkit that can run simultaneous simulations/analyses within a shared memory space. Versions <=2.1 use a long list of global variables that prevent multiple instantiations on linux systems without resorting to compilation tricks (like duplicate binaries or similar via static linkage). This version uses a single "Project" pointer to encapsulate the network and analysis data. There are no changes to existing algo implementations other than to accomodate dereferencing of the passed-in pointers. A more detailed list of major changes below: - mirrors all “ENxxxx” function calls with “EN_xxxx” versions (note the underscore) that take an extra first parameter: a pointer to an EN_Project struct, which contains all network, hydraulic, quality, and associated data. - tweaks some code formatting to make it more readable - removes some deprecated/commented code that was sufficiently old - fixes implicit type-cast warnings * Added ENaddnode and ENaddlink functions * More memory reallocations * Added ENInit, ENsetheadcurveindex * Added ENdeletelink and ENdeletenode * restored default behavior for float types * fixed type * Added docstrings for ENinit * cleanup change * moves global rule variables to vars.h * migrates rule structs to typedefs for better readability * char types to proper enums fixes #93 * Change some variable declarations for compatibility Changes to keep compatibility with C89 compilers: variables must be declared at the top of the functions. Remove the use of EN_LinkType in function call as it is not compatible with ENgetnodetype. * Moved declaration of idstodelete to top of function * Updated ENinit function and headers Updated header files with new functions Updated def file with new functions For ENinit changes names of parameters #98 Added enum for headloss formula * Missed these files in 1a033fc * migrates char types to enums fixes #93, supports unified link/node type enums, rather than public/private redefinitions * removing links in reverse-index order maintains proper indexing fixes #96 * style * clarifies curve getter units issue (dox) closes #95 * fixes link/node confusion in ENsetlinktype partially reverts a3bce95dc330a5a297634a303d438e2e1bc41cc9 * partial compilation fix * fixes dox issue * fixes allocation issues with enums - updates style in various places - introduces FlowDirection enum - use snprintf to prevent overflow * fixes enum type cast * updated mac project settings * Use of _snprintf on Windows and remove DLLEXPORT from mempool.h snprintf it not compatible on Windows so we use _snprintf mempool gave starnge compilation errors while removing DLLEXPORT worked. Not sure why these functions needed to be exposed in the DLL? * Revert "Use of _snprintf on Windows and remove DLLEXPORT from mempool.h" This reverts commit 6238f77d47fa0feaabe5836043c006937de433a2. * use of _snprintf instead of snprintf on Windows and removed DLLEXPORT from mempool.h Had compilation errors on mempool.h. Removed DLLEXPORT so solve it. Not sure why there was a need to expose these functions? * Shift indices for Links in ENaddnode Need to shift indices for Links not just Pipes since a pump could be connected directly to a reservoir. Also set the defult base demand to zero (was 5). * Set defualts for madatory link properties in ENaddlink and small fix for ENsetheadcurveindex Relates to #102 and closes #103 * wraps globals into structs, duplicates api functions with objective versions * parse and serialize Comment field for network elements related to #47 * adds getter for head/efficiency curve in EN_getlinkvalue * adds getter for event node index … to return the index of the junction (tank) that triggered the event. * fixes edge case in parsing … where inp files without demands in [JUNCTIONS] and without any [DEMAND] categories will fail. * adds freeing function for project pointer * removes redundant string literals, fixes overrun issue in error message getter function * check for hydraulics already closed * moving error definitions to data file * deprecates ENR err message getter (unused) * updates location of errors data file also begins to expose blind structs to curves and patterns, anticipating buildout of APIs for those. * updates CLI output to reflect executable name as invoked relates to #109 * Feature nrtest (#131) * Initial commit EPANET testing tools. * Initial commit for epanet-nrtestsuite * SWIG wrapper for EPANET outputapi (#118) * Removed pervious version of outputapi and wrapper * SWIG wrapper for EPANET outputapi * Patching cmake build script fixed target for outputapi * Build failing on deprecated test script * Minor changes. Responding to review comments. * Feature nrtest (#121) * Configured python setup to automatically build nrtest tools. * Working on build / testing automation * Adding EPANET 2.0.12 benchmark * Updated Travis yml to run nrtest * Fixing InsecurePlatformWarning * Fixing InsecurePlatformWarning again * Fixing InsecurePlatformWarning * Fixing InsecurePlatformWarning * Fixing InsecurePlatformWarning * Update .travis.yml * Update .travis.yml * Update .travis.yml * Update .travis.yml * Working on configuring python environment and building test tools under Travis CI. * Making gen-config.sh and run-nrtest.sh executable * Debugging .travis.yml * Debugging .travis.yml * Debugging .travis.yml again * Debugging .travis.yml again * debugging travis setup * debugging Travis setup * debugging Travis setup * debugging Travis setup * debugging Travis setup * debugging Travis setup * debugging Travis setup * debugging Travis setup * debugging Travis setup * debugging Travis setup * debugging Travis setup * Fixing bug with __strncpy_chk destlen < len * nrtesting clean up * re-implements fixes from: 5eead5ae40e46c39e84ddf5002083c3e6622403c 3c788567a43eb6f84c6fedab7586da5402b27d9c * removes extraneous build files, moves cmake and updates travis * mirror of 9b37035560f9683f1514b439f7586a5c17bca5bf * Move some variable declarations * More variable declarations * Fix TmpDir * Allocate _defaultModel * Fix EN_addcurve funcrion * Fix for inpfile * Fix writeRuleinInp call * Set MAXMSG to 79 chars * Fix for flow direction * Refactoring testing related python packages and SWIG wrapper bug fix (#139) * Eliminated epanet-reader package. Removed numpy dependency from epanet-output. Fixed reference counting bug in SWIG wrapper. Added error checking to run_nrtest.sh. Added nrtest package to requirements file. * changing buildhome directory * Fixing bug related to preprocessor definition of PI --- .gitignore | 21 +- .travis.yml | 25 +- CMakeLists.txt | 55 + build/CMake/CMakeLists.txt | 28 - build/Linux/Makefile | 109 - build/Linux/Makefile_Rev | 111 - build/Linux/runepanet.sh.template | 3 - build/MinGW/Makefile | 78 - build/Xcode/epanet.xcodeproj/project.pbxproj | 773 -- include/epanet2.bas | 16 +- include/epanet2.h | 2268 ++--- include/epanet2.vb | 14 +- run/main.c | 9 +- src/enumstxt.h | 266 +- src/epanet.c | 8173 ++++++++++------- src/errors.dat | 75 + src/funcs.h | 579 +- src/hash.c | 44 +- src/hash.h | 4 +- src/hydraul.c | 5464 ++++++----- src/inpfile.c | 1371 +-- src/input1.c | 1361 +-- src/input2.c | 2028 ++-- src/input3.c | 4090 +++++---- src/mempool.c | 414 +- src/mempool.h | 12 +- src/output.c | 1546 ++-- src/quality.c | 3638 ++++---- src/report.c | 2595 +++--- src/rules.c | 2573 +++--- src/smatrix.c | 1561 ++-- src/text.h | 1020 +- src/types.h | 1369 +-- src/vars.h | 227 +- tests/.gitignore | 9 + .../epanet-nrtestsuite/apps/epanet-2011a.json | 7 + .../epanet-nrtestsuite/apps/epanet-2012.json | 7 + .../epanet-2012/Example_1/example1.out | Bin 0 -> 16832 bytes .../epanet-2012/Example_1/example1.rpt | 1029 +++ .../epanet-2012/Example_1/performance.json | 4 + .../epanet-2012/Example_2/example2.out | Bin 0 -> 108236 bytes .../epanet-2012/Example_2/example2.rpt | 5176 +++++++++++ .../epanet-2012/Example_2/performance.json | 4 + .../epanet-2012/Example_3/example3.out | Bin 0 -> 144692 bytes .../epanet-2012/Example_3/example3.rpt | 5852 ++++++++++++ .../epanet-2012/Example_3/performance.json | 4 + .../benchmark/epanet-2012/manifest.json | 42 + .../tests/examples/example1.inp | 180 + .../tests/examples/example1.json | 17 + .../tests/examples/example2.dat | 80 + .../tests/examples/example2.inp | 310 + .../tests/examples/example2.json | 17 + .../tests/examples/example3.inp | 481 + .../tests/examples/example3.json | 17 + tools/.gitignore | 27 + tools/epanet-output/setup.py | 39 + tools/epanet-output/src/epanet_output.c | 969 ++ tools/epanet-output/src/epanet_output.h | 129 + tools/epanet-output/src/epanet_output.i | 254 + tools/epanet-output/src/errormanager.c | 74 + tools/epanet-output/src/errormanager.h | 27 + tools/epanet-output/src/messages.h | 29 + tools/epanet-output/test/data/__init__.py | 14 + tools/epanet-output/test/data/net1.out | Bin 0 -> 16832 bytes .../epanet-output/test/epanet_output_test.py | 102 + .../epanet-output/test/test_epanet_output.cpp | 251 + tools/gen-config.sh | 46 + tools/nrtest-epanet/main.py | 136 + tools/nrtest-epanet/nrtest_epanet/__init__.py | 121 + .../nrtest_epanet/output_reader.py | 69 + tools/nrtest-epanet/setup.py | 44 + tools/outputapi/ENBinaryOutDiff.py | 1 - tools/outputapi/ENOutputWrapper.py | 1 - tools/outputapi/outputapi.c | 502 - tools/outputapi/outputapi.h | 122 - tools/requirements.txt | 16 + tools/run-nrtest.sh | 58 + 77 files changed, 37683 insertions(+), 20504 deletions(-) create mode 100644 CMakeLists.txt delete mode 100644 build/CMake/CMakeLists.txt delete mode 100755 build/Linux/Makefile delete mode 100755 build/Linux/Makefile_Rev delete mode 100644 build/Linux/runepanet.sh.template delete mode 100644 build/MinGW/Makefile delete mode 100755 build/Xcode/epanet.xcodeproj/project.pbxproj mode change 100755 => 100644 include/epanet2.h mode change 100755 => 100644 src/epanet.c create mode 100644 src/errors.dat mode change 100755 => 100644 src/inpfile.c mode change 100755 => 100644 src/input1.c mode change 100755 => 100644 src/input2.c mode change 100755 => 100644 src/input3.c mode change 100755 => 100644 src/output.c mode change 100755 => 100644 src/quality.c mode change 100755 => 100644 src/report.c mode change 100755 => 100644 src/rules.c create mode 100644 tests/.gitignore create mode 100644 tests/epanet-nrtestsuite/apps/epanet-2011a.json create mode 100644 tests/epanet-nrtestsuite/apps/epanet-2012.json create mode 100644 tests/epanet-nrtestsuite/benchmark/epanet-2012/Example_1/example1.out create mode 100644 tests/epanet-nrtestsuite/benchmark/epanet-2012/Example_1/example1.rpt create mode 100644 tests/epanet-nrtestsuite/benchmark/epanet-2012/Example_1/performance.json create mode 100644 tests/epanet-nrtestsuite/benchmark/epanet-2012/Example_2/example2.out create mode 100644 tests/epanet-nrtestsuite/benchmark/epanet-2012/Example_2/example2.rpt create mode 100644 tests/epanet-nrtestsuite/benchmark/epanet-2012/Example_2/performance.json create mode 100644 tests/epanet-nrtestsuite/benchmark/epanet-2012/Example_3/example3.out create mode 100644 tests/epanet-nrtestsuite/benchmark/epanet-2012/Example_3/example3.rpt create mode 100644 tests/epanet-nrtestsuite/benchmark/epanet-2012/Example_3/performance.json create mode 100644 tests/epanet-nrtestsuite/benchmark/epanet-2012/manifest.json create mode 100644 tests/epanet-nrtestsuite/tests/examples/example1.inp create mode 100644 tests/epanet-nrtestsuite/tests/examples/example1.json create mode 100644 tests/epanet-nrtestsuite/tests/examples/example2.dat create mode 100644 tests/epanet-nrtestsuite/tests/examples/example2.inp create mode 100644 tests/epanet-nrtestsuite/tests/examples/example2.json create mode 100644 tests/epanet-nrtestsuite/tests/examples/example3.inp create mode 100644 tests/epanet-nrtestsuite/tests/examples/example3.json create mode 100644 tools/.gitignore create mode 100644 tools/epanet-output/setup.py create mode 100644 tools/epanet-output/src/epanet_output.c create mode 100644 tools/epanet-output/src/epanet_output.h create mode 100644 tools/epanet-output/src/epanet_output.i create mode 100644 tools/epanet-output/src/errormanager.c create mode 100644 tools/epanet-output/src/errormanager.h create mode 100644 tools/epanet-output/src/messages.h create mode 100644 tools/epanet-output/test/data/__init__.py create mode 100644 tools/epanet-output/test/data/net1.out create mode 100644 tools/epanet-output/test/epanet_output_test.py create mode 100644 tools/epanet-output/test/test_epanet_output.cpp create mode 100755 tools/gen-config.sh create mode 100644 tools/nrtest-epanet/main.py create mode 100644 tools/nrtest-epanet/nrtest_epanet/__init__.py create mode 100644 tools/nrtest-epanet/nrtest_epanet/output_reader.py create mode 100644 tools/nrtest-epanet/setup.py delete mode 100644 tools/outputapi/ENBinaryOutDiff.py delete mode 100644 tools/outputapi/ENOutputWrapper.py delete mode 100644 tools/outputapi/outputapi.c delete mode 100644 tools/outputapi/outputapi.h create mode 100644 tools/requirements.txt create mode 100755 tools/run-nrtest.sh diff --git a/.gitignore b/.gitignore index dad50a1..9359679 100755 --- a/.gitignore +++ b/.gitignore @@ -41,7 +41,6 @@ xcuserdata *~.nib/ *.swp *~ -*.dat *.dep *.xcodeproj/ @@ -62,7 +61,7 @@ xcuserdata [Dd]ebug/ [Rr]elease/ x64/ -build/ +#build/ [Bb]in/ [Oo]bj/ @@ -202,3 +201,21 @@ $RECYCLE.BIN/ # WindSDK compiled folders build/WinSDK/32bit/ build/WinSDK/64bit/ +======= + + +# Python compiler files +*.py[c] + +# Python distribution and packaging +dist/ +temp/ +*.cfg +*.egg-info/ + +# Eclipse Stuff +.metadata/ +.settings/ +.project +.cproject +.pydevproject diff --git a/.travis.yml b/.travis.yml index 717ee37..69f29f6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,12 +1,25 @@ -language: c +language: python + +env: + global: + - EPANET_HOME=`pwd` + - BUILD_HOME=buildproducts + - TEST_HOME=tests/epanet-nrtestsuite + +before_install: + - sudo apt-get -qq update + - sudo apt-get install -y swig + +install: + - pip install --src build/packages -r tools/requirements.txt before_script: - - cd build/CMake - - mkdir buildproducts - - cd buildproducts + - mkdir -p $BUILD_HOME + - cd $BUILD_HOME - cmake .. script: - make - - cd ../../../tests - - ./test_networks.sh + - cd $EPANET_HOME + - tools/gen-config.sh $EPANET_HOME/$BUILD_HOME/bin > $TEST_HOME/apps/epanet-$TRAVIS_COMMIT.json + - tools/run-nrtest.sh $TEST_HOME $TRAVIS_COMMIT diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..c655fcd --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,55 @@ +# CMakeLists.txt - CMake configuration file for EPANET 2.0 +# +# CMake is a cross-platform build tool. CMake generates platform native +# makefiles that can be used with your compiler of choice. CMake uses a +# generator concept to represent different build tooling. CMake automatically +# detects the platform it is running on and generates the appropriate makefiles +# for the platform default compiler. Different generators can also be specified. +# +# Note: CMake requires that your platform build system and compiler are +# properly installed. Build using Visual Studio requires msbuild shell. +# +# Example Usage: +# cd build/cmake +# mkdir buildproducts +# cd buildproducts +# cmake .. +# make +# +# Building MSYS on Windows: +# ... +# cmake -G "MSYS Makefiles" .. +# make +# +# Building Visual Studio on Windows: +# ... +# cmake -G "Visual Studio 10 2010" .. +# msbuild /p:Configuration=Release ALL_BUILD.vcxproj +# +# More information: +# cmake --help +# +# CMake is available at https://cmake.org/download/ +# + +cmake_minimum_required (VERSION 2.6) + +SET(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin) +SET(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin) + +project(EPANET) + +IF(APPLE) + SET(CMAKE_INSTALL_NAME_DIR @executable_path) + SET(CMAKE_BUILD_WITH_INSTALL_RPATH ON) +ENDIF(APPLE) + +# the library +include_directories(include) +file(GLOB EPANET_SOURCES src/*.c) +add_library(epanet STATIC ${EPANET_SOURCES}) + +# the standalone executable +include_directories(src) +add_executable(runepanet run/main.c) +target_link_libraries (runepanet LINK_PUBLIC epanet m) diff --git a/build/CMake/CMakeLists.txt b/build/CMake/CMakeLists.txt deleted file mode 100644 index e3e65aa..0000000 --- a/build/CMake/CMakeLists.txt +++ /dev/null @@ -1,28 +0,0 @@ -## cmake . -## make - -cmake_minimum_required (VERSION 2.6) - -SET(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin) -SET(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin) - -project (EPANET) - -IF(APPLE) - SET(CMAKE_INSTALL_NAME_DIR @executable_path) - SET(CMAKE_BUILD_WITH_INSTALL_RPATH ON) -ENDIF(APPLE) - -# the library -include_directories(../../include) -file(GLOB EPANET_SOURCES ../../src/*.c) -add_library(epanet STATIC ${EPANET_SOURCES}) - -# the standalone executable -include_directories(../../src) -add_executable(runepanet ../../run/main.c) -target_link_libraries (runepanet LINK_PUBLIC epanet m) - -# the binary hydraulics file API -include_directories(../../tools/outputapi) -add_library(ENBinaryOut SHARED ../../tools/outputapi/outputapi.c) diff --git a/build/Linux/Makefile b/build/Linux/Makefile deleted file mode 100755 index 584c1ad..0000000 --- a/build/Linux/Makefile +++ /dev/null @@ -1,109 +0,0 @@ -# Linux Makefile for EPANET - -# This will build EPANET as a shared object library -# (libepanet2.so) under Linux/Unix, and a standalone -# executable (epanet2). - -# The following targets are defined: -# make -# -Builds libepanet2.so, epanet2 -# make install -# -Creates shell wrapper runepanet2.sh that executes epanet2. -# The runepanet2.sh wrapper simply exports -# environment variables that help locate the runtime library, -# allowing you to specify your own library locations. -# -Installs epanet2 and runepanet2.sh -# in /bin, where defaults to ~ (and can be set -# below to something different - see "Install directories") -# -Installs libepanet2.so in /lib -# -Installs epanet2.h in /include. This is the required -# header file for the EPANET programmer's toolkit, and thus -# /include should be on your CPP include search path -# for subsequent use of the toolkit and linking with the -# library libepanet2.so -# make clean -# -Removes object and library files, returning the build directory -# to its pristine state. - -# You may wish to change the install path 'prefix', -# or the compiler flags, but these defaults should be fine. - -SHELL = /bin/sh - -# Target filenames -epanetrootname := epanet2 -libname := lib$(epanetrootname).so -exename := $(epanetrootname) -# Shell wrapper -runcmdtemplate = runepanet.sh.template -runcmdname = runepanet2.sh -# Location of EPANET toolkit includes -epanetincludedir = ../../include -# Search path for sources -epanetsrcdir = ../../src -epanetrundir = ../../run -VPATH = $(epanetsrcdir):$(epanetincludedir):$(epanetrundir) - -# Install directories -#prefix = /usr/local # GNU standard -- requires superuser rights -prefix =~ -# -exec_prefix = $(prefix) -libdir = $(exec_prefix)/lib -bindir = $(exec_prefix)/bin -includedir = $(prefix)/include -datarootdir = $(prefix)/share -docdir = $(datarootdir)/doc/epanet - -# Compiler and flags -CC = gcc -CFLAGS = -g -O3 -fPIC -std=c99 -CPPFLAGS = -I $(epanetincludedir) -LDFLAGS = -L . -Wl,-rpath,$(libdir) -lm - -# Installer -INSTALL = install -INSTALL_PROGRAM = $(INSTALL) -INSTALL_DATA = $(INSTALL) -m 644 - -# Files for the shared object library -epanet_objs=hash.o hydraul.o inpfile.o input1.o input2.o \ - input3.o mempool.o output.o quality.o report.o \ - rules.o smatrix.o epanet.o -# Epanet header files -epanet_heads=enumstxt.h funcs.h hash.h mempool.h text.h types.h vars.h -# Epanet main program -epanet_main=main -# Epanet main program header files -epanet_main_heads=epanet2.h - -.PHONY: all -all: $(libname) $(exename) - -$(libname): $(epanet_objs) - $(CC) $(CFLAGS) $(LDFLAGS) -shared -o $@ $^ - -$(exename): $(epanet_main).o $(epanet_main_heads) - $(CC) $(CFLAGS) -o $@ $(epanet_main).o -l$(epanetrootname) $(LDFLAGS) - -$(epanet_objs): $(epanet_heads) - -.PHONY: install -install: - cat $(runcmdtemplate) | sed 's|libdir|$(libdir)|' \ - | sed 's|exename|$(bindir)/$(exename)|' \ - > $(runcmdname) - $(INSTALL_PROGRAM) -D $(exename) $(bindir)/$(exename) - $(INSTALL_PROGRAM) -D $(libname) $(libdir)/$(libname) - $(INSTALL_DATA) -D $(epanetincludedir)/epanet2.h $(includedir)/epanet2.h - $(INSTALL_PROGRAM) -D $(runcmdname) ~/$(runcmdname) - -.PHONY: uninstall -uninstall: - -.PHONY: check -check: - -.PHONY: clean -clean: - -/bin/rm *.o $(libname) $(exename) $(runcmdname) diff --git a/build/Linux/Makefile_Rev b/build/Linux/Makefile_Rev deleted file mode 100755 index 362725d..0000000 --- a/build/Linux/Makefile_Rev +++ /dev/null @@ -1,111 +0,0 @@ -# Linux Makefile for EPANET - -# This will build EPANET as a shared object library -# (libepanet_gcc_.so) under Linux/Unix, and a standalone -# executable (epanet_gcc_). - -# The following targets are defined: -# make -# -Builds libepanet_gcc_.so, epanet_gcc_ -# make install -# -Creates shell wrapper runepanet_.sh that executes epanet_gcc_.exe. -# The runepanet_.sh wrapper simply exports -# environment variables that help locate the runtime library, -# allowing you to specify your own library locations. -# -Installs epanet_gcc_ and runepanet_.sh -# in /bin, where defaults to ~ (and can be set -# below to something different - see "Install directories") -# -Installs libepanet_gcc_.so in /lib -# -Installs epanet2.h in /include. This is the required -# header file for the EPANET programmer's toolkit, and thus -# /include should be on your CPP include search path -# for subsequent use of the toolkit and linking with the -# library libepanet_gcc_.so -# make clean -# -Removes object and library files, returning the build directory -# to its pristine state. - -# You may wish to change the install path 'prefix', -# or the compiler flags, but these defaults should be fine. - -SHELL = /bin/sh - -# Target filenames -# svnname.sh constructs a name: -# where is the atomic revision number of the svn repo. -epanetsvnpath = ../../.. -epanetrootname := $(shell ../svnname.sh $(epanetsvnpath) "" epanet_gcc_ "") -libname := lib$(epanetrootname).so -exename := $(epanetrootname) -# Shell wrapper -runcmdtemplate = runepanet.sh.template -runcmdname = $(shell ../svnname.sh $(epanetsvnpath) "" runepanet_ .sh) -# Location of EPANET toolkit includes -epanetincludedir = ../../include -# Search path for sources -epanetsrcdir = ../../src -VPATH = $(epanetsrcdir):$(epanetincludedir) - -# Install directories -prefix = ~ -exec_prefix = $(prefix) -libdir = $(exec_prefix)/lib -bindir = $(exec_prefix)/bin -includedir = $(prefix)/include -datarootdir = $(prefix)/share -docdir = $(datarootdir)/doc/epanet - -# Compiler and flags -CC = gcc -CFLAGS = -g -O3 -fPIC -std=c99 -CPPFLAGS = -I $(epanetincludedir) -LDFLAGS = -L . -Wl,-rpath,$(libdir) -lm - -# Installer -INSTALL = install -INSTALL_PROGRAM = $(INSTALL) -INSTALL_DATA = $(INSTALL) -m 644 - -# Files for the shared object library -epanet_objs=hash.o hydraul.o inpfile.o input1.o input2.o \ - input3.o mempool.o output.o quality.o report.o \ - rules.o smatrix.o -# Epanet header files -epanet_heads=enumstxt.h funcs.h hash.h mempool.h text.h toolkit.h types.h vars.h -# Epanet main program -epanet_main=epanet -# Epanet main program header files -epanet_main_heads=epanet2.h - -.PHONY: all -all: $(libname) $(exename) - -$(libname): $(epanet_objs) - $(CC) $(CFLAGS) $(CPPFLAGS) -D SOL -c $(epanetsrcdir)/$(epanet_main).c - $(CC) $(CFLAGS) $(LDFLAGS) -shared -o $@ $(epanet_main).o $^ - -$(exename): $(libname) $(epanet_main_heads) - $(CC) $(CFLAGS) $(CPPFLAGS) -D CLE -c $(epanetsrcdir)/$(epanet_main).c - $(CC) $(CFLAGS) -o $@ $(epanet_main).o -l$(epanetrootname) $(LDFLAGS) - -$(epanet_objs): $(epanet_heads) - -.PHONY: install -install: - cat $(runcmdtemplate) | sed 's|libdir|$(libdir)|' \ - | sed 's|exename|$(bindir)/$(exename)|' \ - > $(runcmdname) - $(INSTALL_PROGRAM) -D $(exename) $(bindir)/$(exename) - $(INSTALL_PROGRAM) -D $(libname) $(libdir)/$(libname) - $(INSTALL_DATA) -D $(epanetincludedir)/epanet2.h $(includedir)/epanet2.h - $(INSTALL_PROGRAM) -D $(runcmdname) $(bindir)/$(runcmdname) - -.PHONY: uninstall -uninstall: - -.PHONY: check -check: - -.PHONY: clean -clean: - -/bin/rm *.o $(libname) $(exename) $(runcmdname) diff --git a/build/Linux/runepanet.sh.template b/build/Linux/runepanet.sh.template deleted file mode 100644 index 253dbc8..0000000 --- a/build/Linux/runepanet.sh.template +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -export LD_LIBRARY_PATH=libdir:$LD_LIBRARY_PATH -exec exename $* diff --git a/build/MinGW/Makefile b/build/MinGW/Makefile deleted file mode 100644 index eae49e2..0000000 --- a/build/MinGW/Makefile +++ /dev/null @@ -1,78 +0,0 @@ -# MINGW32 Makefile for EPANET - -# This will build EPANET as DLL -# (epanet2.dll) under MINGW32 GCC, and a standalone -# executable (epanet2d.exe). - -# The following targets are defined: -# make -# -Builds epanet2.dll, epanet2d.exe -# make install -# -Copy epanet2.dll, epanet2d.exe, epanet2.def to $(prefix) -# make clean -# -Removes object and library files, returning the build directory -# to its pristine state. - -# You may wish to change the install path 'prefix', -# or the compiler flags, but these defaults should be fine. - - -# Target filenames -epanetrootname := epanet2 -libname := $(epanetrootname).dll -exename := $(epanetrootname)d.exe - -# Location of EPANET toolkit includes -epanetincludedir = ../../include -# Search path for sources -epanetsrcdir = ../../src -# Search path for sources -epanetmainsrcdir = ../../run -VPATH = $(epanetsrcdir):$(epanetincludedir):$(epanetmainsrcdir) - -# Install directories -prefix = C:\discoD\EPA\EPAnet_util -execdir = $(prefix) -libdir = $(prefix) - - -# Compiler and flags -CC = gcc -CFLAGS = -g -O3 -std=c99 -Wno-implicit-function-declaration -D DLL -CPPFLAGS = -I $(epanetincludedir) -I $(epanetsrcdir) -LDFLAGS = -L . -Wl,--kill-at,--enable-stdcall-fixup,-rpath,$(libdir) -lm - - -# Files for the shared object library -epanet_objs=hash.o hydraul.o inpfile.o input1.o input2.o \ - input3.o mempool.o output.o quality.o report.o \ - rules.o smatrix.o epanet.o -# Epanet header files -epanet_heads=enumstxt.h funcs.h hash.h mempool.h text.h types.h vars.h epanet2.h -# Epanet main program -epanet_main=main -# Epanet main program header files -epanet_main_heads=epanet2.h - -.PHONY: all -all: $(libname) $(exename) - -$(libname): $(epanet_objs) - $(CC) $(CFLAGS) $(LDFLAGS) -shared -o $@ $^ -Wl,--output-def,$(epanetrootname).def - -$(exename): $(epanet_main_heads) $(libname) $(epanet_main).o - $(CC) $(CFLAGS) -o $@ $(epanet_main).o -l$(epanetrootname) $(LDFLAGS) - -$(epanet_objs): $(epanet_heads) - - -.PHONY: clean -clean: - del $(epanet_main).o $(epanet_objs) $(exename) $(libname) $(epanetrootname).def - -.PHONY: install -install: - copy $(exename) $(execdir) - copy $(libname) $(libdir) - copy $(epanetrootname).def $(libdir) - diff --git a/build/Xcode/epanet.xcodeproj/project.pbxproj b/build/Xcode/epanet.xcodeproj/project.pbxproj deleted file mode 100755 index 76e0349..0000000 --- a/build/Xcode/epanet.xcodeproj/project.pbxproj +++ /dev/null @@ -1,773 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 46; - objects = { - -/* Begin PBXBuildFile section */ - 223109E61BA865930030AAE8 /* Net3.inp in CopyFiles */ = {isa = PBXBuildFile; fileRef = 223109E31BA8658A0030AAE8 /* Net3.inp */; }; - 223109EB1BA869DA0030AAE8 /* main.c in Sources */ = {isa = PBXBuildFile; fileRef = 22CD9A5D1B27898E00B65E83 /* main.c */; }; - 223109ED1BA869DA0030AAE8 /* libepanet-static.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2255753B17551217009946B1 /* libepanet-static.a */; }; - 223109EF1BA869DA0030AAE8 /* Net3.inp in CopyFiles */ = {isa = PBXBuildFile; fileRef = 223109E31BA8658A0030AAE8 /* Net3.inp */; }; - 223109F61BA869F30030AAE8 /* epanet.c in Sources */ = {isa = PBXBuildFile; fileRef = 22322F711068369500641384 /* epanet.c */; }; - 223109F71BA869F30030AAE8 /* hash.c in Sources */ = {isa = PBXBuildFile; fileRef = 22322F731068369500641384 /* hash.c */; }; - 223109F81BA869F30030AAE8 /* hydraul.c in Sources */ = {isa = PBXBuildFile; fileRef = 22322F751068369500641384 /* hydraul.c */; }; - 223109F91BA869F30030AAE8 /* inpfile.c in Sources */ = {isa = PBXBuildFile; fileRef = 22322F761068369500641384 /* inpfile.c */; }; - 223109FA1BA869F30030AAE8 /* input1.c in Sources */ = {isa = PBXBuildFile; fileRef = 22322F771068369500641384 /* input1.c */; }; - 223109FB1BA869F30030AAE8 /* input2.c in Sources */ = {isa = PBXBuildFile; fileRef = 22322F781068369500641384 /* input2.c */; }; - 223109FC1BA869F30030AAE8 /* input3.c in Sources */ = {isa = PBXBuildFile; fileRef = 22322F791068369500641384 /* input3.c */; }; - 223109FD1BA869F30030AAE8 /* mempool.c in Sources */ = {isa = PBXBuildFile; fileRef = 22322F7A1068369500641384 /* mempool.c */; }; - 223109FE1BA869F30030AAE8 /* output.c in Sources */ = {isa = PBXBuildFile; fileRef = 22322F7C1068369500641384 /* output.c */; }; - 223109FF1BA869F30030AAE8 /* quality.c in Sources */ = {isa = PBXBuildFile; fileRef = 22322F7D1068369500641384 /* quality.c */; }; - 22310A001BA869F30030AAE8 /* report.c in Sources */ = {isa = PBXBuildFile; fileRef = 22322F7E1068369500641384 /* report.c */; }; - 22310A011BA869F30030AAE8 /* rules.c in Sources */ = {isa = PBXBuildFile; fileRef = 22322F7F1068369500641384 /* rules.c */; }; - 22310A021BA869F30030AAE8 /* smatrix.c in Sources */ = {isa = PBXBuildFile; fileRef = 22322F801068369500641384 /* smatrix.c */; }; - 22310A051BA869F30030AAE8 /* epanet2.h in Headers */ = {isa = PBXBuildFile; fileRef = 22322FA9106836B000641384 /* epanet2.h */; }; - 22322F851068369500641384 /* enumstxt.h in Headers */ = {isa = PBXBuildFile; fileRef = 22322F701068369500641384 /* enumstxt.h */; }; - 22322F861068369500641384 /* epanet.c in Sources */ = {isa = PBXBuildFile; fileRef = 22322F711068369500641384 /* epanet.c */; }; - 22322F871068369500641384 /* funcs.h in Headers */ = {isa = PBXBuildFile; fileRef = 22322F721068369500641384 /* funcs.h */; }; - 22322F881068369500641384 /* hash.c in Sources */ = {isa = PBXBuildFile; fileRef = 22322F731068369500641384 /* hash.c */; }; - 22322F891068369500641384 /* hash.h in Headers */ = {isa = PBXBuildFile; fileRef = 22322F741068369500641384 /* hash.h */; }; - 22322F8A1068369500641384 /* hydraul.c in Sources */ = {isa = PBXBuildFile; fileRef = 22322F751068369500641384 /* hydraul.c */; }; - 22322F8B1068369500641384 /* inpfile.c in Sources */ = {isa = PBXBuildFile; fileRef = 22322F761068369500641384 /* inpfile.c */; }; - 22322F8C1068369500641384 /* input1.c in Sources */ = {isa = PBXBuildFile; fileRef = 22322F771068369500641384 /* input1.c */; }; - 22322F8D1068369500641384 /* input2.c in Sources */ = {isa = PBXBuildFile; fileRef = 22322F781068369500641384 /* input2.c */; }; - 22322F8E1068369500641384 /* input3.c in Sources */ = {isa = PBXBuildFile; fileRef = 22322F791068369500641384 /* input3.c */; }; - 22322F8F1068369500641384 /* mempool.c in Sources */ = {isa = PBXBuildFile; fileRef = 22322F7A1068369500641384 /* mempool.c */; }; - 22322F901068369500641384 /* mempool.h in Headers */ = {isa = PBXBuildFile; fileRef = 22322F7B1068369500641384 /* mempool.h */; }; - 22322F911068369500641384 /* output.c in Sources */ = {isa = PBXBuildFile; fileRef = 22322F7C1068369500641384 /* output.c */; }; - 22322F921068369500641384 /* quality.c in Sources */ = {isa = PBXBuildFile; fileRef = 22322F7D1068369500641384 /* quality.c */; }; - 22322F931068369500641384 /* report.c in Sources */ = {isa = PBXBuildFile; fileRef = 22322F7E1068369500641384 /* report.c */; }; - 22322F941068369500641384 /* rules.c in Sources */ = {isa = PBXBuildFile; fileRef = 22322F7F1068369500641384 /* rules.c */; }; - 22322F951068369500641384 /* smatrix.c in Sources */ = {isa = PBXBuildFile; fileRef = 22322F801068369500641384 /* smatrix.c */; }; - 22322F961068369500641384 /* text.h in Headers */ = {isa = PBXBuildFile; fileRef = 22322F811068369500641384 /* text.h */; }; - 22322F981068369500641384 /* types.h in Headers */ = {isa = PBXBuildFile; fileRef = 22322F831068369500641384 /* types.h */; }; - 22322F991068369500641384 /* vars.h in Headers */ = {isa = PBXBuildFile; fileRef = 22322F841068369500641384 /* vars.h */; }; - 22322FAA106836BC00641384 /* epanet2.h in Headers */ = {isa = PBXBuildFile; fileRef = 22322FA9106836B000641384 /* epanet2.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 2255753F17551234009946B1 /* epanet.c in Sources */ = {isa = PBXBuildFile; fileRef = 22322F711068369500641384 /* epanet.c */; }; - 2255754017551234009946B1 /* hash.c in Sources */ = {isa = PBXBuildFile; fileRef = 22322F731068369500641384 /* hash.c */; }; - 2255754117551234009946B1 /* hydraul.c in Sources */ = {isa = PBXBuildFile; fileRef = 22322F751068369500641384 /* hydraul.c */; }; - 2255754217551234009946B1 /* inpfile.c in Sources */ = {isa = PBXBuildFile; fileRef = 22322F761068369500641384 /* inpfile.c */; }; - 2255754317551234009946B1 /* input1.c in Sources */ = {isa = PBXBuildFile; fileRef = 22322F771068369500641384 /* input1.c */; }; - 2255754417551234009946B1 /* input2.c in Sources */ = {isa = PBXBuildFile; fileRef = 22322F781068369500641384 /* input2.c */; }; - 2255754517551234009946B1 /* input3.c in Sources */ = {isa = PBXBuildFile; fileRef = 22322F791068369500641384 /* input3.c */; }; - 2255754617551234009946B1 /* mempool.c in Sources */ = {isa = PBXBuildFile; fileRef = 22322F7A1068369500641384 /* mempool.c */; }; - 2255754717551234009946B1 /* output.c in Sources */ = {isa = PBXBuildFile; fileRef = 22322F7C1068369500641384 /* output.c */; }; - 2255754817551234009946B1 /* quality.c in Sources */ = {isa = PBXBuildFile; fileRef = 22322F7D1068369500641384 /* quality.c */; }; - 2255754917551234009946B1 /* report.c in Sources */ = {isa = PBXBuildFile; fileRef = 22322F7E1068369500641384 /* report.c */; }; - 2255754A17551234009946B1 /* rules.c in Sources */ = {isa = PBXBuildFile; fileRef = 22322F7F1068369500641384 /* rules.c */; }; - 2255754B17551234009946B1 /* smatrix.c in Sources */ = {isa = PBXBuildFile; fileRef = 22322F801068369500641384 /* smatrix.c */; }; - 225762C51C068A3900484EC7 /* Net1.inp in CopyFiles */ = {isa = PBXBuildFile; fileRef = 225762C31C068A2A00484EC7 /* Net1.inp */; }; - 225762C61C068A3B00484EC7 /* Net2.inp in CopyFiles */ = {isa = PBXBuildFile; fileRef = 225762C41C068A2A00484EC7 /* Net2.inp */; }; - 226537E0179EDEEB00258C60 /* epanet2.h in Headers */ = {isa = PBXBuildFile; fileRef = 22322FA9106836B000641384 /* epanet2.h */; }; - 22CD9A5E1B27898E00B65E83 /* main.c in Sources */ = {isa = PBXBuildFile; fileRef = 22CD9A5D1B27898E00B65E83 /* main.c */; }; - 22CD9A611B278BB900B65E83 /* libepanet-static.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2255753B17551217009946B1 /* libepanet-static.a */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 223109E91BA869DA0030AAE8 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 2255753A17551217009946B1; - remoteInfo = "epanet-static"; - }; - 22CD9A5F1B278B0400B65E83 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 2255753A17551217009946B1; - remoteInfo = "epanet-static"; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 223109E11BA865690030AAE8 /* CopyFiles */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 6; - files = ( - 223109E61BA865930030AAE8 /* Net3.inp in CopyFiles */, - 225762C51C068A3900484EC7 /* Net1.inp in CopyFiles */, - 225762C61C068A3B00484EC7 /* Net2.inp in CopyFiles */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 223109EE1BA869DA0030AAE8 /* CopyFiles */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 6; - files = ( - 223109EF1BA869DA0030AAE8 /* Net3.inp in CopyFiles */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 223109E31BA8658A0030AAE8 /* Net3.inp */ = {isa = PBXFileReference; lastKnownFileType = text; name = Net3.inp; path = "../../example-networks/Net3.inp"; sourceTree = ""; }; - 223109F31BA869DA0030AAE8 /* runepanet copy */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "runepanet copy"; sourceTree = BUILT_PRODUCTS_DIR; }; - 22310A091BA869F30030AAE8 /* libepanet-static copy.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libepanet-static copy.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - 22322F66106833BB00641384 /* runepanet */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = runepanet; sourceTree = BUILT_PRODUCTS_DIR; }; - 22322F701068369500641384 /* enumstxt.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = enumstxt.h; path = ../../src/enumstxt.h; sourceTree = SOURCE_ROOT; }; - 22322F711068369500641384 /* epanet.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; lineEnding = 2; name = epanet.c; path = ../../src/epanet.c; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.c; }; - 22322F721068369500641384 /* funcs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = funcs.h; path = ../../src/funcs.h; sourceTree = SOURCE_ROOT; }; - 22322F731068369500641384 /* hash.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = hash.c; path = ../../src/hash.c; sourceTree = SOURCE_ROOT; }; - 22322F741068369500641384 /* hash.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = hash.h; path = ../../src/hash.h; sourceTree = SOURCE_ROOT; }; - 22322F751068369500641384 /* hydraul.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = hydraul.c; path = ../../src/hydraul.c; sourceTree = SOURCE_ROOT; }; - 22322F761068369500641384 /* inpfile.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = inpfile.c; path = ../../src/inpfile.c; sourceTree = SOURCE_ROOT; }; - 22322F771068369500641384 /* input1.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = input1.c; path = ../../src/input1.c; sourceTree = SOURCE_ROOT; }; - 22322F781068369500641384 /* input2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = input2.c; path = ../../src/input2.c; sourceTree = SOURCE_ROOT; }; - 22322F791068369500641384 /* input3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = input3.c; path = ../../src/input3.c; sourceTree = SOURCE_ROOT; }; - 22322F7A1068369500641384 /* mempool.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = mempool.c; path = ../../src/mempool.c; sourceTree = SOURCE_ROOT; }; - 22322F7B1068369500641384 /* mempool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = mempool.h; path = ../../src/mempool.h; sourceTree = SOURCE_ROOT; }; - 22322F7C1068369500641384 /* output.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = output.c; path = ../../src/output.c; sourceTree = SOURCE_ROOT; }; - 22322F7D1068369500641384 /* quality.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; lineEnding = 2; name = quality.c; path = ../../src/quality.c; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.c; }; - 22322F7E1068369500641384 /* report.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = report.c; path = ../../src/report.c; sourceTree = SOURCE_ROOT; }; - 22322F7F1068369500641384 /* rules.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = rules.c; path = ../../src/rules.c; sourceTree = SOURCE_ROOT; }; - 22322F801068369500641384 /* smatrix.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = smatrix.c; path = ../../src/smatrix.c; sourceTree = SOURCE_ROOT; }; - 22322F811068369500641384 /* text.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = text.h; path = ../../src/text.h; sourceTree = SOURCE_ROOT; }; - 22322F831068369500641384 /* types.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = types.h; path = ../../src/types.h; sourceTree = SOURCE_ROOT; }; - 22322F841068369500641384 /* vars.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = vars.h; path = ../../src/vars.h; sourceTree = SOURCE_ROOT; }; - 22322FA9106836B000641384 /* epanet2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 2; name = epanet2.h; path = ../../include/epanet2.h; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; - 2255753B17551217009946B1 /* libepanet-static.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libepanet-static.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - 225762C31C068A2A00484EC7 /* Net1.inp */ = {isa = PBXFileReference; lastKnownFileType = text; name = Net1.inp; path = "../../example-networks/Net1.inp"; sourceTree = ""; }; - 225762C41C068A2A00484EC7 /* Net2.inp */ = {isa = PBXFileReference; lastKnownFileType = text; name = Net2.inp; path = "../../example-networks/Net2.inp"; sourceTree = ""; }; - 22CD9A5D1B27898E00B65E83 /* main.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = main.c; path = ../../run/main.c; sourceTree = ""; }; - 22E107B21C163E5300689CED /* outputapi.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = outputapi.c; path = ../../tools/outputapi/outputapi.c; sourceTree = ""; }; - 22E107B31C163E5300689CED /* outputapi.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = outputapi.h; path = ../../tools/outputapi/outputapi.h; sourceTree = ""; }; - D2AAC0630554660B00DB518D /* libepanet.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libepanet.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 223109EC1BA869DA0030AAE8 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 223109ED1BA869DA0030AAE8 /* libepanet-static.a in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 22310A031BA869F30030AAE8 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 22322F64106833BB00641384 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 22CD9A611B278BB900B65E83 /* libepanet-static.a in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 2255753817551217009946B1 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D289988505E68E00004EDB86 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 08FB7794FE84155DC02AAC07 /* epanet */ = { - isa = PBXGroup; - children = ( - 22E107B11C163E3B00689CED /* Output API */, - 223109E21BA865790030AAE8 /* sample networks */, - 22CD9A5C1B27896200B65E83 /* run epanet */, - 22322FA8106836A000641384 /* Include */, - 08FB7795FE84155DC02AAC07 /* Source */, - 1AB674ADFE9D54B511CA2CBB /* Products */, - ); - name = epanet; - sourceTree = ""; - }; - 08FB7795FE84155DC02AAC07 /* Source */ = { - isa = PBXGroup; - children = ( - 22322F711068369500641384 /* epanet.c */, - 22322F751068369500641384 /* hydraul.c */, - 22322F701068369500641384 /* enumstxt.h */, - 22322F721068369500641384 /* funcs.h */, - 22322F731068369500641384 /* hash.c */, - 22322F741068369500641384 /* hash.h */, - 22322F761068369500641384 /* inpfile.c */, - 22322F771068369500641384 /* input1.c */, - 22322F781068369500641384 /* input2.c */, - 22322F791068369500641384 /* input3.c */, - 22322F7A1068369500641384 /* mempool.c */, - 22322F7B1068369500641384 /* mempool.h */, - 22322F7C1068369500641384 /* output.c */, - 22322F7D1068369500641384 /* quality.c */, - 22322F7E1068369500641384 /* report.c */, - 22322F7F1068369500641384 /* rules.c */, - 22322F801068369500641384 /* smatrix.c */, - 22322F811068369500641384 /* text.h */, - 22322F831068369500641384 /* types.h */, - 22322F841068369500641384 /* vars.h */, - ); - name = Source; - sourceTree = ""; - }; - 1AB674ADFE9D54B511CA2CBB /* Products */ = { - isa = PBXGroup; - children = ( - D2AAC0630554660B00DB518D /* libepanet.dylib */, - 22322F66106833BB00641384 /* runepanet */, - 2255753B17551217009946B1 /* libepanet-static.a */, - 223109F31BA869DA0030AAE8 /* runepanet copy */, - 22310A091BA869F30030AAE8 /* libepanet-static copy.a */, - ); - name = Products; - sourceTree = ""; - }; - 223109E21BA865790030AAE8 /* sample networks */ = { - isa = PBXGroup; - children = ( - 223109E31BA8658A0030AAE8 /* Net3.inp */, - 225762C31C068A2A00484EC7 /* Net1.inp */, - 225762C41C068A2A00484EC7 /* Net2.inp */, - ); - name = "sample networks"; - sourceTree = ""; - }; - 22322FA8106836A000641384 /* Include */ = { - isa = PBXGroup; - children = ( - 22322FA9106836B000641384 /* epanet2.h */, - ); - name = Include; - sourceTree = ""; - }; - 22CD9A5C1B27896200B65E83 /* run epanet */ = { - isa = PBXGroup; - children = ( - 22CD9A5D1B27898E00B65E83 /* main.c */, - ); - name = "run epanet"; - sourceTree = ""; - }; - 22E107B11C163E3B00689CED /* Output API */ = { - isa = PBXGroup; - children = ( - 22E107B31C163E5300689CED /* outputapi.h */, - 22E107B21C163E5300689CED /* outputapi.c */, - ); - name = "Output API"; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXHeadersBuildPhase section */ - 22310A041BA869F30030AAE8 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - 22310A051BA869F30030AAE8 /* epanet2.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 2255753917551217009946B1 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - 226537E0179EDEEB00258C60 /* epanet2.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D2AAC0600554660B00DB518D /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - 22322FAA106836BC00641384 /* epanet2.h in Headers */, - 22322F851068369500641384 /* enumstxt.h in Headers */, - 22322F871068369500641384 /* funcs.h in Headers */, - 22322F891068369500641384 /* hash.h in Headers */, - 22322F901068369500641384 /* mempool.h in Headers */, - 22322F961068369500641384 /* text.h in Headers */, - 22322F981068369500641384 /* types.h in Headers */, - 22322F991068369500641384 /* vars.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXHeadersBuildPhase section */ - -/* Begin PBXNativeTarget section */ - 223109E71BA869DA0030AAE8 /* runepanet float-precision */ = { - isa = PBXNativeTarget; - buildConfigurationList = 223109F01BA869DA0030AAE8 /* Build configuration list for PBXNativeTarget "runepanet float-precision" */; - buildPhases = ( - 223109EA1BA869DA0030AAE8 /* Sources */, - 223109EC1BA869DA0030AAE8 /* Frameworks */, - 223109EE1BA869DA0030AAE8 /* CopyFiles */, - ); - buildRules = ( - ); - dependencies = ( - 223109E81BA869DA0030AAE8 /* PBXTargetDependency */, - ); - name = "runepanet float-precision"; - productName = runepanet; - productReference = 223109F31BA869DA0030AAE8 /* runepanet copy */; - productType = "com.apple.product-type.tool"; - }; - 223109F41BA869F30030AAE8 /* epanet-static float-precision */ = { - isa = PBXNativeTarget; - buildConfigurationList = 22310A061BA869F30030AAE8 /* Build configuration list for PBXNativeTarget "epanet-static float-precision" */; - buildPhases = ( - 223109F51BA869F30030AAE8 /* Sources */, - 22310A031BA869F30030AAE8 /* Frameworks */, - 22310A041BA869F30030AAE8 /* Headers */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "epanet-static float-precision"; - productName = "epanet-static"; - productReference = 22310A091BA869F30030AAE8 /* libepanet-static copy.a */; - productType = "com.apple.product-type.library.static"; - }; - 22322F65106833BB00641384 /* runepanet */ = { - isa = PBXNativeTarget; - buildConfigurationList = 22322F6A106833E600641384 /* Build configuration list for PBXNativeTarget "runepanet" */; - buildPhases = ( - 22322F63106833BB00641384 /* Sources */, - 22322F64106833BB00641384 /* Frameworks */, - 223109E11BA865690030AAE8 /* CopyFiles */, - ); - buildRules = ( - ); - dependencies = ( - 22CD9A601B278B0400B65E83 /* PBXTargetDependency */, - ); - name = runepanet; - productName = runepanet; - productReference = 22322F66106833BB00641384 /* runepanet */; - productType = "com.apple.product-type.tool"; - }; - 2255753A17551217009946B1 /* epanet-static */ = { - isa = PBXNativeTarget; - buildConfigurationList = 2255753E17551217009946B1 /* Build configuration list for PBXNativeTarget "epanet-static" */; - buildPhases = ( - 2255753717551217009946B1 /* Sources */, - 2255753817551217009946B1 /* Frameworks */, - 2255753917551217009946B1 /* Headers */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "epanet-static"; - productName = "epanet-static"; - productReference = 2255753B17551217009946B1 /* libepanet-static.a */; - productType = "com.apple.product-type.library.static"; - }; - D2AAC0620554660B00DB518D /* epanet */ = { - isa = PBXNativeTarget; - buildConfigurationList = 1DEB914A08733D8E0010E9CD /* Build configuration list for PBXNativeTarget "epanet" */; - buildPhases = ( - D2AAC0600554660B00DB518D /* Headers */, - D2AAC0610554660B00DB518D /* Sources */, - D289988505E68E00004EDB86 /* Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = epanet; - productName = epanet; - productReference = D2AAC0630554660B00DB518D /* libepanet.dylib */; - productType = "com.apple.product-type.library.dynamic"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 08FB7793FE84155DC02AAC07 /* Project object */ = { - isa = PBXProject; - attributes = { - LastUpgradeCheck = 0630; - }; - buildConfigurationList = 1DEB914E08733D8E0010E9CD /* Build configuration list for PBXProject "epanet" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; - hasScannedForEncodings = 1; - knownRegions = ( - English, - Japanese, - French, - German, - ); - mainGroup = 08FB7794FE84155DC02AAC07 /* epanet */; - projectDirPath = ""; - projectRoot = ../../..; - targets = ( - D2AAC0620554660B00DB518D /* epanet */, - 22322F65106833BB00641384 /* runepanet */, - 2255753A17551217009946B1 /* epanet-static */, - 223109E71BA869DA0030AAE8 /* runepanet float-precision */, - 223109F41BA869F30030AAE8 /* epanet-static float-precision */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXSourcesBuildPhase section */ - 223109EA1BA869DA0030AAE8 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 223109EB1BA869DA0030AAE8 /* main.c in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 223109F51BA869F30030AAE8 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 223109F61BA869F30030AAE8 /* epanet.c in Sources */, - 223109F71BA869F30030AAE8 /* hash.c in Sources */, - 223109F81BA869F30030AAE8 /* hydraul.c in Sources */, - 223109F91BA869F30030AAE8 /* inpfile.c in Sources */, - 223109FA1BA869F30030AAE8 /* input1.c in Sources */, - 223109FB1BA869F30030AAE8 /* input2.c in Sources */, - 223109FC1BA869F30030AAE8 /* input3.c in Sources */, - 223109FD1BA869F30030AAE8 /* mempool.c in Sources */, - 223109FE1BA869F30030AAE8 /* output.c in Sources */, - 223109FF1BA869F30030AAE8 /* quality.c in Sources */, - 22310A001BA869F30030AAE8 /* report.c in Sources */, - 22310A011BA869F30030AAE8 /* rules.c in Sources */, - 22310A021BA869F30030AAE8 /* smatrix.c in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 22322F63106833BB00641384 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 22CD9A5E1B27898E00B65E83 /* main.c in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 2255753717551217009946B1 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 2255753F17551234009946B1 /* epanet.c in Sources */, - 2255754017551234009946B1 /* hash.c in Sources */, - 2255754117551234009946B1 /* hydraul.c in Sources */, - 2255754217551234009946B1 /* inpfile.c in Sources */, - 2255754317551234009946B1 /* input1.c in Sources */, - 2255754417551234009946B1 /* input2.c in Sources */, - 2255754517551234009946B1 /* input3.c in Sources */, - 2255754617551234009946B1 /* mempool.c in Sources */, - 2255754717551234009946B1 /* output.c in Sources */, - 2255754817551234009946B1 /* quality.c in Sources */, - 2255754917551234009946B1 /* report.c in Sources */, - 2255754A17551234009946B1 /* rules.c in Sources */, - 2255754B17551234009946B1 /* smatrix.c in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D2AAC0610554660B00DB518D /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 22322F861068369500641384 /* epanet.c in Sources */, - 22322F881068369500641384 /* hash.c in Sources */, - 22322F8A1068369500641384 /* hydraul.c in Sources */, - 22322F8B1068369500641384 /* inpfile.c in Sources */, - 22322F8C1068369500641384 /* input1.c in Sources */, - 22322F8D1068369500641384 /* input2.c in Sources */, - 22322F8E1068369500641384 /* input3.c in Sources */, - 22322F8F1068369500641384 /* mempool.c in Sources */, - 22322F911068369500641384 /* output.c in Sources */, - 22322F921068369500641384 /* quality.c in Sources */, - 22322F931068369500641384 /* report.c in Sources */, - 22322F941068369500641384 /* rules.c in Sources */, - 22322F951068369500641384 /* smatrix.c in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 223109E81BA869DA0030AAE8 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 2255753A17551217009946B1 /* epanet-static */; - targetProxy = 223109E91BA869DA0030AAE8 /* PBXContainerItemProxy */; - }; - 22CD9A601B278B0400B65E83 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 2255753A17551217009946B1 /* epanet-static */; - targetProxy = 22CD9A5F1B278B0400B65E83 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin XCBuildConfiguration section */ - 1DEB914B08733D8E0010E9CD /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - EXECUTABLE_PREFIX = lib; - GCC_MODEL_TUNING = G5; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = "EN_API_FLOAT_TYPE=double"; - PRODUCT_NAME = epanet; - }; - name = Debug; - }; - 1DEB914C08733D8E0010E9CD /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - EXECUTABLE_PREFIX = lib; - GCC_MODEL_TUNING = G5; - GCC_PREPROCESSOR_DEFINITIONS = "EN_API_FLOAT_TYPE=double"; - PRODUCT_NAME = epanet; - }; - name = Release; - }; - 1DEB914F08733D8E0010E9CD /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_CXX_LIBRARY = "libc++"; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = c89; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_VERSION = com.apple.compilers.llvm.clang.1_0; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - HEADER_SEARCH_PATHS = ""; - MACOSX_DEPLOYMENT_TARGET = 10.10; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = ""; - }; - name = Debug; - }; - 1DEB915008733D8E0010E9CD /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_CXX_LIBRARY = "libc++"; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = c89; - GCC_NO_COMMON_BLOCKS = YES; - GCC_VERSION = com.apple.compilers.llvm.clang.1_0; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - HEADER_SEARCH_PATHS = ""; - MACOSX_DEPLOYMENT_TARGET = 10.10; - SDKROOT = ""; - }; - name = Release; - }; - 223109F11BA869DA0030AAE8 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - GCC_MODEL_TUNING = G5; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ""; - INFOPLIST_PREPROCESSOR_DEFINITIONS = ""; - PRODUCT_NAME = "runepanet copy"; - }; - name = Debug; - }; - 223109F21BA869DA0030AAE8 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - GCC_MODEL_TUNING = G5; - GCC_PREPROCESSOR_DEFINITIONS = ""; - INFOPLIST_PREPROCESSOR_DEFINITIONS = ""; - PRODUCT_NAME = "runepanet copy"; - ZERO_LINK = NO; - }; - name = Release; - }; - 22310A071BA869F30030AAE8 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_WARN_EMPTY_BODY = YES; - COMBINE_HIDPI_IMAGES = YES; - EXECUTABLE_PREFIX = lib; - GCC_ENABLE_OBJC_EXCEPTIONS = YES; - GCC_SYMBOLS_PRIVATE_EXTERN = NO; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - PRODUCT_NAME = "epanet-static copy"; - }; - name = Debug; - }; - 22310A081BA869F30030AAE8 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_WARN_EMPTY_BODY = YES; - COMBINE_HIDPI_IMAGES = YES; - EXECUTABLE_PREFIX = lib; - GCC_ENABLE_OBJC_EXCEPTIONS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - PRODUCT_NAME = "epanet-static copy"; - }; - name = Release; - }; - 22322F68106833BC00641384 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - GCC_MODEL_TUNING = G5; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ""; - INFOPLIST_PREPROCESSOR_DEFINITIONS = ""; - PRODUCT_NAME = runepanet; - SDKROOT = macosx; - }; - name = Debug; - }; - 22322F69106833BC00641384 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - GCC_MODEL_TUNING = G5; - GCC_PREPROCESSOR_DEFINITIONS = ""; - INFOPLIST_PREPROCESSOR_DEFINITIONS = ""; - PRODUCT_NAME = runepanet; - SDKROOT = macosx; - ZERO_LINK = NO; - }; - name = Release; - }; - 2255753C17551217009946B1 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_WARN_EMPTY_BODY = YES; - COMBINE_HIDPI_IMAGES = YES; - EXECUTABLE_PREFIX = lib; - GCC_ENABLE_OBJC_EXCEPTIONS = YES; - GCC_PREPROCESSOR_DEFINITIONS = "EN_API_FLOAT_TYPE=double"; - GCC_SYMBOLS_PRIVATE_EXTERN = NO; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = macosx; - }; - name = Debug; - }; - 2255753D17551217009946B1 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_WARN_EMPTY_BODY = YES; - COMBINE_HIDPI_IMAGES = YES; - EXECUTABLE_PREFIX = lib; - GCC_ENABLE_OBJC_EXCEPTIONS = YES; - GCC_PREPROCESSOR_DEFINITIONS = "EN_API_FLOAT_TYPE=double"; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = macosx; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 1DEB914A08733D8E0010E9CD /* Build configuration list for PBXNativeTarget "epanet" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 1DEB914B08733D8E0010E9CD /* Debug */, - 1DEB914C08733D8E0010E9CD /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 1DEB914E08733D8E0010E9CD /* Build configuration list for PBXProject "epanet" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 1DEB914F08733D8E0010E9CD /* Debug */, - 1DEB915008733D8E0010E9CD /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 223109F01BA869DA0030AAE8 /* Build configuration list for PBXNativeTarget "runepanet float-precision" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 223109F11BA869DA0030AAE8 /* Debug */, - 223109F21BA869DA0030AAE8 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 22310A061BA869F30030AAE8 /* Build configuration list for PBXNativeTarget "epanet-static float-precision" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 22310A071BA869F30030AAE8 /* Debug */, - 22310A081BA869F30030AAE8 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 22322F6A106833E600641384 /* Build configuration list for PBXNativeTarget "runepanet" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 22322F68106833BC00641384 /* Debug */, - 22322F69106833BC00641384 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 2255753E17551217009946B1 /* Build configuration list for PBXNativeTarget "epanet-static" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 2255753C17551217009946B1 /* Debug */, - 2255753D17551217009946B1 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 08FB7793FE84155DC02AAC07 /* Project object */; -} diff --git a/include/epanet2.bas b/include/epanet2.bas index 3b1b8d5..388a650 100644 --- a/include/epanet2.bas +++ b/include/epanet2.bas @@ -53,7 +53,9 @@ Global Const EN_ENERGY = 13 Global Const EN_LINKQUAL = 14 'ES Global Const EN_LINKPATTERN = 15 Global Const EN_EFFICIENCY = 16 -Global Const EN_PRICEPATTERN = 17 +Global Const EN_HEADCURVE = 17 +Global Const EN_EFFICIENCYCURVE = 18 +Global Const EN_PRICEPATTERN = 19 Global Const EN_DURATION = 0 ' Time parameters Global Const EN_HYDSTEP = 1 @@ -106,6 +108,10 @@ Global Const EN_MASS = 1 Global Const EN_SETPOINT = 2 Global Const EN_FLOWPACED = 3 +Global Const EN_HW = 0 ' Head loss formula +Global Const EN_DW = 1 +Global Const EN_CM = 2 + Global Const EN_CFS = 0 ' Flow units types Global Const EN_GPM = 1 Global Const EN_MGD = 2 @@ -247,3 +253,11 @@ Global Const EN_CUSTOM = 2 ' user-defined custom curve Declare FUnction ENgetfalseaction Lib "epanet2.dll" (ByVal indexRule As Long, ByVal indexAction As Long, indexLink As Long, status As Long, setting As Single) As Long Declare Function ENsetfalseaction Lib "epanet2.dll" (ByVal indexRule As Long, ByVal indexAction As Long, ByVal indexLink As Long, ByVal status As Long, ByVal setting As Single) As Long Declare Function ENgetruleID Lib "epanet2.dll" (ByVal indexRule As Long, ByVal id As String) As Long + + Declare Function ENinit Lib "epanet2.dll" (ByVal rptFile As String, ByVal binOutFile As String, ByVal UnitsType As Long, ByVal HeadlossFormula As Long) As Long + Declare Function ENsetheadcurveindex Lib "epanet2.dll" (ByVal pumpIndex As Long, ByVal curveIndex As Long) As Long + Declare Function ENsetlinktype Lib "epanet2.dll" (ByVal index As Long, ByVal code As Long) As Long + Declare Function ENaddnode Lib "epanet2.dll" (ByVal id As String, ByVal nodeType As Long) As Long + Declare Function ENaddlink Lib "epanet2.dll" (ByVal id As String, ByVal linkType As Long, ByVal fromNode As String, ByVal toNode As String) As Long + Declare Function ENdeletelink Lib "epanet2.dll" (ByVal nodeIndex As Long) As Long + Declare Function ENdeletenode Lib "epanet2.dll" (ByVal linkIndex As Long) As Long diff --git a/include/epanet2.h b/include/epanet2.h old mode 100755 new mode 100644 index b092866..c9fe158 --- a/include/epanet2.h +++ b/include/epanet2.h @@ -1,1043 +1,1225 @@ -/** @file epanet2.h - @see http://github.com/openwateranalytics/epanet - - */ - -/* - ******************************************************************* - - EPANET2.H - Prototypes for EPANET Functions Exported to DLL Toolkit - - VERSION: 2.00 - DATE: 5/8/00 - 10/25/00 - 3/1/01 - 8/15/07 (2.00.11) - 2/14/08 (2.00.12) - AUTHORS: L. Rossman - US EPA - NRMRL - OpenWaterAnalytics members: see git stats for contributors - - ******************************************************************* - */ - - -#ifndef EPANET2_H -#define EPANET2_H - -// the toolkit can be compiled with support for double-precision as well. -// just make sure that you use the correct #define in your client code. -#ifndef EN_API_FLOAT_TYPE - #define EN_API_FLOAT_TYPE float -#endif - -// --- define WINDOWS -#undef WINDOWS -#ifdef _WIN32 - #define WINDOWS -#endif -#ifdef __WIN32__ - #define WINDOWS -#endif - -// --- define DLLEXPORT -#ifndef DLLEXPORT - #ifdef WINDOWS - #ifdef __cplusplus - #define DLLEXPORT extern "C" __declspec(dllexport) - #else - #define DLLEXPORT __declspec(dllexport) __stdcall - #endif // __cplusplus - #elif defined(CYGWIN) - #define DLLEXPORT __stdcall - #elif defined(__APPLE__) - #ifdef __cplusplus - #define DLLEXPORT - #else - #define DLLEXPORT - #endif - #else - #define DLLEXPORT - #endif -#endif - - -// --- Define the EPANET toolkit constants - -/// Node property codes -typedef enum { - EN_ELEVATION = 0, /**< Node Elevation */ - EN_BASEDEMAND = 1, /**< Node Base Demand, from last demand category */ - EN_PATTERN = 2, /**< Node Demand Pattern */ - EN_EMITTER = 3, /**< Node Emitter Coefficient */ - EN_INITQUAL = 4, /**< Node initial quality */ - EN_SOURCEQUAL = 5, /**< Node source quality */ - EN_SOURCEPAT = 6, /**< Node source pattern index */ - EN_SOURCETYPE = 7, /**< Node source type */ - EN_TANKLEVEL = 8, /**< Tank Level */ - EN_DEMAND = 9, /**< Node current simulated demand */ - EN_HEAD = 10, /**< Node Head value */ - EN_PRESSURE = 11, /**< Node pressure value */ - EN_QUALITY = 12, /**< Node quality value */ - EN_SOURCEMASS = 13, /**< Node source mass value */ - EN_INITVOLUME = 14, /**< Tank or Reservoir initial volume */ - EN_MIXMODEL = 15, /**< Tank mixing model */ - EN_MIXZONEVOL = 16, /**< Tank mixing zone volume */ - EN_TANKDIAM = 17, /**< Tank diameter */ - EN_MINVOLUME = 18, /**< Tank minimum volume */ - EN_VOLCURVE = 19, /**< Tank volume curve index */ - EN_MINLEVEL = 20, /**< Tank minimum level */ - EN_MAXLEVEL = 21, /**< Tank maximum level */ - EN_MIXFRACTION = 22, /**< Tank mixing fraction */ - EN_TANK_KBULK = 23, /**< Tank bulk decay coefficient */ - EN_TANKVOLUME = 24, /**< Tank current volume */ - EN_MAXVOLUME = 25 /**< Tank maximum volume */ -} EN_NodeProperty; - -/// Link property codes -typedef enum { - EN_DIAMETER = 0, - EN_LENGTH = 1, - EN_ROUGHNESS = 2, - EN_MINORLOSS = 3, - EN_INITSTATUS = 4, - EN_INITSETTING = 5, - EN_KBULK = 6, - EN_KWALL = 7, - EN_FLOW = 8, - EN_VELOCITY = 9, - EN_HEADLOSS = 10, - EN_STATUS = 11, - EN_SETTING = 12, - EN_ENERGY = 13, - EN_LINKQUAL = 14, - EN_LINKPATTERN = 15, - EN_EFFICIENCY = 16, - EN_PRICEPATTERN = 17 -} EN_LinkProperty; - -/// Time parameter codes -typedef enum { - EN_DURATION = 0, - EN_HYDSTEP = 1, - EN_QUALSTEP = 2, - EN_PATTERNSTEP = 3, - EN_PATTERNSTART = 4, - EN_REPORTSTEP = 5, - EN_REPORTSTART = 6, - EN_RULESTEP = 7, - EN_STATISTIC = 8, - EN_PERIODS = 9, - EN_STARTTIME = 10, - EN_HTIME = 11, - EN_QTIME = 12, - EN_HALTFLAG = 13, - EN_NEXTEVENT = 14 -} EN_TimeProperty; - - -typedef enum { - EN_ITERATIONS = 0, - EN_RELATIVEERROR = 1 -} EN_AnalysisStatistic; - -typedef enum { - EN_NODECOUNT = 0, /**< Number of Nodes (Juntions + Tanks + Reservoirs) */ - EN_TANKCOUNT = 1, /**< Number of Tanks and Reservoirs */ - EN_LINKCOUNT = 2, /**< Number of Links (Pipes + Pumps + Valves) */ - EN_PATCOUNT = 3, /**< Number of Time Patterns */ - EN_CURVECOUNT = 4, /**< Number of Curves */ - EN_CONTROLCOUNT = 5, /**< Number of Control Statements */ - EN_RULECOUNT = 6 /**< Number of Rule-based Control Statements */ -} EN_CountType; - -typedef enum { - EN_JUNCTION = 0, - EN_RESERVOIR = 1, - EN_TANK = 2 -} EN_NodeType; - - -typedef enum { - EN_CVPIPE = 0, /* Link types. */ - EN_PIPE = 1, /* See LinkType in TYPES.H */ - EN_PUMP = 2, - EN_PRV = 3, - EN_PSV = 4, - EN_PBV = 5, - EN_FCV = 6, - EN_TCV = 7, - EN_GPV = 8 -} EN_LinkType; - - -typedef enum { - EN_NONE = 0, /* Quality analysis types. */ - EN_CHEM = 1, /* See QualType in TYPES.H */ - EN_AGE = 2, - EN_TRACE = 3 -} EN_QualityType; - -typedef enum { - EN_CONCEN = 0, /* Source quality types. */ - EN_MASS = 1, /* See SourceType in TYPES.H. */ - EN_SETPOINT = 2, - EN_FLOWPACED = 3 -} EN_SourceType; - -typedef enum { - EN_CFS = 0, /* Flow units types. */ - EN_GPM = 1, /* See FlowUnitsType */ - EN_MGD = 2, /* in TYPES.H. */ - EN_IMGD = 3, - EN_AFD = 4, - EN_LPS = 5, - EN_LPM = 6, - EN_MLD = 7, - EN_CMH = 8, - EN_CMD = 9 -} EN_FlowUnits; - - -/// Simulation Option codes -typedef enum { - EN_TRIALS = 0, - EN_ACCURACY = 1, - EN_TOLERANCE = 2, - EN_EMITEXPON = 3, - EN_DEMANDMULT = 4 -} EN_Option; - -typedef enum { - EN_LOWLEVEL = 0, /* Control types. */ - EN_HILEVEL = 1, /* See ControlType */ - EN_TIMER = 2, /* in TYPES.H. */ - EN_TIMEOFDAY = 3 -} EN_ControlType; - - - -typedef enum { - EN_AVERAGE = 1, /* Time statistic types. */ - EN_MINIMUM = 2, /* See TstatType in TYPES.H */ - EN_MAXIMUM = 3, - EN_RANGE = 4 -} EN_StatisticType; - - - -typedef enum { - EN_MIX1 = 0, /* Tank mixing models */ - EN_MIX2 = 1, - EN_FIFO = 2, - EN_LIFO = 3 -} EN_MixingModel; - - - -typedef enum { - EN_NOSAVE = 0, - EN_SAVE = 1, - EN_INITFLOW = 10, - EN_SAVE_AND_INIT = 11 -} EN_SaveOption; - - - -typedef enum { - EN_CONST_HP = 0, /* constant horsepower */ - EN_POWER_FUNC = 1, /* power function */ - EN_CUSTOM = 2 /* user-defined custom curve */ -} EN_CurveType; - - - -// --- Declare the EPANET toolkit functions -#if defined(__cplusplus) -extern "C" { -#endif - /** - @brief runs a complete EPANET simulation - @param inpFile pointer to name of input file (must exist) - @param rptFile pointer to name of report file (to be created) - @param binOutFile pointer to name of binary output file (to be created) - @param callback a callback function that takes a character string (char *) as its only parameter. - @return error code - - The callback function should reside in and be used by the calling - code to display the progress messages that EPANET generates - as it carries out its computations. If this feature is not - needed then the argument should be NULL. - */ - int DLLEXPORT ENepanet(char *inpFile, char *rptFile, char *binOutFile, void (*callback) (char *)); - - /** - @brief Opens EPANET input file & reads in network data - @param inpFile pointer to name of input file (must exist) - @param rptFile pointer to name of report file (to be created) - @param binOutFile pointer to name of binary output file (to be created) - @return error code - */ - int DLLEXPORT ENopen(char *inpFile, char *rptFile, char *binOutFile); - - /** - @brief Saves current data to "INP" formatted text file. - @param filename The file path to create - @return Error code - */ - int DLLEXPORT ENsaveinpfile(char *filename); - - /** - @brief Frees all memory and files used by EPANET - @return Error code - */ - int DLLEXPORT ENclose(); - - /** - @brief Solves the network hydraulics for all time periods - @return Error code - */ - int DLLEXPORT ENsolveH(); - - /** - @brief Saves hydraulic results to binary file - @return Error code - - Must be called before ENreport() if no WQ simulation has been made. - Should not be called if ENsolveQ() will be used. - */ - int DLLEXPORT ENsaveH(); - - /** - @brief Sets up data structures for hydraulic analysis - @return Error code - */ - int DLLEXPORT ENopenH(); - - /** - @brief Initializes hydraulic analysis - @param initFlag 2-digit flag where 1st (left) digit indicates if link flows should be re-initialized (1) or not (0), and 2nd digit indicates if hydraulic results should be saved to file (1) or not (0). - @return Error code - */ - int DLLEXPORT ENinitH(int initFlag); - - /** - @brief Run a hydraulic solution period - @param[out] currentTime The current simulation time in seconds - @return Error or warning code - @see ENsolveH - - This function is used in a loop with ENnextH() to run - an extended period hydraulic simulation. - See ENsolveH() for an example. - */ - int DLLEXPORT ENrunH(long *currentTime); - - /** - @brief Determine time (in seconds) until next hydraulic event - @param[out] tStep Time (seconds) until next hydraulic event. 0 marks end of simulation period. - @return Error code - - This function is used in a loop with ENrunH() to run an extended period hydraulic simulation. - See ENsolveH() for an example. - */ - int DLLEXPORT ENnextH(long *tStep); - - - /** - @brief Frees data allocated by hydraulics solver - @return Error code - */ - int DLLEXPORT ENcloseH(); - - /** - @brief Copies binary hydraulics file to disk - @param filename Name of file to be created - @return Error code - */ - int DLLEXPORT ENsavehydfile(char *filename); - - /** - @brief Opens previously saved binary hydraulics file - @param filename Name of file to be used - @return Error code - */ - int DLLEXPORT ENusehydfile(char *filename); - - /** - @brief Solves for network water quality in all time periods - @return Error code - */ - int DLLEXPORT ENsolveQ(); - - /** - @brief Sets up data structures for WQ analysis - @return Error code - */ - int DLLEXPORT ENopenQ(); - - /** - @brief Initializes water quality analysis - @param saveFlag EN_SAVE (1) if results saved to file, EN_NOSAVE (0) if not - @return Error code - */ - int DLLEXPORT ENinitQ(int saveFlag); - - /** - @brief Retrieves hydraulic & WQ results at time t. - @param[out] currentTime Current simulation time, in seconds. - @return Error code - - This function is used in a loop with ENnextQ() to run - an extended period WQ simulation. See ENsolveQ() for - an example. - */ - int DLLEXPORT ENrunQ(long *currentTime); - - /** - @brief Advances WQ simulation to next hydraulic event. - @param[out] tStep Time in seconds until next hydraulic event. 0 marks end of simulation period. - @return Error code - - This function is used in a loop with ENrunQ() to run - an extended period WQ simulation. See ENsolveQ() for - an example. - */ - int DLLEXPORT ENnextQ(long *tStep); - - /** - @brief Advances WQ simulation by a single WQ time step - @param[out] timeLeft Time left in overall simulation (in seconds) - @return Error code - - This function is used in a loop with ENrunQ() to run - an extended period WQ simulation. - */ - int DLLEXPORT ENstepQ(long *timeLeft); - - /** - @brief Frees data allocated by water quality solver. - @return Error code. - */ - int DLLEXPORT ENcloseQ(); - - /** - @brief Writes line of text to the report file. - @param line Text string to write - @return Error code. - */ - int DLLEXPORT ENwriteline(char *line); - - /** - @brief Writes simulation report to the report file - @return Error code - */ - int DLLEXPORT ENreport(); - - /** - @brief Resets report options to default values - @return Error code - */ - int DLLEXPORT ENresetreport(); - - /** - @brief Processes a reporting format command - @return Error code - */ - int DLLEXPORT ENsetreport(char *reportFormat); - - /** - @brief Retrieves parameters that define a simple control - @param controlIndex Control index (position of control statement in the input file, starting from 1) - @param[out] controlType Control type code (see EPANET2.H) - @param[out] linkIndex Index of controlled link - @param[out] setting Control setting on link - @param[out] nodeIndex Index of controlling node (0 for TIMER or TIMEOFDAY control) - @param[out] level Control level (tank level, junction pressure, or time (seconds)) - @return Error code - */ - int DLLEXPORT ENgetcontrol(int controlIndex, int *controlType, int *linkIndex, EN_API_FLOAT_TYPE *setting, int *nodeIndex, EN_API_FLOAT_TYPE *level); - - /** - @brief Retrieves the number of components of a given type in the network. - @param code Component code (see EPANET2.H) - @param[out] count Number of components in network - @return Error code - */ - int DLLEXPORT ENgetcount(int code, int *count); - - /** - @brief Gets value for an analysis option - @param code Option code (see EPANET2.H) - @param[out] value Option value - @return Error code - */ - int DLLEXPORT ENgetoption(int code, EN_API_FLOAT_TYPE *value); - - /** - @brief Retrieves value of specific time parameter. - @param code Time parameter code - @param[out] value Value of time parameter. - @return Error code - */ - int DLLEXPORT ENgettimeparam(int code, long *value); - - /** - @brief Retrieves the flow units code - @param[out] code Code of flow units in use - @return Error code - */ - int DLLEXPORT ENgetflowunits(int *code); - - /** - @brief Retrieves the index of the time pattern with specified ID - @param id String ID of the time pattern - @param[out] index Index of the specified time pattern - @return Error code - @see ENgetpatternid - */ - int DLLEXPORT ENgetpatternindex(char *id, int *index); - - /** - @brief Retrieves ID of a time pattern with specific index. - @param index The index of a time pattern. - @param[out] id The string ID of the time pattern. - @return Error code - @see ENgetpatternindex - */ - int DLLEXPORT ENgetpatternid(int index, char *id); - - /** - @brief Retrieves the number of multipliers in a time pattern. - @param index The index of a time pattern. - @param[out] len The length of the time pattern. - @return Error code - */ - int DLLEXPORT ENgetpatternlen(int index, int *len); - - /** - @brief Retrive a multiplier from a pattern for a specific time period. - @param index The index of a time pattern - @param period The pattern time period. First time period is 1. - @param[out] value Pattern multiplier - @return Error code - */ - int DLLEXPORT ENgetpatternvalue(int index, int period, EN_API_FLOAT_TYPE *value); - - /** - @brief Retrieve the average multiplier value in a time pattern - @param index The index of a time pattern - @param[out] value The average of all of this time pattern's values - @return Error code - */ - int DLLEXPORT ENgetaveragepatternvalue(int index, EN_API_FLOAT_TYPE *value); - - /** - @brief Retrieve the type of quality analytis to be run. - @param[out] qualcode The quality analysis code number. - @param[out] tracenode The index of node being traced, if qualcode == trace - @return Error code - @see ENsetqualtype - */ - int DLLEXPORT ENgetqualtype(int *qualcode, int *tracenode); - - /** - @brief Get the text of an error code. - @param errcode The error code - @param[out] errmsg The error string represented by the code - @param maxLen The maximum number of characters to copy into the char pointer errmsg - @return Error code - */ - int DLLEXPORT ENgeterror(int errcode, char *errmsg, int maxLen); - - /** - @brief Get hydraulic simulation statistic - @param code The type of statistic to get - @param[out] value The value of the statistic - @return Error code - */ - int DLLEXPORT ENgetstatistic(int code, EN_API_FLOAT_TYPE* value); - - /** - @brief Get index of node with specified ID - @param id The string ID of the node - @param[out] index The node's index (first node is index 1) - @return Error code - @see ENgetnodeid - */ - int DLLEXPORT ENgetnodeindex(char *id, int *index); - - /** - @brief Get the string ID of the specified node. - @param index The index of the node (first node is index 1) - @param[out] id The string ID of the specified node. Up to MAXID characters will be copied, so id must be pre-allocated by the calling code to hold at least that many characters. - @return Error code - @see ENgetnodeindex - */ - int DLLEXPORT ENgetnodeid(int index, char *id); - - /** - @brief Get the type of node with specified index. - @param index The index of a node (first node is index 1) - @param[out] code The type code for the node. - @return Error code - */ - int DLLEXPORT ENgetnodetype(int index, int *code); - - /** - @brief Get a property value for specified node - @param index The index of a node (first node is index 1). - @param code The property type code - @param[out] value The value of the node's property. - @return Error code - @see EN_NodeProperty - */ - int DLLEXPORT ENgetnodevalue(int index, int code, EN_API_FLOAT_TYPE *value); - - /** - @brief Get coordinates (x,y) for a node. - @param index The index of a node (first node is index 1). - @param[out] x X-value of node's coordinate - @param[out] y Y-value of node's coordinate - @return Error code - @see ENsetcoord - */ - int DLLEXPORT ENgetcoord(int index, EN_API_FLOAT_TYPE *x, EN_API_FLOAT_TYPE *y); - - /** - @brief Set coordinates (x,y) for a node. - @param index The index of a node (first node is index 1) - @param x X-value of node's coordinate - @param y Y-value of node's coordinate - @return Error code - @see ENgetcoord - */ - int DLLEXPORT ENsetcoord(int index, EN_API_FLOAT_TYPE x, EN_API_FLOAT_TYPE y); - - /** - @brief Get the number of demand categories for a node. - @param nodeIndex The index of a node (first node is index 1) - @param[out] numDemands The number of demand categories - @return Error code - */ - int DLLEXPORT ENgetnumdemands(int nodeIndex, int *numDemands); - - /** - @brief Get a node's base demand for a specified category. - @param nodeIndex The index of a node (first node is index 1) - @param demandIndex The index of the demand category (starting at 1) - @return Error code - */ - int DLLEXPORT ENgetbasedemand(int nodeIndex, int demandIndex, EN_API_FLOAT_TYPE *baseDemand); - - /** - @brief Get the index of the demand pattern assigned to a node for a category index. - @param nodeIndex The index of a node (first node is index 1). - @param demandIndex The index of a category (first category is index 1). - @param[out] pattIndex The index of the pattern for this node and category. - @return Error code - */ - int DLLEXPORT ENgetdemandpattern(int nodeIndex, int demandIndex, int *pattIndex); - - /** - @brief Get the index of a Link with specified ID. - @param id The string ID of a link. - @param[out] index The index of the named link (first link is index 1) - @return Error code - @see ENgetlinkid - */ - int DLLEXPORT ENgetlinkindex(char *id, int *index); - - /** - @brief Get the string ID of a link with specified index - @param index The index of a link (first link is index 1) - @param[out] id The ID of the link. Up to MAXID characters will be copied, so id must be pre-allocated by the calling code to hold at least that many characters. - @return Error code - @see ENgetlinkindex - */ - int DLLEXPORT ENgetlinkid(int index, char *id); - - /** - @brief Get the link type code for a specified link - @param index The index of a link (first link is index 1) - @param[out] code The type code of the link. - @return Error code - @see EN_LinkType - */ - int DLLEXPORT ENgetlinktype(int index, int *code); - - /** - @brief Get the indexes of a link's start- and end-nodes. - @param index The index of a link (first link is index 1) - @param[out] node1 The index of the link's start node (first node is index 1). - @param[out] node2 The index of the link's end node (first node is index 1). - @return Error code - @see ENgetnodeid, ENgetlinkid - */ - int DLLEXPORT ENgetlinknodes(int index, int *node1, int *node2); - - /** - @brief Get a property value for specified link. - @param index The index of a node (first node is index 1). - @param code The parameter desired. - @param[out] value The value of the link's specified property. - @return Error code - @see ENgetnodevalue, EN_LinkProperty - */ - int DLLEXPORT ENgetlinkvalue(int index, int code, EN_API_FLOAT_TYPE *value); - - /** - @brief Get a curve's properties. - @param curveIndex The index of a curve (first curve is index 1). - @param[out] id The curve's string ID. Client code must preallocate at least MAXID characters. - @param[out] nValues The number of values in the curve's (x,y) list. - @param[out] xValues The curve's x-values. Must be freed by client. - @param[out] yValues The curve's y-values. Must be freed by client. - @return Error code. - */ - int DLLEXPORT ENgetcurve(int curveIndex, char* id, int *nValues, EN_API_FLOAT_TYPE **xValues, EN_API_FLOAT_TYPE **yValues); - - /** - @brief Retrieves the curve index for a specified pump index. - @param pumpIndex The index of a pump - @param[out] curveIndex The index of the curve used by the pump. - @return Error code. - */ - int DLLEXPORT ENgetheadcurveindex(int pumpIndex, int *curveIndex); - - /** - @brief Get the type of pump - @param linkIndex The index of the pump element - @param[out] outType The integer-typed pump curve type signifier (output parameter) - @return Error code - @see EN_CurveType - */ - int DLLEXPORT ENgetpumptype(int linkIndex, int *outType); - - /** - @brief Get the version number. This number is to be interpreted with implied decimals, i.e., "20100" == "2(.)01(.)00" - @param[out] version The version of EPANET - @return Error code. - */ - int DLLEXPORT ENgetversion(int *version); - - /** - @brief Specify parameters to define a simple control - @param cindex The index of the control to edit. First control is index 1. - @param ctype The type code to set for this control. - @param lindex The index of a link to control. - @param setting The control setting applied to the link. - @param nindex The index of a node used to control the link, or 0 for TIMER / TIMEOFDAY control. - @param level control point (tank level, junction pressure, or time in seconds). - @return Error code. - */ - int DLLEXPORT ENsetcontrol(int cindex, int ctype, int lindex, EN_API_FLOAT_TYPE setting, int nindex, EN_API_FLOAT_TYPE level); - - /** - @brief Set a property value for a node. - @param index The index of a node. First node is index 1. - @param code The code for the proprty to set. - @param v The value to set for this node and property. - @return Error code. - @see EN_NodeProperty - */ - int DLLEXPORT ENsetnodevalue(int index, int code, EN_API_FLOAT_TYPE v); - - /** - @brief Set a proprty value for a link. - @param index The index of a link. First link is index 1. - @param code The code for the property to set. - @param v The value to set for this link and property. - @return Error code. - @see EN_LinkProperty - */ - int DLLEXPORT ENsetlinkvalue(int index, int code, EN_API_FLOAT_TYPE v); - - /** - @brief Add a new time pattern. - @param id The string ID of the pattern to add. - @return Error code. - @see ENgetpatternindex - */ - int DLLEXPORT ENaddpattern(char *id); - - /** - @brief Set multipliers for a specific pattern - @param index The index of a pattern. First pattern is index 1. - @param f An array of multipliers - @param len The length of array f. - @return Error code. - @see ENgetpatternindex - */ - int DLLEXPORT ENsetpattern(int index, EN_API_FLOAT_TYPE *f, int len); - - /** - @brief Set the multiplier for a specific pattern at a specific period. - @param index The index of a pattern. First pattern is index 1. - @param period The period of the pattern to set. - @param value The value of the multiplier to set. - @return Error code. - */ - int DLLEXPORT ENsetpatternvalue(int index, int period, EN_API_FLOAT_TYPE value); - - /** - @brief Set the value for a time parameter. - @param code The code for the parameter to set. - @param value The desired value of the parameter. - @return Error code. - @see EN_TimeProperty - */ - int DLLEXPORT ENsettimeparam(int code, long value); - - /** - @brief Set a value for an anlysis option. - @param code The code for the desired option. - @param v The desired value for the option specified. - @return Error code. - @see EN_Option - */ - int DLLEXPORT ENsetoption(int code, EN_API_FLOAT_TYPE v); - - /** - @brief Sets the level of hydraulic status reporting. - @param code Status reporting code. - @return Error code. - */ - int DLLEXPORT ENsetstatusreport(int code); - - /** - @brief Sets type of quality analysis called for - @param qualcode WQ parameter code, EN_QualityType - @param chemname Name of WQ constituent - @param chemunits Concentration units of WQ constituent - @param tracenode ID of node being traced (if applicable) - @return Error code. - @see EN_QualityType - - chemname and chemunits only apply when WQ analysis is for chemical. tracenode only applies when WQ analysis is source tracing. - */ - int DLLEXPORT ENsetqualtype(int qualcode, char *chemname, char *chemunits, char *tracenode); - - /** - @brief Get quality analysis information (type, chemical name, units, trace node ID) - @param[out] qualcode The EN_QualityType code being used. - @param[out] chemname The name of the WQ constituent. - @param[out] chemunits The cencentration units of the WQ constituent. - @param[out] tracenode The trace node ID. - @return Error code. - @see EN_QualityType - */ - int DLLEXPORT ENgetqualinfo(int *qualcode, char *chemname, char *chemunits, int *tracenode); - - /** - @brief Sets the node's base demand for a category. - @param nodeIndex The index of a node. - @param demandIdx The index of a demand category. - @param baseDemand The base demand multiplier for the selected category. - @return Error code. - @see ENgetbasedemand - */ - int DLLEXPORT ENsetbasedemand(int nodeIndex, int demandIdx, EN_API_FLOAT_TYPE baseDemand); - - /** - @brief Retrieves index of curve with specific ID. - @param id The ID of a curve. - @param[out] index The index of the named curve - @return Error code. - @see ENgetcurveid - */ - int DLLEXPORT ENgetcurveindex(char *id, int *index); - - /** - @brief Retrieves ID of a curve with specific index. - @param index The index of a curve. - @param[out] id The ID of the curve specified. - @return Error code. - @see ENsetcurveindex - - NOTE: 'id' must be able to hold MAXID characters - */ - int DLLEXPORT ENgetcurveid(int index, char *id); - - /** - @brief Retrieves number of points in a curve - @param index The index of a curve. - @param[out] len The length of the curve coordinate list - @return Error code. - @see ENgetcurvevalue - */ - int DLLEXPORT ENgetcurvelen(int index, int *len); - - /** - @brief retrieves x,y point for a specific point number and curve - @param curveIndex The index of a curve - @param pointIndex The index of a point in the curve - @param[out] x The x-value of the specified point. - @param[out] y The y-value of the specified point. - @return Error code. - @see ENgetcurvelen ENsetcurvevalue - */ - int DLLEXPORT ENgetcurvevalue(int curveIndex, int pointIndex, EN_API_FLOAT_TYPE *x, EN_API_FLOAT_TYPE *y); - - /** - @brief Sets x,y point for a specific point and curve. - @param curveIndex The index of a curve. - @param pointIndex The index of a point in the curve. - @param x The x-value of the point. - @param y The y-value of the point. - @return Error code. - */ - int DLLEXPORT ENsetcurvevalue(int curveIndex, int pointIndex, EN_API_FLOAT_TYPE x, EN_API_FLOAT_TYPE y); - - /** - @brief Sets x,y values for a specified curve. - @param index The index of a curve. - @param x An array of x-values for the curve. - @param y An array of y-values for the curve. - @param len The length of the arrays x and y. - @return Error code. - */ - int DLLEXPORT ENsetcurve(int index, EN_API_FLOAT_TYPE *x, EN_API_FLOAT_TYPE *y, int len); - - /** - @brief Adds a new curve appended to the end of the existing curves. - @param id The name of the curve to be added. - @return Error code. - @see ENgetcurveindex ENsetcurve - */ - int DLLEXPORT ENaddcurve(char *id); - - - /** - @brief Gets the number of premises, true actions, and false actions and the priority of an existing rule-based control. - @param index The index of a rule-based control. - @param nPremises The number of conditions in a rule-based control. - @param nTrueActions The number of actions that are executed when the conditions in the rule-based control are met. - @param nFalseActions The number of actions that are executed when the conditions in the rule-based control are not met. - @param priority The priority of a rule-based control. - @return Error code. - */ - int DLLEXPORT ENgetrule(int index, int *nPremises, int *nTrueActions, int *nFalseActions, EN_API_FLOAT_TYPE *priority); - - /** - @brief Sets the priority of the existing rule-based control. - @param index The index of a rule-based control. - @param priority The priority to be set in the rule-based control. - @return Error code. - */ - int DLLEXPORT ENsetrulepriority(int index, EN_API_FLOAT_TYPE priority); - - /** - @brief Gets the components of a premise/condition in an existing rule-based control. - @param indexRule The index of a rule-based control. - @param indexPremise The index of the premise. - @param logop The logiv operator (IF/AND/OR) in the premise - @param object The object (e.g. TANK) the premise is looking at. - @param indexObj The index of the object (e.g. the index of the tank). - @param variable The variable to be checked (e.g. level). - @param relop The relashionship operator (e.g. LARGER THAN) in the premise. - @param status The status of the object to be checked (e.g. CLOSED) - @param value The value of the variable to be checked (e.g. 5.5) - @return Error code. - */ - int DLLEXPORT ENgetpremise(int indexRule, int indexPremise, int *logop, int *object, int *indexObj, int *variable, int *relop, int *status, EN_API_FLOAT_TYPE *value); - - /** - @brief Sets the components of a premise/condition in an existing rule-based control. - @param indexRule The index of a rule-based control. - @param indexPremise The index of the premise. - @param logop The logiv operator (IF/AND/OR) in the premise - @param object The object (e.g. TANK) the premise is looking at. - @param indexObj The index of the object (e.g. the index of the tank). - @param variable The variable to be checked (e.g. level). - @param relop The relashionship operator (e.g. LARGER THAN) in the premise. - @param status The status of the object to be checked (e.g. CLOSED) - @param value The value of the variable to be checked (e.g. 5.5) - @return Error code. - */ - int DLLEXPORT ENsetpremise(int indexRule, int indexPremise, int logop, int object, int indexObj, int variable, int relop, int status, EN_API_FLOAT_TYPE value); - - /** - @brief Sets the index of an object in a premise of an existing rule-based control. - @param indexRule The index of a rule-based control. - @param indexPremise The index of the premise. - @param indexObj The index of the object (e.g. the index of the tank). - @return Error code. - */ - int DLLEXPORT ENsetpremiseindex(int indexRule, int indexPremise, int indexObj); - - /** - @brief Sets the status in a premise of an existing rule-based control. - @param indexRule The index of a rule-based control. - @param indexPremise The index of the premise. - @param status The status of the object to be checked (e.g. CLOSED) - @return Error code. - */ - int DLLEXPORT ENsetpremisestatus(int indexRule, int indexPremise, int status); - - /** - @brief Sets the value in a premise of an existing rule-based control. - @param indexRule The index of a rule-based control. - @param indexPremise The index of the premise. - @param value The value of the variable to be checked (e.g. 5.5) - @return Error code. - */ - int DLLEXPORT ENsetpremisevalue(int indexRule, int indexPremise, EN_API_FLOAT_TYPE value); - - /** - @brief Gets the components of a true-action in an existing rule-based control. - @param indexRule The index of a rule-based control. - @param indexAction The index of the action when the conditions in the rule are met. - @param indexLink The index of the link in the action (e.g. index of Pump 1) - @param status The status of the link (e.g. CLOSED) - @param setting The value of the link (e.g. pump speed 0.9) - @return Error code. - */ - int DLLEXPORT ENgettrueaction(int indexRule, int indexAction, int *indexLink, int *status, EN_API_FLOAT_TYPE *setting); - - /** - @brief Sets the components of a true-action in an existing rule-based control. - @param indexRule The index of a rule-based control. - @param indexAction The index of the action when the conditions in the rule are met. - @param indexLink The index of the link in the action (e.g. index of Pump 1) - @param status The status of the link (e.g. CLOSED) - @param setting The value of the link (e.g. pump speed 0.9) - @return Error code. - */ - int DLLEXPORT ENsettrueaction(int indexRule, int indexAction, int indexLink, int status, EN_API_FLOAT_TYPE setting); - - /** - @brief Gets the components of a false-action in an existing rule-based control. - @param indexRule The index of a rule-based control. - @param indexAction The index of the action when the conditions in the rule are not met. - @param indexLink The index of the link in the action (e.g. index of Pump 1) - @param status The status of the link (e.g. CLOSED) - @param setting The value of the link (e.g. pump speed 0.9) - @return Error code. - */ - int DLLEXPORT ENgetfalseaction(int indexRule, int indexAction, int *indexLink, int *status, EN_API_FLOAT_TYPE *setting); - - /** - @brief Sets the components of a false-action in an existing rule-based control. - @param indexRule The index of a rule-based control. - @param indexAction The index of the action when the conditions in the rule are not met. - @param indexLink The index of the link in the action (e.g. index of Pump 1) - @param status The status of the link (e.g. CLOSED) - @param setting The value of the link (e.g. pump speed 0.9) - @return Error code. - */ - int DLLEXPORT ENsetfalseaction(int indexRule, int indexAction, int indexLink, int status, EN_API_FLOAT_TYPE setting); - - /** - @brief Returns the ID of a rule. - @param indexRule The index of a rule-based control. - @param id The ID of the rule - @return Error code. - */ - int DLLEXPORT ENgetruleID(int indexRule, char* id); - - -#if defined(__cplusplus) -} -#endif - -#endif //EPANET2_H +/** @file epanet2.h + @see http://github.com/openwateranalytics/epanet + + */ + +/* + ******************************************************************* + + EPANET2.H - Prototypes for EPANET Functions Exported to DLL Toolkit + + VERSION: 2.00 + DATE: 5/8/00 + 10/25/00 + 3/1/01 + 8/15/07 (2.00.11) + 2/14/08 (2.00.12) + AUTHORS: L. Rossman - US EPA - NRMRL + OpenWaterAnalytics members: see git stats for contributors + + ******************************************************************* + */ + + +#ifndef EPANET2_H +#define EPANET2_H + +// the toolkit can be compiled with support for double-precision as well. +// just make sure that you use the correct #define in your client code. +#ifndef EN_API_FLOAT_TYPE + #define EN_API_FLOAT_TYPE float +#endif + +// --- define WINDOWS +#undef WINDOWS +#ifdef _WIN32 + #define WINDOWS +#endif +#ifdef __WIN32__ + #define WINDOWS +#endif + +// --- define DLLEXPORT +#ifndef DLLEXPORT + #ifdef WINDOWS + #ifdef __cplusplus + #define DLLEXPORT extern "C" __declspec(dllexport) + #else + #define DLLEXPORT __declspec(dllexport) __stdcall + #endif // __cplusplus + #elif defined(CYGWIN) + #define DLLEXPORT __stdcall + #elif defined(__APPLE__) + #ifdef __cplusplus + #define DLLEXPORT + #else + #define DLLEXPORT + #endif + #else + #define DLLEXPORT + #endif +#endif + + +// --- Define the EPANET toolkit constants + +/// Node property codes +typedef enum { + EN_ELEVATION = 0, /**< Node Elevation */ + EN_BASEDEMAND = 1, /**< Node Base Demand, from last demand category */ + EN_PATTERN = 2, /**< Node Demand Pattern */ + EN_EMITTER = 3, /**< Node Emitter Coefficient */ + EN_INITQUAL = 4, /**< Node initial quality */ + EN_SOURCEQUAL = 5, /**< Node source quality */ + EN_SOURCEPAT = 6, /**< Node source pattern index */ + EN_SOURCETYPE = 7, /**< Node source type */ + EN_TANKLEVEL = 8, /**< Tank Level */ + EN_DEMAND = 9, /**< Node current simulated demand */ + EN_HEAD = 10, /**< Node Head value */ + EN_PRESSURE = 11, /**< Node pressure value */ + EN_QUALITY = 12, /**< Node quality value */ + EN_SOURCEMASS = 13, /**< Node source mass value */ + EN_INITVOLUME = 14, /**< Tank or Reservoir initial volume */ + EN_MIXMODEL = 15, /**< Tank mixing model */ + EN_MIXZONEVOL = 16, /**< Tank mixing zone volume */ + EN_TANKDIAM = 17, /**< Tank diameter */ + EN_MINVOLUME = 18, /**< Tank minimum volume */ + EN_VOLCURVE = 19, /**< Tank volume curve index */ + EN_MINLEVEL = 20, /**< Tank minimum level */ + EN_MAXLEVEL = 21, /**< Tank maximum level */ + EN_MIXFRACTION = 22, /**< Tank mixing fraction */ + EN_TANK_KBULK = 23, /**< Tank bulk decay coefficient */ + EN_TANKVOLUME = 24, /**< Tank current volume */ + EN_MAXVOLUME = 25 /**< Tank maximum volume */ +} EN_NodeProperty; + +/// Link property codes +typedef enum { + EN_DIAMETER = 0, + EN_LENGTH = 1, + EN_ROUGHNESS = 2, + EN_MINORLOSS = 3, + EN_INITSTATUS = 4, + EN_INITSETTING = 5, + EN_KBULK = 6, + EN_KWALL = 7, + EN_FLOW = 8, + EN_VELOCITY = 9, + EN_HEADLOSS = 10, + EN_STATUS = 11, + EN_SETTING = 12, + EN_ENERGY = 13, + EN_LINKQUAL = 14, + EN_LINKPATTERN = 15, + EN_EFFICIENCY = 16, + EN_HEADCURVE = 17, + EN_EFFICIENCYCURVE = 18, + EN_PRICEPATTERN = 19 +} EN_LinkProperty; + +/// Time parameter codes +typedef enum { + EN_DURATION = 0, + EN_HYDSTEP = 1, + EN_QUALSTEP = 2, + EN_PATTERNSTEP = 3, + EN_PATTERNSTART = 4, + EN_REPORTSTEP = 5, + EN_REPORTSTART = 6, + EN_RULESTEP = 7, + EN_STATISTIC = 8, + EN_PERIODS = 9, + EN_STARTTIME = 10, + EN_HTIME = 11, + EN_QTIME = 12, + EN_HALTFLAG = 13, + EN_NEXTEVENT = 14, + EN_NEXTEVENTIDX = 15 +} EN_TimeProperty; + + +typedef enum { + EN_ITERATIONS = 0, + EN_RELATIVEERROR = 1 +} EN_AnalysisStatistic; + +typedef enum { + EN_NODECOUNT = 0, /**< Number of Nodes (Juntions + Tanks + Reservoirs) */ + EN_TANKCOUNT = 1, /**< Number of Tanks and Reservoirs */ + EN_LINKCOUNT = 2, /**< Number of Links (Pipes + Pumps + Valves) */ + EN_PATCOUNT = 3, /**< Number of Time Patterns */ + EN_CURVECOUNT = 4, /**< Number of Curves */ + EN_CONTROLCOUNT = 5, /**< Number of Control Statements */ + EN_RULECOUNT = 6 /**< Number of Rule-based Control Statements */ +} EN_CountType; + +typedef enum { + EN_JUNCTION = 0, + EN_RESERVOIR = 1, + EN_TANK = 2 +} EN_NodeType; + +typedef enum { + EN_CVPIPE = 0, /* Link types. */ + EN_PIPE = 1, /* See LinkType in TYPES.H */ + EN_PUMP = 2, + EN_PRV = 3, + EN_PSV = 4, + EN_PBV = 5, + EN_FCV = 6, + EN_TCV = 7, + EN_GPV = 8 +} EN_LinkType; + +typedef enum { + EN_NONE = 0, /* Quality analysis types. */ + EN_CHEM = 1, /* See QualType in TYPES.H */ + EN_AGE = 2, + EN_TRACE = 3 +} EN_QualityType; + +typedef enum { + EN_CONCEN = 0, /* Source quality types. */ + EN_MASS = 1, /* See SourceType in TYPES.H. */ + EN_SETPOINT = 2, + EN_FLOWPACED = 3 +} EN_SourceType; + +typedef enum { /* Head loss formula: */ + EN_HW = 0, /* Hazen-Williams */ + EN_DW = 1, /* Darcy-Weisbach */ + EN_CM = 2 /* Chezy-Manning */ +} EN_FormType; /* See FormType in TYPES.H */ + +typedef enum { + EN_CFS = 0, /* Flow units types. */ + EN_GPM = 1, /* See FlowUnitsType */ + EN_MGD = 2, /* in TYPES.H. */ + EN_IMGD = 3, + EN_AFD = 4, + EN_LPS = 5, + EN_LPM = 6, + EN_MLD = 7, + EN_CMH = 8, + EN_CMD = 9 +} EN_FlowUnits; + + +/// Simulation Option codes +typedef enum { + EN_TRIALS = 0, + EN_ACCURACY = 1, + EN_TOLERANCE = 2, + EN_EMITEXPON = 3, + EN_DEMANDMULT = 4 +} EN_Option; + +typedef enum { + EN_LOWLEVEL = 0, /* Control types. */ + EN_HILEVEL = 1, /* See ControlType */ + EN_TIMER = 2, /* in TYPES.H. */ + EN_TIMEOFDAY = 3 +} EN_ControlType; + + + +typedef enum { + EN_AVERAGE = 1, /* Time statistic types. */ + EN_MINIMUM = 2, /* See TstatType in TYPES.H */ + EN_MAXIMUM = 3, + EN_RANGE = 4 +} EN_StatisticType; + + + +typedef enum { + EN_MIX1 = 0, /* Tank mixing models */ + EN_MIX2 = 1, + EN_FIFO = 2, + EN_LIFO = 3 +} EN_MixingModel; + + + +typedef enum { + EN_NOSAVE = 0, + EN_SAVE = 1, + EN_INITFLOW = 10, + EN_SAVE_AND_INIT = 11 +} EN_SaveOption; + + + +typedef enum { + EN_CONST_HP = 0, /* constant horsepower */ + EN_POWER_FUNC = 1, /* power function */ + EN_CUSTOM = 2 /* user-defined custom curve */ +} EN_CurveType; + + + +// --- Declare the EPANET toolkit functions +#if defined(__cplusplus) +extern "C" { +#endif + + /** + @brief The EPANET Project wrapper object + */ + typedef struct EN_Project EN_Project; + typedef struct EN_Pattern EN_Pattern; + typedef struct EN_Curve EN_Curve; + + /** + @brief runs a complete EPANET simulation + @param inpFile pointer to name of input file (must exist) + @param rptFile pointer to name of report file (to be created) + @param binOutFile pointer to name of binary output file (to be created) + @param callback a callback function that takes a character string (char *) as its only parameter. + @return error code + + The callback function should reside in and be used by the calling + code to display the progress messages that EPANET generates + as it carries out its computations. If this feature is not + needed then the argument should be NULL. + */ + int DLLEXPORT ENepanet(char *inpFile, char *rptFile, char *binOutFile, void (*callback) (char *)); + + /** + @brief Initializes an EPANET session + @param rptFile pointer to name of report file (to be created) + @param binOutFile pointer to name of binary output file (to be created) + @param UnitsType flow units flag + @param HeadlossFormula headloss formula flag + @return error code + */ + int DLLEXPORT ENinit(char *rptFile, char *binOutFile, int UnitsType, int HeadlossFormula); + + /** + @brief Opens EPANET input file & reads in network data + @param inpFile pointer to name of input file (must exist) + @param rptFile pointer to name of report file (to be created) + @param binOutFile pointer to name of binary output file (to be created) + @return error code + */ + int DLLEXPORT ENopen(char *inpFile, char *rptFile, char *binOutFile); + + /** + @brief Saves current data to "INP" formatted text file. + @param filename The file path to create + @return Error code + */ + int DLLEXPORT ENsaveinpfile(char *filename); + + /** + @brief Frees all memory and files used by EPANET + @return Error code + */ + int DLLEXPORT ENclose(); + + /** + @brief Solves the network hydraulics for all time periods + @return Error code + */ + int DLLEXPORT ENsolveH(); + + /** + @brief Saves hydraulic results to binary file + @return Error code + + Must be called before ENreport() if no WQ simulation has been made. + Should not be called if ENsolveQ() will be used. + */ + int DLLEXPORT ENsaveH(); + + /** + @brief Sets up data structures for hydraulic analysis + @return Error code + */ + int DLLEXPORT ENopenH(); + + /** + @brief Initializes hydraulic analysis + @param initFlag 2-digit flag where 1st (left) digit indicates if link flows should be re-initialized (1) or not (0), and 2nd digit indicates if hydraulic results should be saved to file (1) or not (0). + @return Error code + */ + int DLLEXPORT ENinitH(int initFlag); + + /** + @brief Run a hydraulic solution period + @param[out] currentTime The current simulation time in seconds + @return Error or warning code + @see ENsolveH + + This function is used in a loop with ENnextH() to run + an extended period hydraulic simulation. + See ENsolveH() for an example. + */ + int DLLEXPORT ENrunH(long *currentTime); + + /** + @brief Determine time (in seconds) until next hydraulic event + @param[out] tStep Time (seconds) until next hydraulic event. 0 marks end of simulation period. + @return Error code + + This function is used in a loop with ENrunH() to run an extended period hydraulic simulation. + See ENsolveH() for an example. + */ + int DLLEXPORT ENnextH(long *tStep); + + + /** + @brief Frees data allocated by hydraulics solver + @return Error code + */ + int DLLEXPORT ENcloseH(); + + /** + @brief Copies binary hydraulics file to disk + @param filename Name of file to be created + @return Error code + */ + int DLLEXPORT ENsavehydfile(char *filename); + + /** + @brief Opens previously saved binary hydraulics file + @param filename Name of file to be used + @return Error code + */ + int DLLEXPORT ENusehydfile(char *filename); + + /** + @brief Solves for network water quality in all time periods + @return Error code + */ + int DLLEXPORT ENsolveQ(); + + /** + @brief Sets up data structures for WQ analysis + @return Error code + */ + int DLLEXPORT ENopenQ(); + + /** + @brief Initializes water quality analysis + @param saveFlag EN_SAVE (1) if results saved to file, EN_NOSAVE (0) if not + @return Error code + */ + int DLLEXPORT ENinitQ(int saveFlag); + + /** + @brief Retrieves hydraulic & WQ results at time t. + @param[out] currentTime Current simulation time, in seconds. + @return Error code + + This function is used in a loop with ENnextQ() to run + an extended period WQ simulation. See ENsolveQ() for + an example. + */ + int DLLEXPORT ENrunQ(long *currentTime); + + /** + @brief Advances WQ simulation to next hydraulic event. + @param[out] tStep Time in seconds until next hydraulic event. 0 marks end of simulation period. + @return Error code + + This function is used in a loop with ENrunQ() to run + an extended period WQ simulation. See ENsolveQ() for + an example. + */ + int DLLEXPORT ENnextQ(long *tStep); + + /** + @brief Advances WQ simulation by a single WQ time step + @param[out] timeLeft Time left in overall simulation (in seconds) + @return Error code + + This function is used in a loop with ENrunQ() to run + an extended period WQ simulation. + */ + int DLLEXPORT ENstepQ(long *timeLeft); + + /** + @brief Frees data allocated by water quality solver. + @return Error code. + */ + int DLLEXPORT ENcloseQ(); + + /** + @brief Writes line of text to the report file. + @param line Text string to write + @return Error code. + */ + int DLLEXPORT ENwriteline(char *line); + + /** + @brief Writes simulation report to the report file + @return Error code + */ + int DLLEXPORT ENreport(); + + /** + @brief Resets report options to default values + @return Error code + */ + int DLLEXPORT ENresetreport(); + + /** + @brief Processes a reporting format command + @return Error code + */ + int DLLEXPORT ENsetreport(char *reportFormat); + + /** + @brief Retrieves parameters that define a simple control + @param controlIndex Control index (position of control statement in the input file, starting from 1) + @param[out] controlType Control type code (see EPANET2.H) + @param[out] linkIndex Index of controlled link + @param[out] setting Control setting on link + @param[out] nodeIndex Index of controlling node (0 for TIMER or TIMEOFDAY control) + @param[out] level Control level (tank level, junction pressure, or time (seconds)) + @return Error code + */ + int DLLEXPORT ENgetcontrol(int controlIndex, int *controlType, int *linkIndex, EN_API_FLOAT_TYPE *setting, int *nodeIndex, EN_API_FLOAT_TYPE *level); + + /** + @brief Retrieves the number of components of a given type in the network. + @param code Component code (see EPANET2.H) + @param[out] count Number of components in network + @return Error code + */ + int DLLEXPORT ENgetcount(int code, int *count); + + /** + @brief Gets value for an analysis option + @param code Option code (see EPANET2.H) + @param[out] value Option value + @return Error code + */ + int DLLEXPORT ENgetoption(int code, EN_API_FLOAT_TYPE *value); + + /** + @brief Retrieves value of specific time parameter. + @param code Time parameter code + @param[out] value Value of time parameter. + @return Error code + */ + int DLLEXPORT ENgettimeparam(int code, long *value); + + /** + @brief Retrieves the flow units code + @param[out] code Code of flow units in use + @return Error code + */ + int DLLEXPORT ENgetflowunits(int *code); + + /** + @brief Retrieves the index of the time pattern with specified ID + @param id String ID of the time pattern + @param[out] index Index of the specified time pattern + @return Error code + @see ENgetpatternid + */ + int DLLEXPORT ENgetpatternindex(char *id, int *index); + + /** + @brief Retrieves ID of a time pattern with specific index. + @param index The index of a time pattern. + @param[out] id The string ID of the time pattern. + @return Error code + @see ENgetpatternindex + */ + int DLLEXPORT ENgetpatternid(int index, char *id); + + /** + @brief Retrieves the number of multipliers in a time pattern. + @param index The index of a time pattern. + @param[out] len The length of the time pattern. + @return Error code + */ + int DLLEXPORT ENgetpatternlen(int index, int *len); + + /** + @brief Retrive a multiplier from a pattern for a specific time period. + @param index The index of a time pattern + @param period The pattern time period. First time period is 1. + @param[out] value Pattern multiplier + @return Error code + */ + int DLLEXPORT ENgetpatternvalue(int index, int period, EN_API_FLOAT_TYPE *value); + + /** + @brief Retrieve the average multiplier value in a time pattern + @param index The index of a time pattern + @param[out] value The average of all of this time pattern's values + @return Error code + */ + int DLLEXPORT ENgetaveragepatternvalue(int index, EN_API_FLOAT_TYPE *value); + + /** + @brief Retrieve the type of quality analytis to be run. + @param[out] qualcode The quality analysis code number. + @param[out] tracenode The index of node being traced, if qualcode == trace + @return Error code + @see ENsetqualtype + */ + int DLLEXPORT ENgetqualtype(int *qualcode, int *tracenode); + + /** + @brief Get the text of an error code. + @param errcode The error code + @param[out] errmsg The error string represented by the code + @param maxLen The maximum number of characters to copy into the char pointer errmsg + @return Error code + */ + int DLLEXPORT ENgeterror(int errcode, char *errmsg, int maxLen); + + /** + @brief Get hydraulic simulation statistic + @param code The type of statistic to get + @param[out] value The value of the statistic + @return Error code + */ + int DLLEXPORT ENgetstatistic(int code, EN_API_FLOAT_TYPE* value); + + /** + @brief Get index of node with specified ID + @param id The string ID of the node + @param[out] index The node's index (first node is index 1) + @return Error code + @see ENgetnodeid + */ + int DLLEXPORT ENgetnodeindex(char *id, int *index); + + /** + @brief Get the string ID of the specified node. + @param index The index of the node (first node is index 1) + @param[out] id The string ID of the specified node. Up to MAXID characters will be copied, so id must be pre-allocated by the calling code to hold at least that many characters. + @return Error code + @see ENgetnodeindex + */ + int DLLEXPORT ENgetnodeid(int index, char *id); + + /** + @brief Get the type of node with specified index. + @param index The index of a node (first node is index 1) + @param[out] code The type code for the node. + @return Error code + */ + int DLLEXPORT ENgetnodetype(int index, int *code); + + /** + @brief Get a property value for specified node + @param index The index of a node (first node is index 1). + @param code The property type code + @param[out] value The value of the node's property. + @return Error code + @see EN_NodeProperty + */ + int DLLEXPORT ENgetnodevalue(int index, int code, EN_API_FLOAT_TYPE *value); + + /** + @brief Get coordinates (x,y) for a node. + @param index The index of a node (first node is index 1). + @param[out] x X-value of node's coordinate + @param[out] y Y-value of node's coordinate + @return Error code + @see ENsetcoord + */ + int DLLEXPORT ENgetcoord(int index, EN_API_FLOAT_TYPE *x, EN_API_FLOAT_TYPE *y); + + /** + @brief Set coordinates (x,y) for a node. + @param index The index of a node (first node is index 1) + @param x X-value of node's coordinate + @param y Y-value of node's coordinate + @return Error code + @see ENgetcoord + */ + int DLLEXPORT ENsetcoord(int index, EN_API_FLOAT_TYPE x, EN_API_FLOAT_TYPE y); + + /** + @brief Get the number of demand categories for a node. + @param nodeIndex The index of a node (first node is index 1) + @param[out] numDemands The number of demand categories + @return Error code + */ + int DLLEXPORT ENgetnumdemands(int nodeIndex, int *numDemands); + + /** + @brief Get a node's base demand for a specified category. + @param nodeIndex The index of a node (first node is index 1) + @param demandIndex The index of the demand category (starting at 1) + @return Error code + */ + int DLLEXPORT ENgetbasedemand(int nodeIndex, int demandIndex, EN_API_FLOAT_TYPE *baseDemand); + + /** + @brief Get the index of the demand pattern assigned to a node for a category index. + @param nodeIndex The index of a node (first node is index 1). + @param demandIndex The index of a category (first category is index 1). + @param[out] pattIndex The index of the pattern for this node and category. + @return Error code + */ + int DLLEXPORT ENgetdemandpattern(int nodeIndex, int demandIndex, int *pattIndex); + + /** + @brief Get the index of a Link with specified ID. + @param id The string ID of a link. + @param[out] index The index of the named link (first link is index 1) + @return Error code + @see ENgetlinkid + */ + int DLLEXPORT ENgetlinkindex(char *id, int *index); + + /** + @brief Get the string ID of a link with specified index + @param index The index of a link (first link is index 1) + @param[out] id The ID of the link. Up to MAXID characters will be copied, so id must be pre-allocated by the calling code to hold at least that many characters. + @return Error code + @see ENgetlinkindex + */ + int DLLEXPORT ENgetlinkid(int index, char *id); + + /** + @brief Get the link type code for a specified link + @param index The index of a link (first link is index 1) + @param[out] code The type code of the link. + @return Error code + @see EN_LinkType + */ + int DLLEXPORT ENgetlinktype(int index, EN_LinkType *code); + + /** + @brief Set the link type code for a specified link + @param id The id of a link + @param type The type code of the link. + @return Error code + @see EN_LinkType + */ + int DLLEXPORT ENsetlinktype(char *id, EN_LinkType type); + + /** + @brief Get the indexes of a link's start- and end-nodes. + @param index The index of a link (first link is index 1) + @param[out] node1 The index of the link's start node (first node is index 1). + @param[out] node2 The index of the link's end node (first node is index 1). + @return Error code + @see ENgetnodeid, ENgetlinkid + */ + int DLLEXPORT ENgetlinknodes(int index, int *node1, int *node2); + + /** + @brief Get a property value for specified link. + @param index The index of a node (first node is index 1). + @param code The parameter desired. + @param[out] value The value of the link's specified property. + @return Error code + @see ENgetnodevalue, EN_LinkProperty + */ + int DLLEXPORT ENgetlinkvalue(int index, int code, EN_API_FLOAT_TYPE *value); + + /** + @brief Get a curve's properties. + @param curveIndex The index of a curve (first curve is index 1). + @param[out] id The curve's string ID. Client code must preallocate at least MAXID characters. + @param[out] nValues The number of values in the curve's (x,y) list. + @param[out] xValues The curve's x-values. Pointer must be freed by client. + @param[out] yValues The curve's y-values. Pointer must be freed by client. + @return Error code. + */ + int DLLEXPORT ENgetcurve(int curveIndex, char* id, int *nValues, EN_API_FLOAT_TYPE **xValues, EN_API_FLOAT_TYPE **yValues); + + /** + @brief Retrieves the curve index for a specified pump index. + @param pumpIndex The index of a pump + @param[out] curveIndex The index of the curve used by the pump. + @return Error code. + */ + int DLLEXPORT ENgetheadcurveindex(int pumpIndex, int *curveIndex); + + /** + @brief Sets the curve id for a specified pump index. + @param pumpIndex The index of the pump + @param curveIndex The index of the curve used by the pump + @return Error code. + */ + int DLLEXPORT ENsetheadcurveindex(int pumpIndex, int curveIndex); + + /** + @brief Get the type of pump + @param linkIndex The index of the pump element + @param[out] outType The integer-typed pump curve type signifier (output parameter) + @return Error code + @see EN_CurveType + */ + int DLLEXPORT ENgetpumptype(int linkIndex, int *outType); + + /** + @brief Get the version number. This number is to be interpreted with implied decimals, i.e., "20100" == "2(.)01(.)00" + @param[out] version The version of EPANET + @return Error code. + */ + int DLLEXPORT ENgetversion(int *version); + + /** + @brief Specify parameters to define a simple control + @param cindex The index of the control to edit. First control is index 1. + @param ctype The type code to set for this control. + @param lindex The index of a link to control. + @param setting The control setting applied to the link. + @param nindex The index of a node used to control the link, or 0 for TIMER / TIMEOFDAY control. + @param level control point (tank level, junction pressure, or time in seconds). + @return Error code. + */ + int DLLEXPORT ENsetcontrol(int cindex, int ctype, int lindex, EN_API_FLOAT_TYPE setting, int nindex, EN_API_FLOAT_TYPE level); + + /** + @brief Set a property value for a node. + @param index The index of a node. First node is index 1. + @param code The code for the proprty to set. + @param v The value to set for this node and property. + @return Error code. + @see EN_NodeProperty + */ + int DLLEXPORT ENsetnodevalue(int index, int code, EN_API_FLOAT_TYPE v); + + /** + @brief Set a proprty value for a link. + @param index The index of a link. First link is index 1. + @param code The code for the property to set. + @param v The value to set for this link and property. + @return Error code. + @see EN_LinkProperty + */ + int DLLEXPORT ENsetlinkvalue(int index, int code, EN_API_FLOAT_TYPE v); + + /** + @brief Add a new time pattern. + @param id The string ID of the pattern to add. + @return Error code. + @see ENgetpatternindex + */ + int DLLEXPORT ENaddpattern(char *id); + + /** + @brief Set multipliers for a specific pattern + @param index The index of a pattern. First pattern is index 1. + @param f An array of multipliers + @param len The length of array f. + @return Error code. + @see ENgetpatternindex + */ + int DLLEXPORT ENsetpattern(int index, EN_API_FLOAT_TYPE *f, int len); + + /** + @brief Set the multiplier for a specific pattern at a specific period. + @param index The index of a pattern. First pattern is index 1. + @param period The period of the pattern to set. + @param value The value of the multiplier to set. + @return Error code. + */ + int DLLEXPORT ENsetpatternvalue(int index, int period, EN_API_FLOAT_TYPE value); + + /** + @brief Set the value for a time parameter. + @param code The code for the parameter to set. + @param value The desired value of the parameter. + @return Error code. + @see EN_TimeProperty + */ + int DLLEXPORT ENsettimeparam(int code, long value); + + /** + @brief Set a value for an anlysis option. + @param code The code for the desired option. + @param v The desired value for the option specified. + @return Error code. + @see EN_Option + */ + int DLLEXPORT ENsetoption(int code, EN_API_FLOAT_TYPE v); + + /** + @brief Sets the level of hydraulic status reporting. + @param code Status reporting code. + @return Error code. + */ + int DLLEXPORT ENsetstatusreport(int code); + + /** + @brief Sets type of quality analysis called for + @param qualcode WQ parameter code, EN_QualityType + @param chemname Name of WQ constituent + @param chemunits Concentration units of WQ constituent + @param tracenode ID of node being traced (if applicable) + @return Error code. + @see EN_QualityType + + chemname and chemunits only apply when WQ analysis is for chemical. tracenode only applies when WQ analysis is source tracing. + */ + int DLLEXPORT ENsetqualtype(int qualcode, char *chemname, char *chemunits, char *tracenode); + + /** + @brief Get quality analysis information (type, chemical name, units, trace node ID) + @param[out] qualcode The EN_QualityType code being used. + @param[out] chemname The name of the WQ constituent. + @param[out] chemunits The cencentration units of the WQ constituent. + @param[out] tracenode The trace node ID. + @return Error code. + @see EN_QualityType + */ + int DLLEXPORT ENgetqualinfo(int *qualcode, char *chemname, char *chemunits, int *tracenode); + + /** + @brief Sets the node's base demand for a category. + @param nodeIndex The index of a node. + @param demandIdx The index of a demand category. + @param baseDemand The base demand multiplier for the selected category. + @return Error code. + @see ENgetbasedemand + */ + int DLLEXPORT ENsetbasedemand(int nodeIndex, int demandIdx, EN_API_FLOAT_TYPE baseDemand); + + /** + @brief Retrieves index of curve with specific ID. + @param id The ID of a curve. + @param[out] index The index of the named curve + @return Error code. + @see ENgetcurveid + */ + int DLLEXPORT ENgetcurveindex(char *id, int *index); + + /** + @brief Retrieves ID of a curve with specific index. + @param index The index of a curve. + @param[out] id The ID of the curve specified. + @return Error code. + @see ENsetcurveindex + + NOTE: 'id' must be able to hold MAXID characters + */ + int DLLEXPORT ENgetcurveid(int index, char *id); + + /** + @brief Retrieves number of points in a curve + @param index The index of a curve. + @param[out] len The length of the curve coordinate list + @return Error code. + @see ENgetcurvevalue + */ + int DLLEXPORT ENgetcurvelen(int index, int *len); + + /** + @brief retrieves x,y point for a specific point number and curve + @param curveIndex The index of a curve + @param pointIndex The index of a point in the curve + @param[out] x The x-value of the specified point. + @param[out] y The y-value of the specified point. + @return Error code. + @see ENgetcurvelen ENsetcurvevalue + */ + int DLLEXPORT ENgetcurvevalue(int curveIndex, int pointIndex, EN_API_FLOAT_TYPE *x, EN_API_FLOAT_TYPE *y); + + /** + @brief Sets x,y point for a specific point and curve. + @param curveIndex The index of a curve. + @param pointIndex The index of a point in the curve. + @param x The x-value of the point. + @param y The y-value of the point. + @return Error code. + */ + int DLLEXPORT ENsetcurvevalue(int curveIndex, int pointIndex, EN_API_FLOAT_TYPE x, EN_API_FLOAT_TYPE y); + + /** + @brief Sets x,y values for a specified curve. + @param index The index of a curve. + @param x An array of x-values for the curve. + @param y An array of y-values for the curve. + @param len The length of the arrays x and y. + @return Error code. + */ + int DLLEXPORT ENsetcurve(int index, EN_API_FLOAT_TYPE *x, EN_API_FLOAT_TYPE *y, int len); + + /** + @brief Adds a new curve appended to the end of the existing curves. + @param id The name of the curve to be added. + @return Error code. + @see ENgetcurveindex ENsetcurve + */ + int DLLEXPORT ENaddcurve(char *id); + + + /** + @brief Gets the number of premises, true actions, and false actions and the priority of an existing rule-based control. + @param index The index of a rule-based control. + @param nPremises The number of conditions in a rule-based control. + @param nTrueActions The number of actions that are executed when the conditions in the rule-based control are met. + @param nFalseActions The number of actions that are executed when the conditions in the rule-based control are not met. + @param priority The priority of a rule-based control. + @return Error code. + */ + int DLLEXPORT ENgetrule(int index, int *nPremises, int *nTrueActions, int *nFalseActions, EN_API_FLOAT_TYPE *priority); + + /** + @brief Sets the priority of the existing rule-based control. + @param index The index of a rule-based control. + @param priority The priority to be set in the rule-based control. + @return Error code. + */ + int DLLEXPORT ENsetrulepriority(int index, EN_API_FLOAT_TYPE priority); + + /** + @brief Gets the components of a premise/condition in an existing rule-based control. + @param indexRule The index of a rule-based control. + @param indexPremise The index of the premise. + @param logop The logiv operator (IF/AND/OR) in the premise + @param object The object (e.g. TANK) the premise is looking at. + @param indexObj The index of the object (e.g. the index of the tank). + @param variable The variable to be checked (e.g. level). + @param relop The relashionship operator (e.g. LARGER THAN) in the premise. + @param status The status of the object to be checked (e.g. CLOSED) + @param value The value of the variable to be checked (e.g. 5.5) + @return Error code. + */ + int DLLEXPORT ENgetpremise(int indexRule, int indexPremise, int *logop, int *object, int *indexObj, int *variable, int *relop, int *status, EN_API_FLOAT_TYPE *value); + + /** + @brief Sets the components of a premise/condition in an existing rule-based control. + @param indexRule The index of a rule-based control. + @param indexPremise The index of the premise. + @param logop The logiv operator (IF/AND/OR) in the premise + @param object The object (e.g. TANK) the premise is looking at. + @param indexObj The index of the object (e.g. the index of the tank). + @param variable The variable to be checked (e.g. level). + @param relop The relashionship operator (e.g. LARGER THAN) in the premise. + @param status The status of the object to be checked (e.g. CLOSED) + @param value The value of the variable to be checked (e.g. 5.5) + @return Error code. + */ + int DLLEXPORT ENsetpremise(int indexRule, int indexPremise, int logop, int object, int indexObj, int variable, int relop, int status, EN_API_FLOAT_TYPE value); + + /** + @brief Sets the index of an object in a premise of an existing rule-based control. + @param indexRule The index of a rule-based control. + @param indexPremise The index of the premise. + @param indexObj The index of the object (e.g. the index of the tank). + @return Error code. + */ + int DLLEXPORT ENsetpremiseindex(int indexRule, int indexPremise, int indexObj); + + /** + @brief Sets the status in a premise of an existing rule-based control. + @param indexRule The index of a rule-based control. + @param indexPremise The index of the premise. + @param status The status of the object to be checked (e.g. CLOSED) + @return Error code. + */ + int DLLEXPORT ENsetpremisestatus(int indexRule, int indexPremise, int status); + + /** + @brief Sets the value in a premise of an existing rule-based control. + @param indexRule The index of a rule-based control. + @param indexPremise The index of the premise. + @param value The value of the variable to be checked (e.g. 5.5) + @return Error code. + */ + int DLLEXPORT ENsetpremisevalue(int indexRule, int indexPremise, EN_API_FLOAT_TYPE value); + + /** + @brief Gets the components of a true-action in an existing rule-based control. + @param indexRule The index of a rule-based control. + @param indexAction The index of the action when the conditions in the rule are met. + @param indexLink The index of the link in the action (e.g. index of Pump 1) + @param status The status of the link (e.g. CLOSED) + @param setting The value of the link (e.g. pump speed 0.9) + @return Error code. + */ + int DLLEXPORT ENgettrueaction(int indexRule, int indexAction, int *indexLink, int *status, EN_API_FLOAT_TYPE *setting); + + /** + @brief Sets the components of a true-action in an existing rule-based control. + @param indexRule The index of a rule-based control. + @param indexAction The index of the action when the conditions in the rule are met. + @param indexLink The index of the link in the action (e.g. index of Pump 1) + @param status The status of the link (e.g. CLOSED) + @param setting The value of the link (e.g. pump speed 0.9) + @return Error code. + */ + int DLLEXPORT ENsettrueaction(int indexRule, int indexAction, int indexLink, int status, EN_API_FLOAT_TYPE setting); + + /** + @brief Gets the components of a false-action in an existing rule-based control. + @param indexRule The index of a rule-based control. + @param indexAction The index of the action when the conditions in the rule are not met. + @param indexLink The index of the link in the action (e.g. index of Pump 1) + @param status The status of the link (e.g. CLOSED) + @param setting The value of the link (e.g. pump speed 0.9) + @return Error code. + */ + int DLLEXPORT ENgetfalseaction(int indexRule, int indexAction, int *indexLink, int *status, EN_API_FLOAT_TYPE *setting); + + /** + @brief Sets the components of a false-action in an existing rule-based control. + @param indexRule The index of a rule-based control. + @param indexAction The index of the action when the conditions in the rule are not met. + @param indexLink The index of the link in the action (e.g. index of Pump 1) + @param status The status of the link (e.g. CLOSED) + @param setting The value of the link (e.g. pump speed 0.9) + @return Error code. + */ + int DLLEXPORT ENsetfalseaction(int indexRule, int indexAction, int indexLink, int status, EN_API_FLOAT_TYPE setting); + + /** + @brief Returns the ID of a rule. + @param indexRule The index of a rule-based control. + @param id The ID of the rule + @return Error code. + */ + int DLLEXPORT ENgetruleID(int indexRule, char* id); + + /** + @brief Adds a new node + @param id The name of the node to be added. + @param nodeType The node type code + @return Error code. + */ + int DLLEXPORT ENaddnode(char *id, EN_NodeType nodeType); + + /** + @brief Adds a new link + @param id The name of the link to be added. + @param linkType The link type code + @param fromNode The id of the from node + @param toNode The id of the to node + @return Error code. + */ + int DLLEXPORT ENaddlink(char *id, EN_LinkType linkType, char *fromNode, char *toNode); + + /** + @brief Deletes a node + @param nodeIndex The node index + @return Error code. + */ + int DLLEXPORT ENdeletenode(int nodeIndex); + + /** + @brief Deletes a link + @param linkIndex The link index + @return Error code. + */ + int DLLEXPORT ENdeletelink(int linkIndex); + + + + + /*************************************************** + + Threadsafe versions of all epanet functions + + ***************************************************/ + int DLLEXPORT EN_alloc(EN_Project **p); + int DLLEXPORT EN_free(EN_Project *p); + int DLLEXPORT EN_epanet(char *inpFile, char *rptFile, char *binOutFile, void (*callback) (char *)); + int DLLEXPORT EN_init(EN_Project *p, char *rptFile, char *binOutFile, EN_FlowUnits UnitsType, EN_FormType HeadlossFormula); + int DLLEXPORT EN_open(EN_Project *p, char *inpFile, char *rptFile, char *binOutFile); + int DLLEXPORT EN_saveinpfile(EN_Project *p, char *filename); + int DLLEXPORT EN_close(EN_Project *p); + int DLLEXPORT EN_solveH(EN_Project *p); + int DLLEXPORT EN_saveH(EN_Project *p); + int DLLEXPORT EN_openH(EN_Project *p); + int DLLEXPORT EN_initH(EN_Project *p, int EN_SaveOption); + int DLLEXPORT EN_runH(EN_Project *p, long *currentTime); + int DLLEXPORT EN_nextH(EN_Project *p, long *tStep); + int DLLEXPORT EN_closeH(EN_Project *p); + int DLLEXPORT EN_savehydfile(EN_Project *p, char *filename); + int DLLEXPORT EN_usehydfile(EN_Project *p, char *filename); + int DLLEXPORT EN_solveQ(EN_Project *p); + int DLLEXPORT EN_openQ(EN_Project *p); + int DLLEXPORT EN_initQ(EN_Project *p, int saveFlag); + int DLLEXPORT EN_runQ(EN_Project *p, long *currentTime); + int DLLEXPORT EN_nextQ(EN_Project *p, long *tStep); + int DLLEXPORT EN_stepQ(EN_Project *p, long *timeLeft); + int DLLEXPORT EN_closeQ(EN_Project *p); + int DLLEXPORT EN_writeline(EN_Project *p, char *line); + int DLLEXPORT EN_report(EN_Project *p); + int DLLEXPORT EN_resetreport(EN_Project *p); + int DLLEXPORT EN_setreport(EN_Project *p, char *reportFormat); + int DLLEXPORT EN_getcontrol(EN_Project *p, int controlIndex, int *controlType, int *linkIndex, EN_API_FLOAT_TYPE *setting, int *nodeIndex, EN_API_FLOAT_TYPE *level); + int DLLEXPORT EN_getcount(EN_Project *p, EN_CountType code, int *count); + int DLLEXPORT EN_getoption(EN_Project *p, EN_Option opt, EN_API_FLOAT_TYPE *value); + int DLLEXPORT EN_gettimeparam(EN_Project *p, int code, long *value); + int DLLEXPORT EN_getflowunits(EN_Project *p, int *code); + int DLLEXPORT EN_getpatternindex(EN_Project *p, char *id, int *index); + int DLLEXPORT EN_getpatternid(EN_Project *p, int index, char *id); + int DLLEXPORT EN_getpatternlen(EN_Project *p, int index, int *len); + int DLLEXPORT EN_getpatternvalue(EN_Project *p, int index, int period, EN_API_FLOAT_TYPE *value); + int DLLEXPORT EN_getaveragepatternvalue(EN_Project *p, int index, EN_API_FLOAT_TYPE *value); + int DLLEXPORT EN_getqualtype(EN_Project *p, int *qualcode, int *tracenode); + int DLLEXPORT EN_geterror(int errcode, char *errmsg, int maxLen); + int DLLEXPORT EN_getstatistic(EN_Project *p, int code, EN_API_FLOAT_TYPE* value); + int DLLEXPORT EN_getnodeindex(EN_Project *p, char *id, int *index); + int DLLEXPORT EN_getnodeid(EN_Project *p, int index, char *id); + int DLLEXPORT EN_getnodetype(EN_Project *p, int index, int *code); + int DLLEXPORT EN_getnodevalue(EN_Project *p, int index, int code, EN_API_FLOAT_TYPE *value); + int DLLEXPORT EN_getcoord(EN_Project *p, int index, EN_API_FLOAT_TYPE *x, EN_API_FLOAT_TYPE *y); + int DLLEXPORT EN_setcoord(EN_Project *p, int index, EN_API_FLOAT_TYPE x, EN_API_FLOAT_TYPE y); + int DLLEXPORT EN_getnumdemands(EN_Project *p, int nodeIndex, int *numDemands); + int DLLEXPORT EN_getbasedemand(EN_Project *p, int nodeIndex, int demandIndex, EN_API_FLOAT_TYPE *baseDemand); + int DLLEXPORT EN_getdemandpattern(EN_Project *p, int nodeIndex, int demandIndex, int *pattIndex); + int DLLEXPORT EN_getlinkindex(EN_Project *p, char *id, int *index); + int DLLEXPORT EN_getlinkid(EN_Project *p, int index, char *id); + int DLLEXPORT EN_getlinktype(EN_Project *p, int index, EN_LinkType *code); + int DLLEXPORT EN_setlinktype(EN_Project *p, char *id, EN_LinkType type); + int DLLEXPORT EN_getlinknodes(EN_Project *p, int index, int *node1, int *node2); + int DLLEXPORT EN_getlinkvalue(EN_Project *p, int index, EN_LinkProperty code, EN_API_FLOAT_TYPE *value); + int DLLEXPORT EN_getcurve(EN_Project *p, int curveIndex, char* id, int *nValues, EN_API_FLOAT_TYPE **xValues, EN_API_FLOAT_TYPE **yValues); + int DLLEXPORT EN_getheadcurveindex(EN_Project *p, int pumpIndex, int *curveIndex); + int DLLEXPORT EN_setheadcurveindex(EN_Project *p, int pumpIndex, int curveIndex); + int DLLEXPORT EN_getpumptype(EN_Project *p, int linkIndex, int *outType); + int DLLEXPORT EN_getversion(int *version); + int DLLEXPORT EN_setcontrol(EN_Project *p, int cindex, int ctype, int lindex, EN_API_FLOAT_TYPE setting, int nindex, EN_API_FLOAT_TYPE level); + int DLLEXPORT EN_setnodevalue(EN_Project *p, int index, int code, EN_API_FLOAT_TYPE v); + int DLLEXPORT EN_setlinkvalue(EN_Project *p, int index, int code, EN_API_FLOAT_TYPE v); + int DLLEXPORT EN_addpattern(EN_Project *p, char *id); + int DLLEXPORT EN_setpattern(EN_Project *p, int index, EN_API_FLOAT_TYPE *f, int len); + int DLLEXPORT EN_setpatternvalue(EN_Project *p, int index, int period, EN_API_FLOAT_TYPE value); + int DLLEXPORT EN_settimeparam(EN_Project *p, int code, long value); + int DLLEXPORT EN_setoption(EN_Project *p, int code, EN_API_FLOAT_TYPE v); + int DLLEXPORT EN_setstatusreport(EN_Project *p, int code); + int DLLEXPORT EN_setqualtype(EN_Project *p, int qualcode, char *chemname, char *chemunits, char *tracenode); + int DLLEXPORT EN_getqualinfo(EN_Project *p, int *qualcode, char *chemname, char *chemunits, int *tracenode); + int DLLEXPORT EN_setbasedemand(EN_Project *p, int nodeIndex, int demandIdx, EN_API_FLOAT_TYPE baseDemand); + int DLLEXPORT EN_getcurveindex(EN_Project *p, char *id, int *index); + int DLLEXPORT EN_getcurveid(EN_Project *p, int index, char *id); + int DLLEXPORT EN_getcurvelen(EN_Project *p, int index, int *len); + int DLLEXPORT EN_getcurvevalue(EN_Project *p, int curveIndex, int pointIndex, EN_API_FLOAT_TYPE *x, EN_API_FLOAT_TYPE *y); + int DLLEXPORT EN_setcurvevalue(EN_Project *p, int curveIndex, int pointIndex, EN_API_FLOAT_TYPE x, EN_API_FLOAT_TYPE y); + int DLLEXPORT EN_setcurve(EN_Project *p, int index, EN_API_FLOAT_TYPE *x, EN_API_FLOAT_TYPE *y, int len); + int DLLEXPORT EN_addcurve(EN_Project *p, char *id); + int DLLEXPORT EN_getrule(EN_Project *p, int index, int *nPremises, int *nTrueActions, int *nFalseActions, EN_API_FLOAT_TYPE *priority); + int DLLEXPORT EN_setrulepriority(EN_Project *p, int index, EN_API_FLOAT_TYPE priority); + int DLLEXPORT EN_getpremise(EN_Project *p, int indexRule, int indexPremise, int *logop, int *object, int *indexObj, int *variable, int *relop, int *status, EN_API_FLOAT_TYPE *value); + int DLLEXPORT EN_setpremise(EN_Project *p, int indexRule, int indexPremise, int logop, int object, int indexObj, int variable, int relop, int status, EN_API_FLOAT_TYPE value); + int DLLEXPORT EN_setpremiseindex(EN_Project *p, int indexRule, int indexPremise, int indexObj); + int DLLEXPORT EN_setpremisestatus(EN_Project *p, int indexRule, int indexPremise, int status); + int DLLEXPORT EN_setpremisevalue(EN_Project *p, int indexRule, int indexPremise, EN_API_FLOAT_TYPE value); + int DLLEXPORT EN_gettrueaction(EN_Project *p, int indexRule, int indexAction, int *indexLink, int *status, EN_API_FLOAT_TYPE *setting); + int DLLEXPORT EN_settrueaction(EN_Project *p, int indexRule, int indexAction, int indexLink, int status, EN_API_FLOAT_TYPE setting); + int DLLEXPORT EN_getfalseaction(EN_Project *p, int indexRule, int indexAction, int *indexLink, int *status, EN_API_FLOAT_TYPE *setting); + int DLLEXPORT EN_setfalseaction(EN_Project *p, int indexRule, int indexAction, int indexLink, int status, EN_API_FLOAT_TYPE setting); + int DLLEXPORT EN_getruleID(EN_Project *p, int indexRule, char* id); + int DLLEXPORT EN_addnode(EN_Project *p, char *id, EN_NodeType nodeType); + int DLLEXPORT EN_addlink(EN_Project *p, char *id, EN_LinkType linkType, char *fromNode, char *toNode); + int DLLEXPORT EN_deletenode(EN_Project *p, int nodeIndex); + int DLLEXPORT EN_deletelink(EN_Project *p, int linkIndex); + + + + + + +#if defined(__cplusplus) +} +#endif + +#endif //EPANET2_H diff --git a/include/epanet2.vb b/include/epanet2.vb index 69c3231..44a47a1 100644 --- a/include/epanet2.vb +++ b/include/epanet2.vb @@ -57,7 +57,9 @@ Public Const EN_ENERGY = 13 Public Const EN_LINKQUAL = 14 'ES Public Const EN_LINKPATTERN = 15 Public Const EN_EFFICIENCY = 16 -Public Const EN_PRICEPATTERN = 17 +Public Const EN_HEADCURVE = 17 +Public Const EN_EFFICIENCYCURVE = 18 +Public Const EN_PRICEPATTERN = 19 Public Const EN_DURATION = 0 ' Time parameters Public Const EN_HYDSTEP = 1 @@ -172,7 +174,7 @@ Public Const EN_CUSTOM = 2 ' user-defined custom curve Declare Function ENreport Lib "epanet2.dll" () As Int32 Declare Function ENresetreport Lib "epanet2.dll" () As Int32 Declare Function ENsetreport Lib "epanet2.dll" (ByVal S As String) As Int32 - + Declare Function ENgetcontrol Lib "epanet2.dll" (ByVal Cindex As Int32, ByRef CtlType As Int32, ByRef Lindex As Int32, ByRef Setting As Single, ByRef Nindex As Int32, ByRef Level As Single) As Int32 Declare Function ENgetcount Lib "epanet2.dll" (ByVal Code As Int32, ByRef Value As Int32) As Int32 Declare Function ENgetoption Lib "epanet2.dll" (ByVal Code As Int32, ByRef Value As Single) As Int32 @@ -217,15 +219,15 @@ Public Const EN_CUSTOM = 2 ' user-defined custom curve Declare Function ENsetoption Lib "epanet2.dll" (ByVal Code As Int32, ByVal Value As Single) As Int32 Declare Function ENsetstatusreport Lib "epanet2.dll" (ByVal Code As Int32) As Int32 Declare Function ENsetqualtype Lib "epanet2.dll" (ByVal QualCode As Int32, ByVal ChemName As String, ByVal ChemUnits As String, ByVal TraceNode As String) As Int32 - + Declare Function ENaddpattern Lib "epanet2.dll" (ByVal ID As String) As Int32 Declare Function ENgetcurveindex Lib "epanet2.dll" (ByVal ID As String, ByRef Index As Int32) As Int32 Declare Function ENgetcurveid Lib "epanet2.dll" (ByVal Index As Int32, ByVal ID As StringBuilder) As Int32 Declare Function ENgetcurvelen Lib "epanet2.dll" (ByVal Index As Int32, ByRef L As Int32) As Int32 - Declare Function ENgetcurvevalue Lib "epanet2.dll" (ByVal Index As Int32, ByVal Pnt As Int32, ByRef X As Single, ByRef Y As Single) As Int32 + Declare Function ENgetcurvevalue Lib "epanet2.dll" (ByVal Index As Int32, ByVal Pnt As Int32, ByRef X As Single, ByRef Y As Single) As Int32 Declare Function ENsetcurvevalue Lib "epanet2.dll" (ByVal Index As Int32, ByVal Pnt As Int32, ByVal X As Single, ByVal Y As Single) As Int32 - Declare Function ENsetcurve Lib "epanet2.dll" (ByVal Index as Int32, ByRef X as Single, ByRef Y as Single, ByVal N as Int32) as Int32 + Declare Function ENsetcurve Lib "epanet2.dll" (ByVal Index as Int32, ByRef X as Single, ByRef Y as Single, ByVal N as Int32) as Int32 Declare Function ENaddcurve Lib "epanet2.dll" (ByVal ID As String) As Int32 Declare Function ENaddcurve Lib "epanet2.dll" (ByVal ID As String) As Long @@ -244,5 +246,5 @@ Declare Function ENaddcurve Lib "epanet2.dll" (ByVal ID As String) As Long Declare Function ENgetruleID Lib "epanet2.dll" (ByVal indexRule As Int32, ByVal id As StringBuilder) As Int32 - + End Module diff --git a/run/main.c b/run/main.c index a96c979..c2480b1 100644 --- a/run/main.c +++ b/run/main.c @@ -2,11 +2,11 @@ #include #include "epanet2.h" -#define MAXMSG 79 /* Max. # characters in message text */ +#define MAXMSG 255 /* Max. # characters in message text */ #define MAXWARNCODE 99 /* text copied here, no more need of include "text.h" */ #define FMT01 "\nEPANET Version %d.%d.%d\n" -#define FMT03 "\n Correct syntax is:\n epanet \n" +#define FMT03 "\n Correct syntax is:\n %s \n" #define FMT09 "\nEPANET completed.\n" #define FMT10 "\nEPANET completed. There are warnings." #define FMT11 "\nEPANET completed. There are errors." @@ -43,7 +43,7 @@ int main(int argc, char *argv[]) char errmsg[MAXMSG+1]=""; int errcode; int version; - char s[25]; + char s[256]; int major; int minor; int patch; @@ -60,7 +60,8 @@ int main(int argc, char *argv[]) /* Check for proper number of command line arguments */ if (argc < 2) { - writeConsole(FMT03); + sprintf(s, FMT03, argv[0]); + writeConsole(s); return(1); } diff --git a/src/enumstxt.h b/src/enumstxt.h index 0863d0c..c3abbc3 100755 --- a/src/enumstxt.h +++ b/src/enumstxt.h @@ -1,137 +1,129 @@ -/* -*********************************************************************** - -ENUMSTXT.H -- Text strings for enumerated data types in EPANET - -VERSION: 2.00 -DATE: 5/8/00 -AUTHOR: L. Rossman - US EPA - NRMRL - -********************************************************************** -*/ - -#ifndef ENUMSTXT_H -#define ENUMSTXT_H - -char *NodeTxt[] = {t_JUNCTION, - t_RESERVOIR, - t_TANK}; - -char *LinkTxt[] = {w_CV, - w_PIPE, - w_PUMP, - w_PRV, - w_PSV, - w_PBV, - w_FCV, - w_TCV, - w_GPV}; - -char *StatTxt[] = {t_XHEAD, - t_TEMPCLOSED, - t_CLOSED, - t_OPEN, - t_ACTIVE, - t_XFLOW, - t_XFCV, - t_XPRESSURE, - t_FILLING, - t_EMPTYING}; - -char *FormTxt[] = {w_HW, - w_DW, - w_CM}; - -char *RptFormTxt[] = {t_HW, - t_DW, - t_CM}; - -char *RptFlowUnitsTxt[] = {u_CFS, - u_GPM, - u_MGD, - u_IMGD, - u_AFD, - u_LPS, - u_LPM, - u_MLD, - u_CMH, - u_CMD}; - -char *FlowUnitsTxt[] = {w_CFS, - w_GPM, - w_MGD, - w_IMGD, - w_AFD, - w_LPS, - w_LPM, - w_MLD, - w_CMH, - w_CMD}; - -char *PressUnitsTxt[] = {w_PSI, - w_KPA, - w_METERS}; - -char *QualTxt[] = {w_NONE, - w_CHEM, - w_AGE, - w_TRACE}; - - -char *SourceTxt[] = {w_CONCEN, - w_MASS, - w_SETPOINT, - w_FLOWPACED}; - -char *ControlTxt[] = {w_BELOW, - w_ABOVE, - w_TIME, - w_CLOCKTIME}; - -char *TstatTxt[] = {w_NONE, - w_AVG, - w_MIN, - w_MAX, - w_RANGE}; - -char *MixTxt[] = {w_MIXED, - w_2COMP, - w_FIFO, - w_LIFO, - NULL}; - -char *RptFlagTxt[] = {w_NO, - w_YES, - w_FULL}; - -char *SectTxt[] = {s_TITLE, s_JUNCTIONS, s_RESERVOIRS, - s_TANKS, s_PIPES, s_PUMPS, - s_VALVES, s_CONTROLS, s_RULES, - s_DEMANDS, s_SOURCES, s_EMITTERS, - s_PATTERNS, s_CURVES, s_QUALITY, - s_STATUS, s_ROUGHNESS, s_ENERGY, - s_REACTIONS, s_MIXING, s_REPORT, - s_TIMES, s_OPTIONS, s_COORDS, - s_VERTICES, s_LABELS, s_BACKDROP, - s_TAGS, s_END, - NULL}; - -char *RptSectTxt[] = {NULL, t_JUNCTION, t_RESERVOIR, - t_TANK, t_PIPE, t_PUMP, - t_VALVE, t_CONTROL, t_RULE, - t_DEMANDFOR,t_SOURCE, t_EMITTER, - t_PATTERN, t_CURVE, t_QUALITY, - t_STATUS, t_ROUGHNESS,t_ENERGY, - t_REACTION, t_MIXING, t_REPORT, - t_TIME, t_OPTION}; - -char *Fldname[] = {t_ELEV, t_DEMAND, t_HEAD, - t_PRESSURE, t_QUALITY, t_LENGTH, - t_DIAM, t_FLOW, t_VELOCITY, - t_HEADLOSS, t_LINKQUAL, t_LINKSTATUS, - t_SETTING, t_REACTRATE, t_FRICTION, - "", "", "", "", "", "", NULL}; - - -#endif \ No newline at end of file +/* +*********************************************************************** + +ENUMSTXT.H -- Text strings for enumerated data types in EPANET + +VERSION: 2.00 +DATE: 5/8/00 +AUTHOR: L. Rossman + US EPA - NRMRL + +********************************************************************** +*/ + +#ifndef ENUMSTXT_H +#define ENUMSTXT_H +#include "text.h" + +char *NodeTxt[] = {t_JUNCTION, + t_RESERVOIR, + t_TANK}; + +char *LinkTxt[] = {w_CV, + w_PIPE, + w_PUMP, + w_PRV, + w_PSV, + w_PBV, + w_FCV, + w_TCV, + w_GPV}; + +char *StatTxt[] = {t_XHEAD, + t_TEMPCLOSED, + t_CLOSED, + t_OPEN, + t_ACTIVE, + t_XFLOW, + t_XFCV, + t_XPRESSURE, + t_FILLING, + t_EMPTYING}; + +char *FormTxt[] = {w_HW, + w_DW, + w_CM}; + +char *RptFormTxt[] = {t_HW, + t_DW, + t_CM}; + +char *RptFlowUnitsTxt[] = {u_CFS, + u_GPM, + u_MGD, + u_IMGD, + u_AFD, + u_LPS, + u_LPM, + u_MLD, + u_CMH, + u_CMD}; + +char *FlowUnitsTxt[] = {w_CFS, + w_GPM, + w_MGD, + w_IMGD, + w_AFD, + w_LPS, + w_LPM, + w_MLD, + w_CMH, + w_CMD}; + +char *PressUnitsTxt[] = {w_PSI, + w_KPA, + w_METERS}; + +char *QualTxt[] = {w_NONE, + w_CHEM, + w_AGE, + w_TRACE}; + + +char *SourceTxt[] = {w_CONCEN, + w_MASS, + w_SETPOINT, + w_FLOWPACED}; + +char *ControlTxt[] = {w_BELOW, + w_ABOVE, + w_TIME, + w_CLOCKTIME}; + +char *TstatTxt[] = {w_NONE, + w_AVG, + w_MIN, + w_MAX, + w_RANGE}; + +char *MixTxt[] = {w_MIXED, + w_2COMP, + w_FIFO, + w_LIFO, + NULL}; + +char *RptFlagTxt[] = {w_NO, + w_YES, + w_FULL}; + +char *SectTxt[] = {s_TITLE, s_JUNCTIONS, s_RESERVOIRS, + s_TANKS, s_PIPES, s_PUMPS, + s_VALVES, s_CONTROLS, s_RULES, + s_DEMANDS, s_SOURCES, s_EMITTERS, + s_PATTERNS, s_CURVES, s_QUALITY, + s_STATUS, s_ROUGHNESS, s_ENERGY, + s_REACTIONS, s_MIXING, s_REPORT, + s_TIMES, s_OPTIONS, s_COORDS, + s_VERTICES, s_LABELS, s_BACKDROP, + s_TAGS, s_END, + NULL}; + +char *Fldname[] = {t_ELEV, t_DEMAND, t_HEAD, + t_PRESSURE, t_QUALITY, t_LENGTH, + t_DIAM, t_FLOW, t_VELOCITY, + t_HEADLOSS, t_LINKQUAL, t_LINKSTATUS, + t_SETTING, t_REACTRATE, t_FRICTION, + "", "", "", "", "", "", NULL}; + + +#endif diff --git a/src/epanet.c b/src/epanet.c old mode 100755 new mode 100644 index 95ee7a7..6059fc0 --- a/src/epanet.c +++ b/src/epanet.c @@ -1,3476 +1,4697 @@ -/* -******************************************************************************* - -EPANET.C -- Hydraulic & Water Quality Simulator for Water Distribution Networks - -VERSION: 2.00 -DATE: 5/30/00 - 9/7/00 - 10/25/00 - 3/1/01 - 11/19/01 - 6/24/02 - 8/15/07 (2.00.11) - 2/14/08 (2.00.12) - AUTHORS: L. Rossman - US EPA - NRMRL - OpenWaterAnalytics members: see git stats for contributors - -EPANET performs extended period hydraulic and water quality analysis of -looped, pressurized piping networks. The program consists of the -following code modules: - - EPANET.C -- main module providing supervisory control - INPUT1.C -- controls processing of input data - INPUT2.C -- reads data from input file - INPUT3.C -- parses individual lines of input data - INPFILE.C -- saves modified input data to a text file - RULES.C -- implements rule-based control of piping system - HYDRAUL.C -- computes extended period hydraulic behavior - QUALITY.C -- tracks transport & fate of water quality - OUTPUT.C -- handles transfer of data to and from binary files - REPORT.C -- handles reporting of results to text file - SMATRIX.C -- sparse matrix linear equation solver routines - MEMPOOL.C -- memory allocation routines - HASH.C -- hash table routines - -The program can be compiled as either a stand-alone console application -or as a dynamic link library (DLL) of function calls depending on whether -the macro identifier 'DLL' is defined or not. - -See EPANET2.H for function prototypes of exported DLL functions -See FUNCS.H for prototypes of all other functions -See TYPES.H for declaration of global constants and data structures -See VARS.H for declaration of global variables -See TEXT.H for declaration of all string constants -See ENUMSTXT.H for assignment of string constants to enumerated types - -The following naming conventions are used in all modules of this program: -1. Names of exportable functions in the DLL begin with the "EN" prefix. -2. All other function names are lowercase. -3. Global variable names begin with an uppercase letter. -4. Local variable names are all lowercase. -5. Declared constants and enumerated values defined in TYPES.H are - all uppercase. -6. String constants defined in TEXT.H begin with a lower case character - followed by an underscore and then all uppercase characters (e.g. - t_HEADLOSS) - --------------------------------------------------------------------------- - -This is the main module of the EPANET program. It uses a series of -functions, all beginning with the letters EN, to control program behavior. -See the main() and ENepanet() functions below for the simplest example of -these. - -This module calls the following functions that reside in other modules: - RULES.C - initrules() - allocrules() - closerules() - INPUT1.C - getdata() - initreport() - INPUT2.C - netsize() - setreport() - HYDRAUL.C - openhyd() - inithyd() - runhyd() - nexthyd() - closehyd() - resistance() - tankvolume() - getenergy() - setlinkstatus() - setlinksetting() - QUALITY.C - openqual() - initqual() - runqual() - nextqual() - stepqual() - closequal() - REPORT.C - writeline() - writelogo() - writereport() - HASH.C - ENHashTablecreate() - ENHashTableFind() - ENHashTableFree() - -The macro ERRCODE(x) is defined in TYPES.H. It says if the current -value of the error code variable (errcode) is not fatal (< 100) then -execute function x and set the error code equal to its return value. - -******************************************************************************* -*/ - -/*** Need to define WINDOWS to use the getTmpName function ***/ //(2.00.12 - LR) -// --- define WINDOWS -#undef WINDOWS -#ifdef _WIN32 - #define WINDOWS -#endif -#ifdef __WIN32__ - #define WINDOWS -#endif -/************************************************************/ - -#include -#include -#include -#ifndef __APPLE__ -#include -#endif -#include -#include //(2.00.12 - LR) - -#include "text.h" -#include "types.h" -#include "enumstxt.h" -#include "funcs.h" -#define EXTERN -#include "vars.h" -#include "epanet2.h" - -void (* viewprog) (char *); /* Pointer to progress viewing function */ - - -/* ----------------------------------------------------------------- - Entry point used to compile a Windows DLL ----------------------------------------------------------------- -*/ - - -/* ----------------------------------------------------------------- - Functions for opening & closing the EPANET system ----------------------------------------------------------------- -*/ - - -/*** updated 3/1/01 ***/ -int DLLEXPORT ENepanet(char *f1, char *f2, char *f3, void (*pviewprog) (char *)) - -/*------------------------------------------------------------------------ -** Input: f1 = pointer to name of input file -** f2 = pointer to name of report file -** f3 = pointer to name of binary output file -** pviewprog = see note below -** Output: none -** Returns: error code -** Purpose: runs a complete EPANET simulation -** -** The pviewprog() argument is a pointer to a callback function -** that takes a character string (char *) as its only parameter. -** The function would reside in and be used by the calling -** program to display the progress messages that EPANET generates -** as it carries out its computations. If this feature is not -** needed then the argument should be NULL. -**------------------------------------------------------------------------- -*/ -{ - int errcode = 0; - viewprog = pviewprog; - ERRCODE(ENopen(f1,f2,f3)); - if (Hydflag != USE) ERRCODE(ENsolveH()); - ERRCODE(ENsolveQ()); - ERRCODE(ENreport()); - ENclose(); - return(MAX(errcode, Warnflag) ); -} - - -int DLLEXPORT ENopen(char *f1, char *f2, char *f3) -/*---------------------------------------------------------------- -** Input: f1 = pointer to name of input file -** f2 = pointer to name of report file -** f3 = pointer to name of binary output file -** Output: none -** Returns: error code -** Purpose: opens EPANET input file & reads in network data -**---------------------------------------------------------------- -*/ -{ - int errcode = 0; - -/*** Updated 9/7/00 ***/ -/* Reset math coprocessor */ -#ifdef DLL - _fpreset(); -#endif - -/* Set system flags */ - Openflag = FALSE; - OpenHflag = FALSE; - OpenQflag = FALSE; - SaveHflag = FALSE; - SaveQflag = FALSE; - Warnflag = FALSE; - Coordflag = TRUE; - -/*** Updated 9/7/00 ***/ - Messageflag = TRUE; - Rptflag = 1; - -/* Initialize global pointers to NULL. */ - initpointers(); - -/* Open input & report files */ - ERRCODE(openfiles(f1,f2,f3)); - if (errcode > 0) - { - errmsg(errcode); - return(errcode); - } - writelogo(); - -/* Find network size & allocate memory for data */ - writecon(FMT02); - writewin(FMT100); - ERRCODE(netsize()); - ERRCODE(allocdata()); - -/* Retrieve input data */ - ERRCODE(getdata()); - -/* Free temporary linked lists used for Patterns & Curves */ - freeTmplist(Patlist); - freeTmplist(Curvelist); - -/* If using previously saved hydraulics then open its file */ - if (Hydflag == USE) ERRCODE(openhydfile()); - -/* Write input summary to report file */ - if (!errcode) - { - if (Summaryflag) writesummary(); - writetime(FMT104); - Openflag = TRUE; - } - else errmsg(errcode); - return(errcode); -} - - -int DLLEXPORT ENsaveinpfile(char *filename) -/*---------------------------------------------------------------- -** Input: filename = name of INP file -** Output: none -** Returns: error code -** Purpose: saves current data base to file -**---------------------------------------------------------------- -*/ -{ - if (!Openflag) return(102); - return(saveinpfile(filename)); -} - - -int DLLEXPORT ENclose() -/*---------------------------------------------------------------- -** Input: none -** Output: none -** Returns: error code -** Purpose: frees all memory & files used by EPANET -**---------------------------------------------------------------- -*/ -{ - if (Openflag) writetime(FMT105); - freedata(); - - if (TmpOutFile != OutFile) //(2.00.12 - LR) - { //(2.00.12 - LR) - if (TmpOutFile != NULL) fclose(TmpOutFile); //(2.00.12 - LR) - remove(TmpFname); //(2.00.12 - LR) - } //(2.00.12 - LR) - TmpOutFile=NULL; - - if (InFile != NULL) { fclose(InFile); InFile=NULL; } - if (RptFile != NULL && RptFile != stdout) { fclose(RptFile); RptFile=NULL; } - if (HydFile != NULL) { fclose(HydFile); HydFile=NULL; } - if (OutFile != NULL) { fclose(OutFile); OutFile=NULL; } - - if (Hydflag == SCRATCH) remove(HydFname); //(2.00.12 - LR) - if (Outflag == SCRATCH) remove(OutFname); //(2.00.12 - LR) - - Openflag = FALSE; - OpenHflag = FALSE; - SaveHflag = FALSE; - OpenQflag = FALSE; - SaveQflag = FALSE; - return(0); -} - - -/* ----------------------------------------------------------------- - Functions for running a hydraulic analysis ----------------------------------------------------------------- -*/ - - -int DLLEXPORT ENsolveH() -/*---------------------------------------------------------------- -** Input: none -** Output: none -** Returns: error code -** Purpose: solves for network hydraulics in all time periods -**---------------------------------------------------------------- -*/ -{ - int errcode; - long t, tstep; - -/* Open hydraulics solver */ - errcode = ENopenH(); - if (!errcode) - { - /* Initialize hydraulics */ - errcode = ENinitH(EN_SAVE); - writecon(FMT14); - - /* Analyze each hydraulic period */ - if (!errcode) do - { - - /* Display progress message */ - -/*** Updated 6/24/02 ***/ - sprintf(Msg,"%-10s",clocktime(Atime,Htime)); - - writecon(Msg); - sprintf(Msg,FMT101,Atime); - writewin(Msg); - - /* Solve for hydraulics & advance to next time period */ - tstep = 0; - ERRCODE(ENrunH(&t)); - ERRCODE(ENnextH(&tstep)); - -/*** Updated 6/24/02 ***/ - writecon("\b\b\b\b\b\b\b\b\b\b"); - } - while (tstep > 0); - } - -/* Close hydraulics solver */ - -/*** Updated 6/24/02 ***/ - writecon("\b\b\b\b\b\b\b\b "); - - ENcloseH(); - errcode = MAX(errcode, Warnflag); - return(errcode); -} - - -int DLLEXPORT ENsaveH() -/*---------------------------------------------------------------- -** Input: none -** Output: none -** Returns: error code -** Purpose: saves hydraulic results to binary file. -** -** Must be called before ENreport() if no WQ simulation made. -** Should not be called if ENsolveQ() will be used. -**---------------------------------------------------------------- -*/ -{ - char tmpflag; - int errcode; - -/* Check if hydraulic results exist */ - if (!SaveHflag) return(104); - -/* Temporarily turn off WQ analysis */ - tmpflag = Qualflag; - Qualflag = NONE; - -/* Call WQ solver to simply transfer results */ -/* from Hydraulics file to Output file at */ -/* fixed length reporting time intervals. */ - errcode = ENsolveQ(); - -/* Restore WQ analysis option */ - Qualflag = tmpflag; - if (errcode) errmsg(errcode); - return(errcode); -} - - -int DLLEXPORT ENopenH() -/*---------------------------------------------------------------- -** Input: none -** Output: none -** Returns: error code -** Purpose: sets up data structures for hydraulic analysis -**---------------------------------------------------------------- -*/ -{ - int errcode = 0; - -/* Check that input data exists */ - OpenHflag = FALSE; - SaveHflag = FALSE; - if (!Openflag) return(102); - -/* Check that previously saved hydraulics file not in use */ - if (Hydflag == USE) return(107); - -/* Open hydraulics solver */ - ERRCODE(openhyd()); - if (!errcode) OpenHflag = TRUE; - else errmsg(errcode); - return(errcode); -} - - -/*** Updated 3/1/01 ***/ -int DLLEXPORT ENinitH(int flag) -/*---------------------------------------------------------------- -** Input: flag = 2-digit flag where 1st (left) digit indicates -** if link flows should be re-initialized (1) or -** not (0) and 2nd digit indicates if hydraulic -** results should be saved to file (1) or not (0) -** Output: none -** Returns: error code -** Purpose: initializes hydraulic analysis -**---------------------------------------------------------------- -*/ -{ - int errcode = 0; - int sflag, fflag; - -/* Reset status flags */ - SaveHflag = FALSE; - Warnflag = FALSE; - -/* Get values of save-to-file flag and reinitialize-flows flag */ - fflag = flag/EN_INITFLOW; - sflag = flag - fflag*EN_INITFLOW; - -/* Check that hydraulics solver was opened */ - if (!OpenHflag) return(103); - -/* Open hydraulics file */ - Saveflag = FALSE; - if (sflag > 0) - { - errcode = openhydfile(); - if (!errcode) Saveflag = TRUE; - else errmsg(errcode); - } - -/* Initialize hydraulics */ - inithyd(fflag); - if (Statflag > 0) writeheader(STATHDR,0); - return(errcode); -} - - -int DLLEXPORT ENrunH(long *t) -{ - int errcode; - *t = 0; - if (!OpenHflag) return(103); - errcode = runhyd(t); - if (errcode) errmsg(errcode); - return(errcode); -} - - -int DLLEXPORT ENnextH(long *tstep) -{ - int errcode; - *tstep = 0; - if (!OpenHflag) return(103); - errcode = nexthyd(tstep); - if (errcode) errmsg(errcode); - else if (Saveflag && *tstep == 0) SaveHflag = TRUE; - return(errcode); -} - - -int DLLEXPORT ENcloseH() - -{ - if (!Openflag) return(102); - closehyd(); - OpenHflag = FALSE; - return(0); -} - - -int DLLEXPORT ENsavehydfile(char *filename) -{ - FILE *f; - int c; - -/* Check that hydraulics results exist */ - if (HydFile == NULL || !SaveHflag) return(104); - -/* Open file */ - if ( (f = fopen(filename,"w+b")) == NULL) return(305); - -/* Copy from HydFile to f */ - fseek(HydFile, 0, SEEK_SET); - while ( (c = fgetc(HydFile)) != EOF) fputc(c, f); - fclose(f); - return(0); -} - - -int DLLEXPORT ENusehydfile(char *filename) -{ - int errcode; - -/* Check that input data exists & hydraulics system closed */ - if (!Openflag) return(102); - if (OpenHflag) return(108); - -/* Try to open hydraulics file */ - strncpy(HydFname, filename, MAXFNAME); - Hydflag = USE; - SaveHflag = TRUE; - errcode = openhydfile(); - -/* If error, then reset flags */ - if (errcode) - { - strcpy(HydFname, ""); - Hydflag = SCRATCH; - SaveHflag = FALSE; - } - return(errcode); -} - - -/* ----------------------------------------------------------------- - Functions for running a WQ analysis ----------------------------------------------------------------- -*/ - - -int DLLEXPORT ENsolveQ() -{ - int errcode; - long t, tstep; - -/* Open WQ solver */ - errcode = ENopenQ(); - if (!errcode) - { - /* Initialize WQ */ - errcode = ENinitQ(EN_SAVE); - if (Qualflag) writecon(FMT15); - else - { - writecon(FMT16); - writewin(FMT103); - } - - /* Analyze each hydraulic period */ - if (!errcode) do - { - - /* Display progress message */ - -/*** Updated 6/24/02 ***/ - sprintf(Msg,"%-10s",clocktime(Atime,Htime)); - - writecon(Msg); - if (Qualflag) - { - sprintf(Msg,FMT102,Atime); - writewin(Msg); - } - - /* Retrieve current network solution & update WQ to next time period */ - tstep = 0; - ERRCODE(ENrunQ(&t)); - ERRCODE(ENnextQ(&tstep)); - -/*** Updated 6/24/02 ***/ - writecon("\b\b\b\b\b\b\b\b\b\b"); - - } while (tstep > 0); - - } - -/* Close WQ solver */ - -/*** Updated 6/24/02 ***/ - writecon("\b\b\b\b\b\b\b\b "); - ENcloseQ(); - return(errcode); -} - - -int DLLEXPORT ENopenQ() -{ - int errcode = 0; - -/* Check that hydraulics results exist */ - OpenQflag = FALSE; - SaveQflag = FALSE; - if (!Openflag) return(102); - // !LT! todo - check for SaveHflag / set sequential/step mode - //if (!SaveHflag) return(104); - -/* Open WQ solver */ - ERRCODE(openqual()); - if (!errcode) OpenQflag = TRUE; - else errmsg(errcode); - return(errcode); -} - - -int DLLEXPORT ENinitQ(int saveflag) -{ - int errcode = 0; - if (!OpenQflag) return(105); - initqual(); - SaveQflag = FALSE; - Saveflag = FALSE; - if (saveflag) - { - errcode = openoutfile(); - if (!errcode) Saveflag = TRUE; - } - return(errcode); -} - - -int DLLEXPORT ENrunQ(long *t) -{ - int errcode; - *t = 0; - if (!OpenQflag) return(105); - errcode = runqual(t); - if (errcode) errmsg(errcode); - return(errcode); -} - - -int DLLEXPORT ENnextQ(long *tstep) -{ - int errcode; - *tstep = 0; - if (!OpenQflag) return(105); - errcode = nextqual(tstep); - if (!errcode && Saveflag && *tstep == 0) SaveQflag = TRUE; - if (errcode) errmsg(errcode); - return(errcode); -} - - -int DLLEXPORT ENstepQ(long *tleft) -{ - int errcode; - *tleft = 0; - if (!OpenQflag) return(105); - errcode = stepqual(tleft); - if (!errcode && Saveflag && *tleft == 0) SaveQflag = TRUE; - if (errcode) errmsg(errcode); - return(errcode); -} - - -int DLLEXPORT ENcloseQ() - -{ - if (!Openflag) return(102); - closequal(); - OpenQflag = FALSE; - return(0); -} - - -/* ----------------------------------------------------------------- - Functions for generating an output report ----------------------------------------------------------------- -*/ - - -int DLLEXPORT ENwriteline(char *line) -{ - if (!Openflag) return(102); - writeline(line); - return(0); -} - - -int DLLEXPORT ENreport() -{ - int errcode; - -/* Check if results saved to binary output file */ - if (!SaveQflag) return(106); - errcode = writereport(); - if (errcode) errmsg(errcode); - return(errcode); -} - - -int DLLEXPORT ENresetreport() -{ - int i; - if (!Openflag) return(102); - initreport(); - for (i=1; i<=Nnodes; i++) Node[i].Rpt = 0; - for (i=1; i<=Nlinks; i++) Link[i].Rpt = 0; - return(0); -} - - -int DLLEXPORT ENsetreport(char *s) -{ - char s1[MAXLINE+1]; - if (!Openflag) return(102); - if (strlen(s) > MAXLINE) return(250); - strcpy(s1,s); - if (setreport(s1) > 0) return(250); - else return(0); -} - - -/* ----------------------------------------------------------------- - Functions for retrieving network information ----------------------------------------------------------------- -*/ - -/*** Updated 10/25/00 ***/ -int DLLEXPORT ENgetversion(int *v) -/*---------------------------------------------------------------- -** Input: none -** Output: *v = version number of the source code -** Returns: error code (should always be 0) -** Purpose: retrieves a number assigned to the most recent -** update of the source code. This number, set by the -** constant CODEVERSION found in TYPES.H, is to be -** interpreted with implied decimals, i.e., "20100" == "2(.)01(.)00" -**---------------------------------------------------------------- -*/ -{ - *v = CODEVERSION; - return(0); -} - - -int DLLEXPORT ENgetcontrol(int cindex, int *ctype, int *lindex, EN_API_FLOAT_TYPE *setting, int *nindex, EN_API_FLOAT_TYPE *level) -{ - double s, lvl; - - s = 0.0; - lvl = 0.0; - *ctype = 0; - *lindex = 0; - *nindex = 0; - if (!Openflag) return(102); - if (cindex < 1 || cindex > Ncontrols) return(241); - *ctype = Control[cindex].Type; - *lindex = Control[cindex].Link; - s = Control[cindex].Setting; - if (Control[cindex].Setting != MISSING) switch (Link[*lindex].Type) - { - case PRV: - case PSV: - case PBV: s *= Ucf[PRESSURE]; break; - case FCV: s *= Ucf[FLOW]; - } - else if (Control[cindex].Status == OPEN) s = 1.0; - -/*** Updated 3/1/01 ***/ - else s = 0.0; - - *nindex = Control[cindex].Node; - if (*nindex > Njuncs) - lvl = (Control[cindex].Grade - Node[*nindex].El)*Ucf[ELEV]; - else if (*nindex > 0) - lvl = (Control[cindex].Grade - Node[*nindex].El)*Ucf[PRESSURE]; - else - lvl = (EN_API_FLOAT_TYPE)Control[cindex].Time; - *setting = (EN_API_FLOAT_TYPE)s; - *level = (EN_API_FLOAT_TYPE)lvl; - return(0); -} - - -int DLLEXPORT ENgetcount(int code, int *count) -{ - *count = 0; - if (!Openflag) return(102); - switch (code) - { - case EN_NODECOUNT: *count = Nnodes; break; - case EN_TANKCOUNT: *count = Ntanks; break; - case EN_LINKCOUNT: *count = Nlinks; break; - case EN_PATCOUNT: *count = Npats; break; - case EN_CURVECOUNT: *count = Ncurves; break; - case EN_CONTROLCOUNT: *count = Ncontrols; break; - case EN_RULECOUNT: *count = Nrules; break; - default: return(251); - } - return(0); -} - - -int DLLEXPORT ENgetoption(int code, EN_API_FLOAT_TYPE *value) -{ - double v = 0.0; - *value = 0.0; - if (!Openflag) return(102); - switch (code) - { - case EN_TRIALS: v = (double)MaxIter; - break; - case EN_ACCURACY: v = Hacc; - break; - case EN_TOLERANCE: v = Ctol*Ucf[QUALITY]; - break; - case EN_EMITEXPON: if (Qexp > 0.0) v = 1.0/Qexp; - break; - case EN_DEMANDMULT: v = Dmult; - break; - default: return(251); - } - *value = (EN_API_FLOAT_TYPE)v; - return(0); -} - - -int DLLEXPORT ENgettimeparam(int code, long *value) -{ - *value = 0; - if (!Openflag) return(102); - if (code < EN_DURATION || code > EN_NEXTEVENT) return(251); - switch (code) - { - case EN_DURATION: *value = Dur; break; - case EN_HYDSTEP: *value = Hstep; break; - case EN_QUALSTEP: *value = Qstep; break; - case EN_PATTERNSTEP: *value = Pstep; break; - case EN_PATTERNSTART: *value = Pstart; break; - case EN_REPORTSTEP: *value = Rstep; break; - case EN_REPORTSTART: *value = Rstart; break; - case EN_STATISTIC: *value = Tstatflag; break; - case EN_RULESTEP: *value = Rulestep; break; - case EN_PERIODS: *value = Nperiods; break; - case EN_STARTTIME: *value = Tstart; break; /* Added TNT 10/2/2009 */ - case EN_HTIME: *value = Htime; break; - case EN_NEXTEVENT: - *value = Hstep; // find the lesser of the hydraulic time step length, or the time to next fill/empty - tanktimestep(value); - break; - } - return(0); -} - - -int DLLEXPORT ENgetflowunits(int *code) -{ - *code = -1; - if (!Openflag) return(102); - *code = Flowflag; - return(0); -} - - -int DLLEXPORT ENgetpatternindex(char *id, int *index) -{ - int i; - *index = 0; - if (!Openflag) return(102); - for (i=1; i<=Npats; i++) - { - if (strcmp(id, Pattern[i].ID) == 0) - { - *index = i; - return(0); - } - } - *index = 0; - return(205); -} - - -int DLLEXPORT ENgetpatternid(int index, char *id) -{ - strcpy(id,""); - if (!Openflag) return(102); - if (index < 1 || index > Npats) return(205); - strcpy(id,Pattern[index].ID); - return(0); -} - - -int DLLEXPORT ENgetpatternlen(int index, int *len) -{ - if (!Openflag) return(102); - if (index < 1 || index > Npats) return(205); - *len = Pattern[index].Length; - return(0); -} - - -int DLLEXPORT ENgetpatternvalue(int index, int period, EN_API_FLOAT_TYPE *value) -{ *value = 0.0; - if (!Openflag) return(102); - if (index < 1 || index > Npats) return(205); - if (period < 1 || period > Pattern[index].Length) return(251); - *value = (EN_API_FLOAT_TYPE)Pattern[index].F[period-1]; - return(0); -} - - -int DLLEXPORT ENgetcurveindex(char *id, int *index) -{ - int i; - *index = 0; - if (!Openflag) return(102); - for (i=1; i<=Ncurves; i++) - { - if (strcmp(id, Curve[i].ID) == 0) - { - *index = i; - return(0); - } - } - *index = 0; - return(206); -} - - -int DLLEXPORT ENgetcurveid(int index, char *id) -{ - strcpy(id,""); - if (!Openflag) return(102); - if (index < 1 || index > Ncurves) return(206); - strcpy(id,Curve[index].ID); - return(0); -} - - -int DLLEXPORT ENgetcurvelen(int index, int *len) -{ - if (!Openflag) return(102); - if (index < 1 || index > Ncurves) return(206); - *len = Curve[index].Npts; - return(0); -} - - -int DLLEXPORT ENgetcurvevalue(int index, int pnt, EN_API_FLOAT_TYPE *x, EN_API_FLOAT_TYPE *y) -{ - *x = 0.0; - *y = 0.0; - if (!Openflag) return(102); - if (index < 1 || index > Ncurves) return(206); - if (pnt < 1 || pnt > Curve[index].Npts) return(251); - *x = (EN_API_FLOAT_TYPE)Curve[index].X[pnt-1]; - *y = (EN_API_FLOAT_TYPE)Curve[index].Y[pnt-1]; - return(0); -} - - -int DLLEXPORT ENgetqualtype(int *qualcode, int *tracenode) -{ - *tracenode = 0; - if (!Openflag) return(102); - *qualcode = Qualflag; - if (Qualflag == TRACE) *tracenode = TraceNode; - return(0); -} - -int DLLEXPORT ENgetqualinfo(int *qualcode, char *chemname, char *chemunits, int *tracenode) -{ - ENgetqualtype(qualcode, tracenode); - if (Qualflag == TRACE) { - strncpy(chemname, "", MAXID); - strncpy(chemunits, "dimensionless", MAXID); - } - else { - strncpy(chemname,ChemName,MAXID); - strncpy(chemunits,ChemUnits,MAXID); - } - return 0; -} - -int DLLEXPORT ENgeterror(int errcode, char *errmsg, int n) -{ - switch (errcode) - { - case 1: strncpy(errmsg,WARN1,n); break; - case 2: strncpy(errmsg,WARN2,n); break; - case 3: strncpy(errmsg,WARN3,n); break; - case 4: strncpy(errmsg,WARN4,n); break; - case 5: strncpy(errmsg,WARN5,n); break; - case 6: strncpy(errmsg,WARN6,n); break; - default: strncpy(errmsg,geterrmsg(errcode),n); - } - if (strlen(errmsg) == 0) return(251); - else return(0); -} - -int DLLEXPORT ENgetstatistic(int code, EN_API_FLOAT_TYPE* value) -{ - switch (code) { - case EN_ITERATIONS: - *value = (EN_API_FLOAT_TYPE)_iterations; - break; - case EN_RELATIVEERROR: - *value = (EN_API_FLOAT_TYPE)_relativeError; - break; - default: - break; - } - return 0; -} - -/* ----------------------------------------------------------------- - Functions for retrieving node data ----------------------------------------------------------------- -*/ - - -int DLLEXPORT ENgetnodeindex(char *id, int *index) -{ - *index = 0; - if (!Openflag) return(102); - *index = findnode(id); - if (*index == 0) return(203); - else return(0); -} - - -int DLLEXPORT ENgetnodeid(int index, char *id) -{ - strcpy(id,""); - if (!Openflag) return(102); - if (index < 1 || index > Nnodes) return(203); - strcpy(id,Node[index].ID); - return(0); -} - - -int DLLEXPORT ENgetnodetype(int index, int *code) -{ - *code = -1; - if (!Openflag) return(102); - if (index < 1 || index > Nnodes) return(203); - if (index <= Njuncs) *code = EN_JUNCTION; - else - { - if (Tank[index-Njuncs].A == 0.0) *code = EN_RESERVOIR; - else *code = EN_TANK; - } - return(0); -} - - -int DLLEXPORT ENgetcoord(int index, EN_API_FLOAT_TYPE *x, EN_API_FLOAT_TYPE *y) -{ - if (!Openflag) return(102); - if (index < 1 || index > Nnodes) return(203); - if (!Coordflag) return(255); - - // check if node have coords - if (Coord[index].HaveCoords == FALSE) return(254); - - *x = Coord[index].X; - *y = Coord[index].Y; - return 0; -} - - -int DLLEXPORT ENsetcoord(int index, EN_API_FLOAT_TYPE x, EN_API_FLOAT_TYPE y) -{ - if (!Openflag) return(102); - if (index < 1 || index > Nnodes) return(203); - if (!Coordflag) return(255); - - Coord[index].X = x; - Coord[index].Y = y; - Coord[index].HaveCoords = TRUE; - return 0; -} - - -int DLLEXPORT ENgetnodevalue(int index, int code, EN_API_FLOAT_TYPE *value) -{ - double v = 0.0; - Pdemand demand; - Psource source; - -/* Check for valid arguments */ - *value = 0.0; - if (!Openflag) return(102); - if (index <= 0 || index > Nnodes) return(203); - -/* Retrieve called-for parameter */ - switch (code) - { - case EN_ELEVATION: - v = Node[index].El*Ucf[ELEV]; - break; - - case EN_BASEDEMAND: - v = 0.0; - /* NOTE: primary demand category is last on demand list */ - if (index <= Njuncs) - for (demand = Node[index].D; demand != NULL; demand = demand->next) - v = (demand->Base); - v *= Ucf[FLOW]; - break; - - case EN_PATTERN: - v = 0.0; - /* NOTE: primary demand category is last on demand list */ - if (index <= Njuncs) - { - for (demand = Node[index].D; demand != NULL; demand = demand->next) - v = (double)(demand->Pat); - } - else v = (double)(Tank[index-Njuncs].Pat); - break; - - case EN_EMITTER: - v = 0.0; - if (Node[index].Ke > 0.0) - v = Ucf[FLOW]/pow((Ucf[PRESSURE]*Node[index].Ke),(1.0/Qexp)); - break; - - case EN_INITQUAL: - v = Node[index].C0*Ucf[QUALITY]; - break; - -/*** Additional parameters added for retrieval ***/ //(2.00.11 - LR) - case EN_SOURCEQUAL: - case EN_SOURCETYPE: - case EN_SOURCEMASS: - case EN_SOURCEPAT: - source = Node[index].S; - if (source == NULL) return(240); - if (code == EN_SOURCEQUAL) v = source->C0; - else if (code == EN_SOURCEMASS) v = source->Smass*60.0; - else if (code == EN_SOURCEPAT) v = source->Pat; - else v = source->Type; - break; - - case EN_TANKLEVEL: - if (index <= Njuncs) return(251); - v = (Tank[index-Njuncs].H0 - Node[index].El)*Ucf[ELEV]; - break; - -/*** New parameter added for retrieval ***/ //(2.00.11 - LR) - case EN_INITVOLUME: //(2.00.11 - LR) - v = 0.0; //(2.00.11 - LR) - if ( index > Njuncs ) v = Tank[index-Njuncs].V0*Ucf[VOLUME]; //(2.00.11 - LR) - break; //(2.00.11 - LR) - -/*** New parameter added for retrieval ***/ //(2.00.11 - LR) - case EN_MIXMODEL: //(2.00.11 - LR) - v = MIX1; //(2.00.11 - LR) - if ( index > Njuncs ) v = Tank[index-Njuncs].MixModel; //(2.00.11 - LR) - break; //(2.00.11 - LR) - -/*** New parameter added for retrieval ***/ //(2.00.11 - LR) - case EN_MIXZONEVOL: //(2.00.11 - LR) - v = 0.0; //(2.00.11 - LR) - if ( index > Njuncs ) v = Tank[index-Njuncs].V1max*Ucf[VOLUME]; //(2.00.11 - LR) - break; //(2.00.11 - LR) - - case EN_DEMAND: - v = NodeDemand[index]*Ucf[FLOW]; - break; - - case EN_HEAD: - v = NodeHead[index]*Ucf[HEAD]; - break; - - case EN_PRESSURE: - v = (NodeHead[index] - Node[index].El)*Ucf[PRESSURE]; - break; - - case EN_QUALITY: - v = NodeQual[index]*Ucf[QUALITY]; - break; - -/*** New parameters added for retrieval begins here ***/ //(2.00.12 - LR) -/*** (Thanks to Nicolas Basile of Ecole Polytechnique ***/ -/*** de Montreal for suggesting some of these.) ***/ - - case EN_TANKDIAM: - v = 0.0; - if ( index > Njuncs ) - { - v = sqrt(4.0/PI*Tank[index-Njuncs].A)*Ucf[ELEV]; - } - break; - - case EN_MINVOLUME: - v = 0.0; - if ( index > Njuncs ) v = Tank[index-Njuncs].Vmin * Ucf[VOLUME]; - break; - - case EN_MAXVOLUME: // !sph - v = 0.0; - if ( index > Njuncs ) v = Tank[index-Njuncs].Vmax * Ucf[VOLUME]; - break; - - case EN_VOLCURVE: - v = 0.0; - if ( index > Njuncs ) v = Tank[index-Njuncs].Vcurve; - break; - - case EN_MINLEVEL: - v = 0.0; - if ( index > Njuncs ) - { - v = (Tank[index-Njuncs].Hmin - Node[index].El) * Ucf[ELEV]; - } - break; - - case EN_MAXLEVEL: - v = 0.0; - if ( index > Njuncs ) - { - v = (Tank[index-Njuncs].Hmax - Node[index].El) * Ucf[ELEV]; - } - break; - - case EN_MIXFRACTION: - v = 1.0; - if ( index > Njuncs && Tank[index-Njuncs].Vmax > 0.0) - { - v = Tank[index-Njuncs].V1max / Tank[index-Njuncs].Vmax; - } - break; - - case EN_TANK_KBULK: - v = 0.0; - if (index > Njuncs) v = Tank[index-Njuncs].Kb * SECperDAY; - break; - -/*** New parameter additions ends here. ***/ //(2.00.12 - LR) - - case EN_TANKVOLUME: - if (index <= Njuncs) return(251); - v = tankvolume(index-Njuncs, NodeHead[index])*Ucf[VOLUME]; - break; - - default: return(251); - } - *value = (EN_API_FLOAT_TYPE)v; - return(0); -} - - -/* ----------------------------------------------------------------- - Functions for retrieving link data ----------------------------------------------------------------- -*/ - - -int DLLEXPORT ENgetlinkindex(char *id, int *index) -{ - *index = 0; - if (!Openflag) return(102); - *index = findlink(id); - if (*index == 0) return(204); - else return(0); -} - - -int DLLEXPORT ENgetlinkid(int index, char *id) -{ - strcpy(id,""); - if (!Openflag) return(102); - if (index < 1 || index > Nlinks) return(204); - strcpy(id,Link[index].ID); - return(0); -} - - -int DLLEXPORT ENgetlinktype(int index, int *code) -{ - *code = -1; - if (!Openflag) return(102); - if (index < 1 || index > Nlinks) return(204); - *code = Link[index].Type; - return(0); -} - - -int DLLEXPORT ENgetlinknodes(int index, int *node1, int *node2) -{ - *node1 = 0; - *node2 = 0; - if (!Openflag) return(102); - if (index < 1 || index > Nlinks) return(204); - *node1 = Link[index].N1; - *node2 = Link[index].N2; - return(0); -} - - -int DLLEXPORT ENgetlinkvalue(int index, int code, EN_API_FLOAT_TYPE *value) -{ - double a,h,q, v = 0.0; - -/* Check for valid arguments */ - *value = 0.0; - if (!Openflag) return(102); - if (index <= 0 || index > Nlinks) return(204); - -/* Retrieve called-for parameter */ - switch (code) - { - case EN_DIAMETER: - if (Link[index].Type == PUMP) v = 0.0; - else v = Link[index].Diam*Ucf[DIAM]; - break; - - case EN_LENGTH: - v = Link[index].Len*Ucf[ELEV]; - break; - - case EN_ROUGHNESS: - if (Link[index].Type <= PIPE) - { - if (Formflag == DW) - v = Link[index].Kc*(1000.0*Ucf[ELEV]); - else v = Link[index].Kc; - } - else v = 0.0; - break; - - case EN_MINORLOSS: - if (Link[index].Type != PUMP) - { - v = Link[index].Km; - v *= (SQR(Link[index].Diam)*SQR(Link[index].Diam)/0.02517); - } - else v = 0.0; - break; - - case EN_INITSTATUS: - if (Link[index].Stat <= CLOSED) v = 0.0; - else v = 1.0; - break; - - case EN_INITSETTING: - if (Link[index].Type == PIPE || Link[index].Type == CV) - return(ENgetlinkvalue(index, EN_ROUGHNESS, value)); - v = Link[index].Kc; - switch (Link[index].Type) - { - case PRV: - case PSV: - case PBV: v *= Ucf[PRESSURE]; break; - case FCV: v *= Ucf[FLOW]; - } - break; - - case EN_KBULK: - v = Link[index].Kb*SECperDAY; - break; - - case EN_KWALL: - v = Link[index].Kw*SECperDAY; - break; - - case EN_FLOW: - -/*** Updated 10/25/00 ***/ - if (LinkStatus[index] <= CLOSED) v = 0.0; - else v = Q[index]*Ucf[FLOW]; - break; - - case EN_VELOCITY: - if (Link[index].Type == PUMP) v = 0.0; - -/*** Updated 11/19/01 ***/ - else if (LinkStatus[index] <= CLOSED) v = 0.0; - - else - { - q = ABS(Q[index]); - a = PI*SQR(Link[index].Diam)/4.0; - v = q/a*Ucf[VELOCITY]; - } - break; - - case EN_HEADLOSS: - -/*** Updated 11/19/01 ***/ - if (LinkStatus[index] <= CLOSED) v = 0.0; - - else - { - h = NodeHead[Link[index].N1] - NodeHead[Link[index].N2]; - if (Link[index].Type != PUMP) h = ABS(h); - v = h*Ucf[HEADLOSS]; - } - break; - - case EN_STATUS: - if (LinkStatus[index] <= CLOSED) v = 0.0; - else v = 1.0; - break; - - case EN_SETTING: - if (Link[index].Type == PIPE || Link[index].Type == CV) { - return(ENgetlinkvalue(index, EN_ROUGHNESS, value)); - } - if (LinkSetting[index] == MISSING) { - v = 0.0; - } - else { - v = LinkSetting[index]; - } - switch (Link[index].Type) - { - case PRV: - case PSV: - case PBV: v *= Ucf[PRESSURE]; break; - case FCV: v *= Ucf[FLOW]; - } - break; - - case EN_ENERGY: - getenergy(index, &v, &a); - break; - - case EN_LINKQUAL: - v = avgqual(index) * Ucf[LINKQUAL]; - break; - - case EN_LINKPATTERN: - if (Link[index].Type == PUMP) - v = (double)Pump[PUMPINDEX(index)].Upat; - break; - - case EN_PRICEPATTERN: - if (Link[index].Type == PUMP) - v = (double)Pump[PUMPINDEX(index)].Epat; - break; - - case EN_EFFICIENCY: - getenergy(index, &a, &v); - break; - - default: return(251); - } - *value = (EN_API_FLOAT_TYPE)v; - return(0); -} - - -int DLLEXPORT ENgetcurve(int curveIndex, char *id, int *nValues, EN_API_FLOAT_TYPE **xValues, EN_API_FLOAT_TYPE **yValues) -{ - int iPoint, nPoints; - Scurve curve; - EN_API_FLOAT_TYPE *pointX, *pointY; - -/* Check that input file opened */ - if (!Openflag) return(102); - - curve = Curve[curveIndex]; - nPoints = curve.Npts; - - pointX = calloc(nPoints, sizeof(EN_API_FLOAT_TYPE)); - pointY = calloc(nPoints, sizeof(EN_API_FLOAT_TYPE)); - - for (iPoint = 0; iPoint < nPoints; iPoint++) { - double x = curve.X[iPoint] * Ucf[LENGTH]; - double y = curve.Y[iPoint] * Ucf[VOLUME]; - pointX[iPoint] = (EN_API_FLOAT_TYPE)x; - pointY[iPoint] = (EN_API_FLOAT_TYPE)y; - } - - strncpy(id,"", MAXID); - strncpy(id, curve.ID, MAXID); - *nValues = nPoints; - *xValues = pointX; - *yValues = pointY; - - return(0); -} - - -/* ----------------------------------------------------------------- - Functions for changing network data ----------------------------------------------------------------- -*/ - - -int DLLEXPORT ENsetcontrol(int cindex, int ctype, int lindex, - EN_API_FLOAT_TYPE setting, int nindex, EN_API_FLOAT_TYPE level) -{ - char status = ACTIVE; - long t = 0; - double s = setting, lvl = level; - -/* Check that input file opened */ - if (!Openflag) return(102); - -/* Check that control exists */ - if (cindex < 1 || cindex > Ncontrols) return(241); - -/* Check that controlled link exists */ - if (lindex == 0) - { - Control[cindex].Link = 0; - return(0); - } - if (lindex < 0 || lindex > Nlinks) return(204); - -/* Cannot control check valve. */ - if (Link[lindex].Type == CV) return(207); - -/* Check for valid parameters */ - if (ctype < 0 || ctype > EN_TIMEOFDAY) return(251); - if (ctype == EN_LOWLEVEL || ctype == EN_HILEVEL) - { - if (nindex < 1 || nindex > Nnodes) return(203); - } - else nindex = 0; - if (s < 0.0 || lvl < 0.0) return(202); - -/* Adjust units of control parameters */ - switch (Link[lindex].Type) - { - case PRV: - case PSV: - case PBV: s /= Ucf[PRESSURE]; - break; - case FCV: s /= Ucf[FLOW]; - break; - -/*** Updated 9/7/00 ***/ - case GPV: if (s == 0.0) status = CLOSED; - else if (s == 1.0) status = OPEN; - else return(202); - s = Link[lindex].Kc; - break; - - case PIPE: - case PUMP: status = OPEN; - if (s == 0.0) status = CLOSED; - } - if (ctype == LOWLEVEL || ctype == HILEVEL) - { - if (nindex > Njuncs) lvl = Node[nindex].El + level/Ucf[ELEV]; - else lvl = Node[nindex].El + level/Ucf[PRESSURE]; - } - if (ctype == TIMER) t = (long)ROUND(lvl); - if (ctype == TIMEOFDAY) t = (long)ROUND(lvl) % SECperDAY; - -/* Reset control's parameters */ - Control[cindex].Type = (char)ctype; - Control[cindex].Link = lindex; - Control[cindex].Node = nindex; - Control[cindex].Status = status; - Control[cindex].Setting = s; - Control[cindex].Grade = lvl; - Control[cindex].Time = t; - return(0); -} - - -int DLLEXPORT ENsetnodevalue(int index, int code, EN_API_FLOAT_TYPE v) -/*---------------------------------------------------------------- -** Input: index = node index -** code = node parameter code (see EPANET2.H) -** value = parameter value -** Output: none -** Returns: error code -** Purpose: sets input parameter value for a node -**---------------------------------------------------------------- -*/ -{ - int j; - Pdemand demand; - Psource source; - double Htmp; - double value = v; - - if (!Openflag) return(102); - if (index <= 0 || index > Nnodes) return(203); - switch (code) - { - case EN_ELEVATION: - if (index <= Njuncs) Node[index].El = value/Ucf[ELEV]; - else - { - value = (value/Ucf[ELEV]) - Node[index].El; - j = index - Njuncs; - Tank[j].H0 += value; - Tank[j].Hmin += value; - Tank[j].Hmax += value; - Node[index].El += value; - NodeHead[index] += value; - } - break; - - case EN_BASEDEMAND: - /* NOTE: primary demand category is last on demand list */ - if (index <= Njuncs) - { - for (demand = Node[index].D; demand != NULL; demand = demand ->next) - { - if (demand->next == NULL) demand->Base = value/Ucf[FLOW]; - } - } - break; - - case EN_PATTERN: - /* NOTE: primary demand category is last on demand list */ - j = ROUND(value); - if (j < 0 || j > Npats) return(205); - if (index <= Njuncs) - { - for (demand = Node[index].D; demand != NULL; demand = demand ->next) - { - if (demand->next == NULL) demand->Pat = j; - } - } - else Tank[index-Njuncs].Pat = j; - break; - - case EN_EMITTER: - if (index > Njuncs) return(203); - if (value < 0.0) return(202); - if (value > 0.0) - value = pow((Ucf[FLOW]/value),Qexp)/Ucf[PRESSURE]; - Node[index].Ke = value; - break; - - case EN_INITQUAL: - if (value < 0.0) return(202); - Node[index].C0 = value/Ucf[QUALITY]; - if (index > Njuncs) Tank[index-Njuncs].C = Node[index].C0; - break; - - case EN_SOURCEQUAL: - case EN_SOURCETYPE: - case EN_SOURCEPAT: - if (value < 0.0) return(202); - source = Node[index].S; - if (source == NULL) - { - source = (struct Ssource *) malloc(sizeof(struct Ssource)); - if (source == NULL) return(101); - source->Type = CONCEN; - source->C0 = 0.0; - source->Pat = 0; - Node[index].S = source; - } - if (code == EN_SOURCEQUAL) { - source->C0 = value; - } - else if (code == EN_SOURCEPAT) - { - j = ROUND(value); - if (j < 0 || j > Npats) return(205); - source->Pat = j; - } - else // code == EN_SOURCETYPE - { - j = ROUND(value); - if ( j < CONCEN || j > FLOWPACED) return(251); - else source->Type = (char)j; - } - return(0); - - case EN_TANKLEVEL: - if (index <= Njuncs) return(251); - j = index - Njuncs; - if (Tank[j].A == 0.0) /* Tank is a reservoir */ - { - Tank[j].H0 = value/Ucf[ELEV]; - Tank[j].Hmin = Tank[j].H0; - Tank[j].Hmax = Tank[j].H0; - Node[index].El = Tank[j].H0; - NodeHead[index] = Tank[j].H0; - } - else - { - value = Node[index].El + value/Ucf[ELEV]; - if (value > Tank[j].Hmax - || value < Tank[j].Hmin) return(202); - Tank[j].H0 = value; - Tank[j].V0 = tankvolume(j, Tank[j].H0); - // Resetting Volume in addition to initial volume - Tank[j].V = Tank[j].V0; - NodeHead[index] = Tank[j].H0; - } - break; - -/*** New parameters added for retrieval begins here ***/ //(2.00.12 - LR) -/*** (Thanks to Nicolas Basile of Ecole Polytechnique ***/ -/*** de Montreal for suggesting some of these.) ***/ - - case EN_TANKDIAM: - if (value <= 0.0) return(202); - if (index <= Njuncs) return(251); - j = index - Njuncs; - if (j > 0 && Tank[j].A > 0.0) - { - value /= Ucf[ELEV]; - Tank[j].A = PI*SQR(value)/4.0; - Tank[j].Vmin = tankvolume(j, Tank[j].Hmin); - Tank[j].V0 = tankvolume(j, Tank[j].H0); - Tank[j].Vmax = tankvolume(j, Tank[j].Hmax); - } - else - { - return(251); - } - break; - - case EN_MINVOLUME: - if (value < 0.0) return(202); - if (index <= Njuncs) return(251); - j = index - Njuncs; - if (j > 0 && Tank[j].A > 0.0) - { - Tank[j].Vmin = value/Ucf[VOLUME]; - Tank[j].V0 = tankvolume(j, Tank[j].H0); - Tank[j].Vmax = tankvolume(j, Tank[j].Hmax); - } - else - { - return(251); - } - break; - - case EN_MINLEVEL: - if (value < 0.0) return(202); - if (index <= Njuncs) return(251); //not a tank or reservoir - j = index - Njuncs; - if (Tank[j].A == 0.0) return(251); //node is a reservoir - Htmp = value/Ucf[ELEV] + Node[index].El; - if (Htmp < Tank[j].Hmax && Htmp <= Tank[j].H0) - { - if (Tank[j].Vcurve > 0) return(202); - Tank[j].Hmin = Htmp; - Tank[j].Vmin = tankvolume(j, Tank[j].Hmin); - } - else - { - return(251); - } - break; - - case EN_MAXLEVEL: - if (value < 0.0) return(202); - if (index <= Njuncs) return(251); //not a tank or reservoir - j = index - Njuncs; - if (Tank[j].A == 0.0) return(251); //node is a reservoir - Htmp = value/Ucf[ELEV] + Node[index].El; - if (Htmp > Tank[j].Hmin && Htmp >= Tank[j].H0) - { - if (Tank[j].Vcurve > 0) return(202); - Tank[j].Hmax = Htmp; - Tank[j].Vmax = tankvolume(j, Tank[j].Hmax); - } - else - { - return(251); - } - break; - - case EN_MIXMODEL: - j = ROUND(value); - if (index <= Njuncs) return(251); - if (j < MIX1 || j > LIFO) return(202); - if (index > Njuncs && Tank[index-Njuncs].A > 0.0) - { - Tank[index-Njuncs].MixModel = (char)j; - } - else - { - return(251); - } - break; - - case EN_MIXFRACTION: - if (value < 0.0 || value > 1.0) return(202); - if (index <= Njuncs) return(251); - j = index - Njuncs; - if (j > 0 && Tank[j].A > 0.0) - { - Tank[j].V1max = value*Tank[j].Vmax; - } - break; - - case EN_TANK_KBULK: - if (index <= Njuncs) return(251); - j = index - Njuncs; - if (j > 0 && Tank[j].A > 0.0) - { - Tank[j].Kb = value/SECperDAY; - Reactflag = 1; - } - else - { - return(251); - } - break; - -/*** New parameter additions ends here. ***/ //(2.00.12 - LR) - - default: return(251); - } - return(0); -} - - -int DLLEXPORT ENsetlinkvalue(int index, int code, EN_API_FLOAT_TYPE v) -/*---------------------------------------------------------------- -** Input: index = link index -** code = link parameter code (see EPANET2.H) -** v = parameter value -** Output: none -** Returns: error code -** Purpose: sets input parameter value for a link -**---------------------------------------------------------------- -*/ -{ - char s; - double r, value = v; - - if (!Openflag) return(102); - if (index <= 0 || index > Nlinks) return(204); - switch (code) - { - case EN_DIAMETER: - if (Link[index].Type != PUMP) - { - if (value <= 0.0) return(202); - value /= Ucf[DIAM]; /* Convert to feet */ - r = Link[index].Diam/value; /* Ratio of old to new diam */ - Link[index].Km *= SQR(r)*SQR(r); /* Adjust minor loss factor */ - Link[index].Diam = value; /* Update diameter */ - resistance(index); /* Update resistance factor */ - } - break; - - case EN_LENGTH: - if (Link[index].Type <= PIPE) - { - if (value <= 0.0) return(202); - Link[index].Len = value/Ucf[ELEV]; - resistance(index); - } - break; - - case EN_ROUGHNESS: - if (Link[index].Type <= PIPE) - { - if (value <= 0.0) return(202); - Link[index].Kc = value; - if (Formflag == DW) Link[index].Kc /= (1000.0*Ucf[ELEV]); - resistance(index); - } - break; - - case EN_MINORLOSS: - if (Link[index].Type != PUMP) - { - if (value <= 0.0) return(202); - Link[index].Km = 0.02517*value/SQR(Link[index].Diam)/SQR(Link[index].Diam); - } - break; - - case EN_INITSTATUS: - case EN_STATUS: - /* Cannot set status for a check valve */ - if (Link[index].Type == CV) return(207); - s = (char)ROUND(value); - if (s < 0 || s > 1) return(251); - if (code == EN_INITSTATUS) - setlinkstatus(index, s, &Link[index].Stat, &Link[index].Kc); - else - setlinkstatus(index, s, &LinkStatus[index], &LinkSetting[index]); - break; - - case EN_INITSETTING: - case EN_SETTING: - if (value < 0.0) return(202); - if (Link[index].Type == PIPE || Link[index].Type == CV) - return(ENsetlinkvalue(index, EN_ROUGHNESS, v)); - else - { - switch (Link[index].Type) - { - case PUMP: break; - case PRV: - case PSV: - case PBV: value /= Ucf[PRESSURE]; break; - case FCV: value /= Ucf[FLOW]; break; - case TCV: break; - -/*** Updated 9/7/00 ***/ - case GPV: return(202); /* Cannot modify setting for GPV */ - - default: return(251); - } - if (code == EN_INITSETTING) - setlinksetting(index, value, &Link[index].Stat, &Link[index].Kc); - else - setlinksetting(index, value, &LinkStatus[index], &LinkSetting[index]); - } - break; - - case EN_KBULK: - if (Link[index].Type <= PIPE) - { - Link[index].Kb = value/SECperDAY; - Reactflag = 1; //(2.00.12 - LR) - } - break; - - case EN_KWALL: - if (Link[index].Type <= PIPE) - { - Link[index].Kw = value/SECperDAY; - Reactflag = 1; //(2.00.12 - LR) - } - break; - - default: return(251); - } - return(0); -} - - -int DLLEXPORT ENaddpattern(char *id) -{ - int i, j, n, err = 0; - Spattern *tmpPat; - -/* Check if a pattern with same id already exists */ - - if ( !Openflag ) return(102); - if ( ENgetpatternindex(id, &i) == 0 ) return(215); - -/* Check that id name is not too long */ - - if (strlen(id) > MAXID) return(250); - -/* Allocate memory for a new array of patterns */ - - n = Npats + 1; - tmpPat = (Spattern *) calloc(n+1, sizeof(Spattern)); - if ( tmpPat == NULL ) return(101); - -/* Copy contents of old pattern array to new one */ - - for (i=0; i<=Npats; i++) - { - strcpy(tmpPat[i].ID, Pattern[i].ID); - tmpPat[i].Length = Pattern[i].Length; - tmpPat[i].F = (double *) calloc(Pattern[i].Length, sizeof(double)); - if (tmpPat[i].F == NULL) err = 1; - else for (j=0; j Npats) return(205); - if (n <= 0) return(202); - -/* Re-set number of time periods & reallocate memory for multipliers */ - Pattern[index].Length = n; - Pattern[index].F = (double *) realloc(Pattern[index].F, n*sizeof(double)); - if (Pattern[index].F == NULL) return(101); - -/* Load multipliers into pattern */ - for (j=0; j Npats) return(205); - if (period <= 0 || period > Pattern[index].Length) return(251); - Pattern[index].F[period-1] = value; - return(0); -} - - -int DLLEXPORT ENaddcurve(char *id) -{ - int i, j, n, err = 0; - Scurve *tmpCur; - -/* Check if a curve with same id already exists */ - - if ( !Openflag ) return(102); - if ( ENgetcurveindex(id, &i) == 0 ) return(215); - -/* Check that id name is not too long */ - - if (strlen(id) > MAXID) return(250); - -/* Allocate memory for a new array of curves */ - - n = Ncurves + 1; - tmpCur = (Scurve *) calloc(n+1, sizeof(Scurve)); - if ( tmpCur == NULL ) return(101); - -/* Copy contents of old curve array to new one */ - - for (i=0; i<=Ncurves; i++) - { - strcpy(tmpCur[i].ID, Curve[i].ID); - tmpCur[i].Npts = Curve[i].Npts; - tmpCur[i].X = (double *) calloc(Curve[i].Npts, sizeof(double)); - tmpCur[i].Y = (double *) calloc(Curve[i].Npts, sizeof(double)); - if (tmpCur[i].X == NULL) err = 1; - else if (tmpCur[i].Y == NULL) err = 1; - else for (j=0; j Ncurves) return(206); - if (n <= 0) return(202); - -/* Re-set number of points & reallocate memory for values */ - Curve[index].Npts = n; - Curve[index].X = (double *) realloc(Curve[index].X, n*sizeof(double)); - Curve[index].Y = (double *) realloc(Curve[index].Y, n*sizeof(double)); - if (Curve[index].X == NULL) return(101); - if (Curve[index].Y == NULL) return(101); - -/* Load values into curve */ - for (j=0; j Ncurves) return(206); - if (pnt <= 0 || pnt > Curve[index].Npts) return(251); - Curve[index].X[pnt-1] = x; - Curve[index].Y[pnt-1] = y; - return(0); -} - - -int DLLEXPORT ENsettimeparam(int code, long value) - -{ - if (!Openflag) return(102); - if (OpenHflag || OpenQflag) { - // --> there's nothing wrong with changing certain time parameters during a simulation run, or before the run has started. - // todo -- how to tell? - /* - if (code == EN_DURATION || code == EN_HTIME || code == EN_REPORTSTEP || code == EN_DURATION || Htime == 0) { - // it's ok - } - else { - return(109); - } - */ - } - if (value < 0) return(202); - switch(code) - { - case EN_DURATION: - Dur = value; - if (Rstart > Dur) Rstart = 0; - break; - - case EN_HYDSTEP: - if (value == 0) return(202); - Hstep = value; - Hstep = MIN(Pstep, Hstep); - Hstep = MIN(Rstep, Hstep); - Qstep = MIN(Qstep, Hstep); - break; - - case EN_QUALSTEP: - if (value == 0) return(202); - Qstep = value; - Qstep = MIN(Qstep, Hstep); - break; - - case EN_PATTERNSTEP: - if (value == 0) return(202); - Pstep = value; - if (Hstep > Pstep) Hstep = Pstep; - break; - - case EN_PATTERNSTART: - Pstart = value; - break; - - case EN_REPORTSTEP: - if (value == 0) return(202); - Rstep = value; - if (Hstep > Rstep) Hstep = Rstep; - break; - - case EN_REPORTSTART: - if (Rstart > Dur) return(202); - Rstart = value; - break; - - case EN_RULESTEP: - if (value == 0) return(202); - Rulestep = value; - Rulestep = MIN(Rulestep, Hstep); - break; - - case EN_STATISTIC: - if (value > RANGE) return(202); - Tstatflag = (char)value; - break; - - case EN_HTIME: - Htime = value; - break; - - case EN_QTIME: - Qtime = value; - break; - - default: - return(251); - } - return(0); -} - - -int DLLEXPORT ENsetoption(int code, EN_API_FLOAT_TYPE v) -/*---------------------------------------------------------------- -** Input: code = option code (see EPANET2.H) -** v = option value -** Output: none -** Returns: error code -** Purpose: sets value for an analysis option -**---------------------------------------------------------------- -*/ -{ - int i,j; - double Ke,n,ucf, value = v; - if (!Openflag) return(102); - switch (code) - { - case EN_TRIALS: if (value < 1.0) return(202); - MaxIter = (int)value; - break; - case EN_ACCURACY: if (value < 1.e-5 || value > 1.e-1) return(202); - Hacc = value; - break; - case EN_TOLERANCE: if (value < 0.0) return(202); - Ctol = value/Ucf[QUALITY]; - break; - case EN_EMITEXPON: if (value <= 0.0) return(202); - n = 1.0/value; - ucf = pow(Ucf[FLOW],n)/Ucf[PRESSURE]; - for (i=1; i<=Njuncs; i++) - { - j = ENgetnodevalue(i,EN_EMITTER,&v); - Ke = v; - if (j == 0 && Ke > 0.0) Node[i].Ke = ucf/pow(Ke,n); - } - Qexp = n; - break; - case EN_DEMANDMULT: if (value <= 0.0) return(202); - Dmult = value; - break; - default: return(251); - } - return(0); -} - - -int DLLEXPORT ENsetstatusreport(int code) -{ - int errcode = 0; - if (code >= 0 && code <= 2) Statflag = (char)code; - else errcode = 202; - return(errcode); -} - - -int DLLEXPORT ENsetqualtype(int qualcode, char *chemname, char *chemunits, char *tracenode) -{ -/*** Updated 3/1/01 ***/ - double ccf = 1.0; - int i; - - if (!Openflag) return(102); - if (qualcode < EN_NONE || qualcode > EN_TRACE) return(251); - Qualflag = (char)qualcode; - Ctol *= Ucf[QUALITY]; - if (Qualflag == CHEM) /* Chemical constituent */ - { - strncpy(ChemName,chemname,MAXID); - strncpy(ChemUnits,chemunits,MAXID); - -/*** Updated 3/1/01 ***/ - strncpy(Field[QUALITY].Units,ChemUnits,MAXID); - strncpy(Field[REACTRATE].Units,ChemUnits,MAXID); - strcat(Field[REACTRATE].Units,t_PERDAY); - ccf = 1.0/LperFT3; - - } - if (Qualflag == TRACE) /* Source tracing option */ - { - TraceNode = findnode(tracenode); - if (TraceNode == 0) return(203); - strncpy(ChemName,u_PERCENT,MAXID); - strncpy(ChemUnits,tracenode,MAXID); - -/*** Updated 3/1/01 ***/ - strcpy(Field[QUALITY].Units,u_PERCENT); - } - if (Qualflag == AGE) /* Water age analysis */ - { - strncpy(ChemName,w_AGE,MAXID); - strncpy(ChemUnits,u_HOURS,MAXID); - -/*** Updated 3/1/01 ***/ - strcpy(Field[QUALITY].Units,u_HOURS); - } - - /* when changing from CHEM to AGE or TRACE, nodes initial quality values must be returned to their original ones */ - if ((Qualflag == AGE || Qualflag == TRACE) & (Ucf[QUALITY] != 1)) - { - for (i=1; i<=Nnodes; i++) - { - Node[i].C0 *= Ucf[QUALITY]; - } - } - -/*** Updated 3/1/01 ***/ - Ucf[QUALITY] = ccf; - Ucf[LINKQUAL] = ccf; - Ucf[REACTRATE] = ccf; - - Ctol /= Ucf[QUALITY]; - - return(0); -} - -int DLLEXPORT ENgetheadcurveindex(int index, int *curveindex) -{ - if (!Openflag) return(102); - if (index < 1 || index > Nlinks || PUMP != Link[index].Type) return(204); - *curveindex = Pump[PUMPINDEX(index)].Hcurve; - return(0); -} - -int DLLEXPORT ENgetpumptype(int index, int *type) -{ - *type=-1; - if (!Openflag) return(102); - if (index < 1 || index > Nlinks || PUMP != Link[index].Type) return(204); - *type = Pump[PUMPINDEX(index)].Ptype; - return(0); -} - -/* ----------------------------------------------------------------- - Functions for opening files ----------------------------------------------------------------- -*/ - - -int openfiles(char *f1, char *f2, char *f3) -/*---------------------------------------------------------------- -** Input: f1 = pointer to name of input file -** f2 = pointer to name of report file -** f3 = pointer to name of binary output file -** Output: none -** Returns: error code -** Purpose: opens input & report files -**---------------------------------------------------------------- -*/ -{ -/* Initialize file pointers to NULL */ - InFile = NULL; - RptFile = NULL; - OutFile = NULL; - HydFile = NULL; - -/* Save file names */ - strncpy(InpFname,f1,MAXFNAME); - strncpy(Rpt1Fname,f2,MAXFNAME); - strncpy(OutFname,f3,MAXFNAME); - if (strlen(f3) > 0) Outflag = SAVE; //(2.00.12 - LR) - else Outflag = SCRATCH; //(2.00.12 - LR) - -/* Check that file names are not identical */ - if (strcomp(f1,f2) || strcomp(f1,f3) || (strcomp(f2,f3) && (strlen(f2) > 0 || strlen(f3) > 0))) - { - writecon(FMT04); - return(301); - } - -/* Attempt to open input and report files */ - if ((InFile = fopen(f1,"rt")) == NULL) - { - writecon(FMT05); - writecon(f1); - return(302); - } - if (strlen(f2) == 0) RptFile = stdout; - else if ((RptFile = fopen(f2,"wt")) == NULL) - { - writecon(FMT06); - return(303); - } - - return(0); -} /* End of openfiles */ - - -int openhydfile() -/*---------------------------------------------------------------- -** Input: none -** Output: none -** Returns: error code -** Purpose: opens file that saves hydraulics solution -**---------------------------------------------------------------- -*/ -{ - INT4 nsize[6]; /* Temporary array */ - INT4 magic; - INT4 version; - int errcode = 0; - -/* If HydFile currently open, then close it if its not a scratch file */ - if (HydFile != NULL) - { - if (Hydflag == SCRATCH) return(0); - fclose(HydFile); - } - -/* Use Hydflag to determine the type of hydraulics file to use. */ -/* Write error message if the file cannot be opened. */ - HydFile = NULL; - switch(Hydflag) - { - case SCRATCH: getTmpName(HydFname); //(2.00.12 - LR) - HydFile = fopen(HydFname, "w+b"); //(2.00.12 - LR) - break; - case SAVE: HydFile = fopen(HydFname,"w+b"); - break; - case USE: HydFile = fopen(HydFname,"rb"); - break; - } - if (HydFile == NULL) return(305); - -/* If a previous hydraulics solution is not being used, then */ -/* save the current network size parameters to the file. */ - if (Hydflag != USE) - { - magic = MAGICNUMBER; - version = ENGINE_VERSION; - nsize[0] = Nnodes; - nsize[1] = Nlinks; - nsize[2] = Ntanks; - nsize[3] = Npumps; - nsize[4] = Nvalves; - nsize[5] = (int)Dur; - fwrite(&magic,sizeof(INT4),1,HydFile); - fwrite(&version,sizeof(INT4),1,HydFile); - fwrite(nsize,sizeof(INT4),6,HydFile); - } - -/* If a previous hydraulics solution is being used, then */ -/* make sure its network size parameters match those of */ -/* the current network. */ - if (Hydflag == USE) - { - fread(&magic,sizeof(INT4),1,HydFile); - if (magic != MAGICNUMBER) return(306); - fread(&version,sizeof(INT4),1,HydFile); - if (version != ENGINE_VERSION) return(306); - if (fread(nsize,sizeof(INT4),6,HydFile) < 6) return(306); - if (nsize[0] != Nnodes || nsize[1] != Nlinks || - nsize[2] != Ntanks || nsize[3] != Npumps || - nsize[4] != Nvalves || nsize[5] != Dur) return(306); - SaveHflag = TRUE; - } - -/* Save current position in hydraulics file */ -/* where storage of hydraulic results begins */ - HydOffset = ftell(HydFile); - return(errcode); -} - - -int openoutfile() -/*---------------------------------------------------------------- -** Input: none -** Output: none -** Returns: error code -** Purpose: opens binary output file. -**---------------------------------------------------------------- -*/ -{ - int errcode = 0; - -/* Close output file if already opened */ - if (OutFile != NULL) fclose(OutFile); - OutFile = NULL; - if (TmpOutFile != NULL) fclose(TmpOutFile); - TmpOutFile = NULL; - - if (Outflag == SCRATCH) remove(OutFname); //(2.00.12 - LR) - remove(TmpFname); //(2.00.12 - LR) - -/* If output file name was supplied, then attempt to */ -/* open it. Otherwise open a temporary output file. */ - //if (strlen(OutFname) != 0) //(2.00.12 - LR) - if (Outflag == SAVE) //(2.00.12 - LR) - { - if ( (OutFile = fopen(OutFname,"w+b")) == NULL) - { - writecon(FMT07); - errcode = 304; - } - } - //else if ( (OutFile = tmpfile()) == NULL) //(2.00.12 - LR) - else //(2.00.12 - LR) - { - getTmpName(OutFname); //(2.00.12 - LR) - if ( (OutFile = fopen(OutFname,"w+b")) == NULL) //(2.00.12 - LR) - { - writecon(FMT08); - errcode = 304; - } - } - -/* Save basic network data & energy usage results */ - ERRCODE(savenetdata()); - OutOffset1 = ftell(OutFile); - ERRCODE(saveenergy()); - OutOffset2 = ftell(OutFile); - -/* Open temporary file if computing time series statistic */ - if (!errcode) - { - if (Tstatflag != SERIES) - { - //if ( (TmpOutFile = tmpfile()) == NULL) errcode = 304; //(2.00.12 - LR) - getTmpName(TmpFname); //(2.00.12 - LR) - TmpOutFile = fopen(TmpFname, "w+b"); //(2.00.12 - LR) - if (TmpOutFile == NULL) errcode = 304; //(2.00.12 - LR) - } - else TmpOutFile = OutFile; - } - return(errcode); -} - - -/* ----------------------------------------------------------------- - Global memory management functions ----------------------------------------------------------------- -*/ - - -void initpointers() -/*---------------------------------------------------------------- -** Input: none -** Output: none -** Purpose: initializes global pointers to NULL -**---------------------------------------------------------------- -*/ -{ - NodeDemand = NULL; - NodeQual = NULL; - NodeHead = NULL; - Q = NULL; - PipeRateCoeff = NULL; - LinkStatus = NULL; - LinkSetting = NULL; - OldStat = NULL; - - Node = NULL; - Link = NULL; - Tank = NULL; - Pump = NULL; - Valve = NULL; - Pattern = NULL; - Curve = NULL; - Control = NULL; - Coord = NULL; - - X = NULL; - Patlist = NULL; - Curvelist = NULL; - Adjlist = NULL; - Aii = NULL; - Aij = NULL; - F = NULL; - P = NULL; - Y = NULL; - Order = NULL; - Row = NULL; - Ndx = NULL; - XLNZ = NULL; - NZSUB = NULL; - LNZ = NULL; - NodeHashTable = NULL; - LinkHashTable = NULL; - initrules(); -} - - -int allocdata() -/*---------------------------------------------------------------- -** Input: none -** Output: none -** Returns: error code -** Purpose: allocates memory for network data structures -**---------------------------------------------------------------- -*/ -{ - int n; - int errcode = 0; - -/* Allocate node & link ID hash tables */ - NodeHashTable = ENHashTableCreate(); - LinkHashTable = ENHashTableCreate(); - ERRCODE(MEMCHECK(NodeHashTable)); - ERRCODE(MEMCHECK(LinkHashTable)); - -/* Allocate memory for network nodes */ -/************************************************************* - NOTE: Because network components of a given type are indexed - starting from 1, their arrays must be sized 1 - element larger than the number of components. -*************************************************************/ - if (!errcode) - { - n = MaxNodes + 1; - Node = (Snode *) calloc(n, sizeof(Snode)); - NodeDemand = (double *) calloc(n, sizeof(double)); - NodeQual = (double *) calloc(n, sizeof(double)); - NodeHead = (double *) calloc(n, sizeof(double)); - ERRCODE(MEMCHECK(Node)); - ERRCODE(MEMCHECK(NodeDemand)); - ERRCODE(MEMCHECK(NodeQual)); - ERRCODE(MEMCHECK(NodeHead)); - } - -/* Allocate memory for network links */ - if (!errcode) - { - n = MaxLinks + 1; - Link = (Slink *) calloc(n, sizeof(Slink)); - Q = (double *) calloc(n, sizeof(double)); - LinkSetting = (double *) calloc(n, sizeof(double)); - LinkStatus = (char *) calloc(n, sizeof(char)); - ERRCODE(MEMCHECK(Link)); - ERRCODE(MEMCHECK(Q)); - ERRCODE(MEMCHECK(LinkSetting)); - ERRCODE(MEMCHECK(LinkStatus)); - } - -/* Allocate memory for tanks, sources, pumps, valves, */ -/* controls, demands, time patterns, & operating curves */ - if (!errcode) - { - Tank = (Stank *) calloc(MaxTanks+1, sizeof(Stank)); - Pump = (Spump *) calloc(MaxPumps+1, sizeof(Spump)); - Valve = (Svalve *) calloc(MaxValves+1, sizeof(Svalve)); - Control = (Scontrol *) calloc(MaxControls+1,sizeof(Scontrol)); - Pattern = (Spattern *) calloc(MaxPats+1, sizeof(Spattern)); - Curve = (Scurve *) calloc(MaxCurves+1, sizeof(Scurve)); - if (Coordflag == TRUE) - { - Coord = (Scoord *) calloc(MaxNodes+1, sizeof(Scoord)); - } - ERRCODE(MEMCHECK(Tank)); - ERRCODE(MEMCHECK(Pump)); - ERRCODE(MEMCHECK(Valve)); - ERRCODE(MEMCHECK(Control)); - ERRCODE(MEMCHECK(Pattern)); - ERRCODE(MEMCHECK(Curve)); - if (Coordflag == TRUE) ERRCODE(MEMCHECK(Coord)); - } - -/* Initialize pointers used in patterns, curves, and demand category lists */ - if (!errcode) - { - for (n=0; n<=MaxPats; n++) - { - Pattern[n].Length = 0; - Pattern[n].F = NULL; - } - for (n=0; n<=MaxCurves; n++) - { - Curve[n].Npts = 0; - Curve[n].Type = -1; - Curve[n].X = NULL; - Curve[n].Y = NULL; - } - - for (n=0; n<=MaxNodes; n++) - { - // node demand - Node[n].D = NULL; - /* ini coord data */ - if (Coordflag == TRUE) - { - Coord[n].X = 0; - Coord[n].Y = 0; - Coord[n].HaveCoords = FALSE; - } - } - - } - -/* Allocate memory for rule base (see RULES.C) */ - if (!errcode) errcode = allocrules(); - return(errcode); -} /* End of allocdata */ - - -void freeTmplist(STmplist *t) -/*---------------------------------------------------------------- -** Input: t = pointer to start of a temporary list -** Output: none -** Purpose: frees memory used for temporary storage -** of pattern & curve data -**---------------------------------------------------------------- -*/ -{ - STmplist *tnext; - while (t != NULL) - { - tnext = t->next; - freeFloatlist(t->x); - freeFloatlist(t->y); - free(t); - t = tnext; - } -} - - -void freeFloatlist(SFloatlist *f) -/*---------------------------------------------------------------- -** Input: f = pointer to start of list of floats -** Output: none -** Purpose: frees memory used for storing list of floats -**---------------------------------------------------------------- -*/ -{ - SFloatlist *fnext; - while (f != NULL) - { - fnext = f->next; - free(f); - f = fnext; - } -} - - -void freedata() -/*---------------------------------------------------------------- -** Input: none -** Output: none -** Purpose: frees memory allocated for network data structures. -**---------------------------------------------------------------- -*/ -{ - int j; - Pdemand demand, nextdemand; - Psource source; - -/* Free memory for computed results */ - free(NodeDemand); - free(NodeQual); - free(NodeHead); - free(Q); - free(LinkSetting); - free(LinkStatus); - -/* Free memory for node data */ - if (Node != NULL) - { - for (j=0; j<=MaxNodes; j++) - { - /* Free memory used for demand category list */ - demand = Node[j].D; - while (demand != NULL) - { - nextdemand = demand->next; - free(demand); - demand = nextdemand; - } - /* Free memory used for WQ source data */ - source = Node[j].S; - if (source != NULL) free(source); - } - free(Node); - } - -/* Free memory for other network objects */ - free(Link); - free(Tank); - free(Pump); - free(Valve); - free(Control); - -/* Free memory for time patterns */ - if (Pattern != NULL) - { - for (j=0; j<=MaxPats; j++) free(Pattern[j].F); - free(Pattern); - } - -/* Free memory for curves */ - if (Curve != NULL) - { - for (j=0; j<=MaxCurves; j++) - { - free(Curve[j].X); - free(Curve[j].Y); - } - free(Curve); - } - -/* Free memory for node coordinates */ - if (Coordflag == TRUE) free(Coord); - -/* Free memory for rule base (see RULES.C) */ - freerules(); - -/* Free hash table memory */ - if (NodeHashTable != NULL) ENHashTableFree(NodeHashTable); - if (LinkHashTable != NULL) ENHashTableFree(LinkHashTable); -} - - -/* ----------------------------------------------------------------- - General purpose functions ----------------------------------------------------------------- -*/ - -/*** New function for 2.00.12 ***/ //(2.00.12 - LR) -char* getTmpName(char* fname) -// -// Input: fname = file name string -// Output: returns pointer to file name -// Purpose: creates a temporary file name with path prepended to it. -// -{ - char name[MAXFNAME+1]; - int n; - - // --- for Windows systems: - #ifdef WINDOWS - // --- use system function tmpnam() to create a temporary file name - tmpnam(name); - - // --- if user supplied the name of a temporary directory, - // then make it be the prefix of the full file name - n = (int)strlen(TmpDir); - if ( n > 0 ) - { - strcpy(fname, TmpDir); - if ( fname[n-1] != '\\' ) strcat(fname, "\\"); - } - - // --- otherwise, use the relative path notation as the file name - // prefix so that the file will be placed in the current directory - else - { - strcpy(fname, ".\\"); - } - - // --- now add the prefix to the file name - strcat(fname, name); - - // --- for non-Windows systems: - #else - // --- use system function mkstemp() to create a temporary file name - strcpy(fname, "enXXXXXX"); - mkstemp(fname); - #endif - return fname; -} - - -int strcomp(char *s1, char *s2) -/*--------------------------------------------------------------- -** Input: s1 = character string -** s2 = character string -** Output: none -** Returns: 1 if s1 is same as s2, 0 otherwise -** Purpose: case insensitive comparison of strings s1 & s2 -**--------------------------------------------------------------- -*/ -{ - int i; - for (i=0; UCHAR(s1[i]) == UCHAR(s2[i]); i++) - if (!s1[i+1] && !s2[i+1]) return(1); - return(0); -} /* End of strcomp */ - - -double interp(int n, double x[], double y[], double xx) -/*---------------------------------------------------------------- -** Input: n = number of data pairs defining a curve -** x = x-data values of curve -** y = y-data values of curve -** xx = specified x-value -** Output: none -** Returns: y-value on curve at x = xx -** Purpose: uses linear interpolation to find y-value on a -** data curve corresponding to specified x-value. -** NOTE: does not extrapolate beyond endpoints of curve. -**---------------------------------------------------------------- -*/ -{ - int k,m; - double dx,dy; - - m = n - 1; /* Highest data index */ - if (xx <= x[0]) return(y[0]); /* xx off low end of curve */ - for (k=1; k<=m; k++) /* Bracket xx on curve */ - { - if (x[k] >= xx) /* Interp. over interval */ - { - dx = x[k]-x[k-1]; - dy = y[k]-y[k-1]; - if (ABS(dx) < TINY) return(y[k]); - else return(y[k] - (x[k]-xx)*dy/dx); - } - } - return(y[m]); /* xx off high end of curve */ -} /* End of interp */ - - -int findnode(char *id) -/*---------------------------------------------------------------- -** Input: id = node ID -** Output: none -** Returns: index of node with given ID, or 0 if ID not found -** Purpose: uses hash table to find index of node with given ID -**---------------------------------------------------------------- -*/ -{ - return(ENHashTableFind(NodeHashTable,id)); -} - - -int findlink(char *id) -/*---------------------------------------------------------------- -** Input: id = link ID -** Output: none -** Returns: index of link with given ID, or 0 if ID not found -** Purpose: uses hash table to find index of link with given ID -**---------------------------------------------------------------- -*/ -{ - return(ENHashTableFind(LinkHashTable,id)); -} - - -char *geterrmsg(int errcode) -/*---------------------------------------------------------------- -** Input: errcode = error code -** Output: none -** Returns: pointer to string with error message -** Purpose: retrieves text of error message -**---------------------------------------------------------------- -*/ -{ - switch (errcode) - { /* Warnings */ -/* - case 1: strcpy(Msg,WARN1); break; - case 2: strcpy(Msg,WARN2); break; - case 3: strcpy(Msg,WARN3); break; - case 4: strcpy(Msg,WARN4); break; - case 5: strcpy(Msg,WARN5); break; - case 6: strcpy(Msg,WARN6); break; -*/ - /* System Errors */ - case 101: strcpy(Msg,ERR101); break; - case 102: strcpy(Msg,ERR102); break; - case 103: strcpy(Msg,ERR103); break; - case 104: strcpy(Msg,ERR104); break; - case 105: strcpy(Msg,ERR105); break; - case 106: strcpy(Msg,ERR106); break; - case 107: strcpy(Msg,ERR107); break; - case 108: strcpy(Msg,ERR108); break; - case 109: strcpy(Msg,ERR109); break; - case 110: strcpy(Msg,ERR110); break; - case 120: strcpy(Msg,ERR120); break; - - /* Input Errors */ - case 200: strcpy(Msg,ERR200); break; - case 223: strcpy(Msg,ERR223); break; - case 224: strcpy(Msg,ERR224); break; - - /* Toolkit function errors */ - case 202: sprintf(Msg,ERR202,t_FUNCCALL,""); break; - case 203: sprintf(Msg,ERR203,t_FUNCCALL,""); break; - case 204: sprintf(Msg,ERR204,t_FUNCCALL,""); break; - case 205: sprintf(Msg,ERR205,t_FUNCCALL,""); break; - case 207: sprintf(Msg,ERR207,t_FUNCCALL,""); break; - case 240: sprintf(Msg,ERR240,t_FUNCCALL,""); break; - case 241: sprintf(Msg,ERR241,t_FUNCCALL,""); break; - case 250: sprintf(Msg,ERR250); break; - case 251: sprintf(Msg,ERR251); break; - case 253: sprintf(Msg,ERR253); break; - case 254: sprintf(Msg,ERR254); break; - case 255: sprintf(Msg,ERR255); break; - - /* File Errors */ - case 301: strcpy(Msg,ERR301); break; - case 302: strcpy(Msg,ERR302); break; - case 303: strcpy(Msg,ERR303); break; - case 304: strcpy(Msg,ERR304); break; - case 305: strcpy(Msg,ERR305); break; - case 306: strcpy(Msg,ERR306); break; - case 307: strcpy(Msg,ERR307); break; - case 308: strcpy(Msg,ERR308); break; - case 309: strcpy(Msg,ERR309); break; - - case 401: strcpy(Msg,ERR401); break; - default: strcpy(Msg,""); - } - return(Msg); -} - - -void errmsg(int errcode) -/*---------------------------------------------------------------- -** Input: errcode = error code -** Output: none -** Purpose: writes error message to report file -**---------------------------------------------------------------- -*/ -{ - if (errcode == 309) /* Report file write error - */ - { /* Do not write msg to file. */ - writecon("\n "); - writecon(geterrmsg(errcode)); - } - else if (RptFile != NULL && Messageflag) - { - writeline(geterrmsg(errcode)); - } -} - - -void writecon(char *s) -/*---------------------------------------------------------------- -** Input: text string -** Output: none -** Purpose: writes string of characters to console -**---------------------------------------------------------------- -*/ -{ - //(2.00.11 - LR) - //fprintf(stdout,s); - //fflush(stdout); - -} - - -void writewin(char *s) -/*---------------------------------------------------------------- -** Input: text string -** Output: none -** Purpose: passes character string to viewprog() in -** application which calls the EPANET DLL -**---------------------------------------------------------------- -*/ -{ - char progmsg[MAXMSG+1]; - if (viewprog != NULL) - { - strncpy(progmsg,s,MAXMSG); - viewprog(progmsg); - } -} - - -int DLLEXPORT ENgetnumdemands(int nodeIndex, int *numDemands) -{ - Pdemand d; - int n=0; - /* Check for valid arguments */ - if (!Openflag) return(102); - if (nodeIndex <= 0 || nodeIndex > Nnodes) return(203); - for(d=Node[nodeIndex].D; d != NULL; d=d->next) n++; - *numDemands=n; - return 0; -} - - -int DLLEXPORT ENgetbasedemand(int nodeIndex, int demandIdx, EN_API_FLOAT_TYPE *baseDemand) -{ - Pdemand d; - int n=1; - /* Check for valid arguments */ - if (!Openflag) return(102); - if (nodeIndex <= 0 || nodeIndex > Nnodes) return(203); - if (nodeIndex <= Njuncs) { - for(d=Node[nodeIndex].D; nnext) { - n++; - } - if(n != demandIdx) { - return(253); - } - *baseDemand=(EN_API_FLOAT_TYPE)(d->Base*Ucf[FLOW]); - } - else { - *baseDemand=(EN_API_FLOAT_TYPE)(0.0); - } - return 0; -} - - -int DLLEXPORT ENsetbasedemand(int nodeIndex, int demandIdx, EN_API_FLOAT_TYPE baseDemand) -{ - Pdemand d; - int n=1; - /* Check for valid arguments */ - if (!Openflag) return(102); - if (nodeIndex <= 0 || nodeIndex > Nnodes) return(203); - if (nodeIndex <= Njuncs) { - for(d=Node[nodeIndex].D; nnext) n++; - if(n!=demandIdx) return(253); - d->Base = baseDemand/Ucf[FLOW]; - } - return 0; -} - - -int DLLEXPORT ENgetdemandpattern(int nodeIndex, int demandIdx, int *pattIdx) -{ - Pdemand d; - int n=1; - /* Check for valid arguments */ - if (!Openflag) return(102); - if (nodeIndex <= 0 || nodeIndex > Nnodes) return(203); - for(d=Node[nodeIndex].D; nnext) n++; - if(n!=demandIdx) return(253); - *pattIdx=d->Pat; - return 0; -} - - -int DLLEXPORT ENgetaveragepatternvalue(int index, EN_API_FLOAT_TYPE *value) -{ - int i; - *value = 0.0; - if (!Openflag) return(102); - if (index < 1 || index > Npats) return(205); - //if (period < 1 || period > Pattern[index].Length) return(251); - for (i=0; i Nrules) return(257); - *priority = (EN_API_FLOAT_TYPE)Rule[index].priority; - count = 1; - p = Rule[index].Pchain; - while (p->next != NULL) - { - count++; - p=p->next; - } - *nPremises = count; - count = 1; - c=Rule[index].Tchain; - while (c->next != NULL) - { - count++; - c = c->next; - } - *nTrueActions = count; - - c = Rule[index].Fchain; - count = 0; - if (c != NULL) - { - count = 1; - while (c->next != NULL) - { - count ++; - c = c->next; - } - } - *nFalseActions = count; - return(0); -} - - -int DLLEXPORT ENgetpremise(int indexRule, int idxPremise, int *logop, int *object, int *indexObj, int *variable, int *relop, int *status, EN_API_FLOAT_TYPE *value) -{ - int count = 1, error = 0, nPremises, a, b; - EN_API_FLOAT_TYPE priority; - struct Premise *p; - - if (indexRule > Nrules) return(257); - error = ENgetrule(indexRule, &nPremises, &a, &b, &priority); - if (idxPremise > nPremises) return(258); - - p = Rule[indexRule].Pchain; - while (count < idxPremise) - { - count++; - p = p->next; - } - *logop = p->logop; - *object = p->object; - *indexObj = p->index; - *variable = p->variable; - *relop = p->relop; - *status = p->status; - *value = p[0].value; - return(0); -} - - -int DLLEXPORT ENsetrulepriority(int index, EN_API_FLOAT_TYPE priority) -/*---------------------------------------------------------------- -** Input: index = index of the rule -** priority = rule priority -** Output: none -** Returns: error code -**---------------------------------------------------------------- -*/ -{ - if (index > Nrules) return(257); - Rule[index].priority = priority; - - return(0); -} - - -int DLLEXPORT ENsetpremise(int indexRule, int indexPremise, int logop, int object, int indexObj, int variable, int relop, int status, EN_API_FLOAT_TYPE value) -{ - int count = 1,error = 0, nPremises, a, b; - EN_API_FLOAT_TYPE priority; - struct Premise *p; - - if (indexRule > Nrules) return(257); - error = ENgetrule(indexRule, &nPremises, &a, &b, &priority); - if (indexPremise > nPremises) return(258); - - p = Rule[indexRule].Pchain; - while (count < indexPremise) - { - count++; - p = p->next; - } - p->logop = logop; - p->object = object; - p->index = indexObj; - p->variable = variable; - p->relop = relop; - p->status = status; - p->value = value; - return(0); -} - - -int DLLEXPORT ENsetpremiseindex(int indexRule, int indexPremise, int indexObj) -{ - int count = 1,error = 0, nPremises, a, b; - EN_API_FLOAT_TYPE priority; - struct Premise *p; - - if (indexRule > Nrules) return(257); - error = ENgetrule(indexRule, &nPremises, &a, &b, &priority); - if (indexPremise > nPremises) return(258); - - p = Rule[indexRule].Pchain; - while (count < indexPremise) - { - count++; - p = p->next; - } - p->index = indexObj; - return(0); -} - - -int DLLEXPORT ENsetpremisestatus(int indexRule, int indexPremise, int status) -{ - int count = 1, error = 0, nPremises, a, b; - EN_API_FLOAT_TYPE priority; - struct Premise *p; - - if (indexRule > Nrules) return(257); - error = ENgetrule(indexRule, &nPremises, &a, &b, &priority); - if (indexPremise > nPremises) return(258); - - p = Rule[indexRule].Pchain; - while (count < indexPremise) - { - count++; - p = p->next; - } - p->status = status; - return(0); -} - - -int DLLEXPORT ENsetpremisevalue(int indexRule, int indexPremise, EN_API_FLOAT_TYPE value) -{ - int count = 1,error = 0, nPremises, a, b; - EN_API_FLOAT_TYPE priority; - struct Premise *p; - - if (indexRule > Nrules) return(257); - error = ENgetrule(indexRule, &nPremises, &a, &b, &priority); - if (indexPremise > nPremises) return(258); - - p = Rule[indexRule].Pchain; - while (count < indexPremise) - { - count++; - p = p->next; - } - p->value = value; - return(0); -} - - -int DLLEXPORT ENgettrueaction(int indexRule, int indexAction, int *indexLink, int *status, EN_API_FLOAT_TYPE *setting) -{ - int count = 1, error = 0, nTrueAction, c, b; - EN_API_FLOAT_TYPE priority; - struct Action *a; - - if (indexRule > Nrules) return(252); - error = ENgetrule(indexRule, &c, &nTrueAction, &b, &priority); - if (indexAction > nTrueAction) return(253); - - a = Rule[indexRule].Tchain; - while (countnext; - } - *indexLink = a->link; - *status = a->status; - *setting = a->setting; - return(0); -} - - -int DLLEXPORT ENsettrueaction(int indexRule, int indexAction, int indexLink, int status, EN_API_FLOAT_TYPE setting) -{ - int count = 1, error = 0, nTrueAction, c, b; - EN_API_FLOAT_TYPE priority; - struct Action *a; - - if (indexRule > Nrules) return(257); - error = ENgetrule(indexRule, &c, &nTrueAction, &b, &priority); - if (indexAction > nTrueAction) return(258); - - a = Rule[indexRule].Tchain; - while (countnext; - } - a->link = indexLink; - a->status = status; - a->setting = setting; - return(0); -} - - -int DLLEXPORT ENgetfalseaction(int indexRule, int indexAction, int *indexLink, int *status, EN_API_FLOAT_TYPE *setting) -{ - int count = 1, error = 0, nFalseAction, c, b; - EN_API_FLOAT_TYPE priority; - struct Action *a; - - if (indexRule > Nrules) return(257); - error = ENgetrule(indexRule, &c, &b, &nFalseAction, &priority); - if (indexAction > nFalseAction) return(258); - - a = Rule[indexRule].Fchain; - while (count < indexAction) - { - count++; - a = a->next; - } - *indexLink = a->link; - *status = a->status; - *setting = a->setting; - return(0); -} - - -int DLLEXPORT ENsetfalseaction(int indexRule, int indexAction, int indexLink, int status,EN_API_FLOAT_TYPE setting) -{ - int count = 1, error = 0, nFalseAction, c, b; - EN_API_FLOAT_TYPE priority; - struct Action *a; - - if (indexRule > Nrules) return(257); - error = ENgetrule(indexRule, &c, &b, &nFalseAction, &priority); - if (indexAction > nFalseAction) return(258); - - a = Rule[indexRule].Fchain; - while (count < indexAction) - { - count++; - a = a->next; - } - a->link = indexLink; - a->status = status; - a->setting = setting; - - return(0); - } - -int DLLEXPORT ENgetruleID(int indexRule, char* id) -{ - strcpy(id,""); - if (!Openflag) return(102); - if (indexRule < 1 || indexRule > Nrules) return(257); - strcpy(id,Rule[indexRule].label); - return(0); -} - -/*************************** END OF EPANET.C ***************************/ - +/* +******************************************************************************* + +EPANET.C -- Hydraulic & Water Quality Simulator for Water Distribution Networks + +VERSION: 2.1 +AUTHORS: L. Rossman - US EPA - NRMRL + OpenWaterAnalytics members: see git stats for contributors + +EPANET performs extended period hydraulic and water quality analysis of +looped, pressurized piping networks. The program consists of the +following code modules: + + EPANET.C -- main module providing supervisory control + INPUT1.C -- controls processing of input data + INPUT2.C -- reads data from input file + INPUT3.C -- parses individual lines of input data + INPFILE.C -- saves modified input data to a text file + RULES.C -- implements rule-based control of piping system + HYDRAUL.C -- computes extended period hydraulic behavior + QUALITY.C -- tracks transport & fate of water quality + OUTPUT.C -- handles transfer of data to and from binary files + REPORT.C -- handles reporting of results to text file + SMATRIX.C -- sparse matrix linear equation solver routines + MEMPOOL.C -- memory allocation routines + HASH.C -- hash table routines + +The program can be compiled as either a stand-alone console application +or as a dynamic link library (DLL) of function calls depending on whether +the macro identifier 'DLL' is defined or not. + +See EPANET2.H for function prototypes of exported DLL functions +See FUNCS.H for prototypes of all other functions +See TYPES.H for declaration of global constants and data structures +See VARS.H for declaration of global variables +See TEXT.H for declaration of all string constants +See ENUMSTXT.H for assignment of string constants to enumerated types + +The following naming conventions are used in all modules of this program: +1. Names of exportable functions in the DLL begin with the "EN" prefix. +2. All other function names are lowercase. +3. Global variable names begin with an uppercase letter. +4. Local variable names are all lowercase. +5. Declared constants and enumerated values defined in TYPES.H are + all uppercase. +6. String constants defined in TEXT.H begin with a lower case character + followed by an underscore and then all uppercase characters (e.g. + t_HEADLOSS) + +-------------------------------------------------------------------------- + +This is the main module of the EPANET program. It uses a series of +functions, all beginning with the letters EN, to control program behavior. +See the main() and ENepanet() functions below for the simplest example of +these. + +This module calls the following functions that reside in other modules: + RULES.C + initrules() + allocrules() + closerules() + INPUT1.C + getdata() + initreport() + INPUT2.C + netsize() + setreport() + HYDRAUL.C + openhyd() + inithyd() + runhyd() + nexthyd() + closehyd() + resistance() + tankvolume() + getenergy() + setlinkstatus() + setlinksetting() + QUALITY.C + openqual() + initqual() + runqual() + nextqual() + stepqual() + closequal() + REPORT.C + writeline(pr, ) + writelogo() + writereport() + HASH.C + ENHashTablecreate() + ENHashTableFind() + ENHashTableFree() + +The macro ERRCODE(x) is defined in TYPES.H. It says if the current +value of the error code variable (errcode) is not fatal (< 100) then +execute function x and set the error code equal to its return value. + +******************************************************************************* +*/ + +/*** Need to define WINDOWS to use the getTmpName function ***/ +// --- define WINDOWS +#undef WINDOWS +#ifdef _WIN32 +#define WINDOWS +#endif +#ifdef __WIN32__ +#define WINDOWS +#endif +/************************************************************/ + +#include +#include +#include +#ifndef __APPLE__ +#include +#endif +#include +#include + +#include "enumstxt.h" +#include "epanet2.h" +#include "funcs.h" +#include "text.h" +#include "types.h" +#define EXTERN +#include "epanet2.h" +#include "vars.h" + +/**************************************************************** + + LEGACY (v <= 2.1) API: uses global project variable + +*****************************************************************/ + +/*------------------------------------------------------------------------ + ** Input: f1 = pointer to name of input file + ** f2 = pointer to name of report file + ** f3 = pointer to name of binary output file + ** pviewprog = see note below + ** Output: none + ** Returns: error code + ** Purpose: runs a complete EPANET simulation + ** + ** The pviewprog() argument is a pointer to a callback function + ** that takes a character string (char *) as its only parameter. + ** The function would reside in and be used by the calling + ** program to display the progress messages that EPANET generates + ** as it carries out its computations. If this feature is not + ** needed then the argument should be NULL. + **------------------------------------------------------------------------- + */ +int DLLEXPORT ENepanet(char *f1, char *f2, char *f3, + void (*pviewprog)(char *)) { + int errcode = 0; + ERRCODE(EN_alloc(&_defaultModel)); + ERRCODE(EN_open(_defaultModel, f1, f2, f3)); + _defaultModel->viewprog = pviewprog; + if (_defaultModel->out_files.Hydflag != USE) { + ERRCODE(EN_solveH(_defaultModel)); + } + ERRCODE(EN_solveQ(_defaultModel)); + ERRCODE(EN_report(_defaultModel)); + EN_close(_defaultModel); + EN_free(_defaultModel); + return (errcode); +} +int DLLEXPORT ENopen(char *f1, char *f2, char *f3) { + int errcode = 0; + ERRCODE(EN_alloc(&_defaultModel)); + EN_open(_defaultModel, f1, f2, f3); + return (errcode); +} +int DLLEXPORT ENsaveinpfile(char *filename) { + return EN_saveinpfile(_defaultModel, filename); +} +int DLLEXPORT ENclose() { return EN_close(_defaultModel); } +int DLLEXPORT ENsolveH() { return EN_solveH(_defaultModel); } +int DLLEXPORT ENsaveH() { return EN_saveH(_defaultModel); } +int DLLEXPORT ENopenH() { return EN_openH(_defaultModel); } +int DLLEXPORT ENinitH(int flag) { return EN_initH(_defaultModel, flag); } +int DLLEXPORT ENrunH(long *t) { return EN_runH(_defaultModel, t); } +int DLLEXPORT ENnextH(long *tstep) { return EN_nextH(_defaultModel, tstep); } +int DLLEXPORT ENcloseH() { return EN_closeH(_defaultModel); } +int DLLEXPORT ENsavehydfile(char *filename) { + return EN_savehydfile(_defaultModel, filename); +} +int DLLEXPORT ENusehydfile(char *filename) { + return EN_usehydfile(_defaultModel, filename); +} +int DLLEXPORT ENsolveQ() { return EN_solveQ(_defaultModel); } +int DLLEXPORT ENopenQ() { return EN_openQ(_defaultModel); } +int DLLEXPORT ENinitQ(int saveflag) { + return EN_initQ(_defaultModel, saveflag); +} +int DLLEXPORT ENrunQ(long *t) { return EN_runQ(_defaultModel, t); } +int DLLEXPORT ENnextQ(long *tstep) { return EN_nextQ(_defaultModel, tstep); } +int DLLEXPORT ENstepQ(long *tleft) { return EN_stepQ(_defaultModel, tleft); } +int DLLEXPORT ENcloseQ() { return EN_closeQ(_defaultModel); } +int DLLEXPORT ENwriteline(char *line) { + return EN_writeline(_defaultModel, line); +} +int DLLEXPORT ENreport() { return EN_report(_defaultModel); } +int DLLEXPORT ENresetreport() { return EN_resetreport(_defaultModel); } +int DLLEXPORT ENsetreport(char *s) { return EN_setreport(_defaultModel, s); } +int DLLEXPORT ENgetversion(int *v) { return EN_getversion(v); } +int DLLEXPORT ENgetcontrol(int cindex, int *ctype, int *lindex, + EN_API_FLOAT_TYPE *setting, int *nindex, + EN_API_FLOAT_TYPE *level) { + return EN_getcontrol(_defaultModel, cindex, ctype, lindex, setting, nindex, + level); +} +int DLLEXPORT ENgetcount(int code, int *count) { + return EN_getcount(_defaultModel, (EN_CountType)code, count); +} +int DLLEXPORT ENgetoption(int code, EN_API_FLOAT_TYPE *value) { + return EN_getoption(_defaultModel, (EN_Option)code, value); +} +int DLLEXPORT ENgettimeparam(int code, long *value) { + return EN_gettimeparam(_defaultModel, code, value); +} +int DLLEXPORT ENgetflowunits(int *code) { + return EN_getflowunits(_defaultModel, code); +} +int DLLEXPORT ENgetpatternindex(char *id, int *index) { + return EN_getpatternindex(_defaultModel, id, index); +} +int DLLEXPORT ENgetpatternid(int index, char *id) { + return EN_getpatternid(_defaultModel, index, id); +} +int DLLEXPORT ENgetpatternlen(int index, int *len) { + return EN_getpatternlen(_defaultModel, index, len); +} +int DLLEXPORT ENgetpatternvalue(int index, int period, + EN_API_FLOAT_TYPE *value) { + return EN_getpatternvalue(_defaultModel, index, period, value); +} +int DLLEXPORT ENgetcurveindex(char *id, int *index) { + return EN_getcurveindex(_defaultModel, id, index); +} +int DLLEXPORT ENgetcurveid(int index, char *id) { + return EN_getcurveid(_defaultModel, index, id); +} +int DLLEXPORT ENgetcurvelen(int index, int *len) { + return EN_getcurvelen(_defaultModel, index, len); +} +int DLLEXPORT ENgetcurvevalue(int index, int pnt, EN_API_FLOAT_TYPE *x, + EN_API_FLOAT_TYPE *y) { + return EN_getcurvevalue(_defaultModel, index, pnt, x, y); +} +int DLLEXPORT ENgetqualtype(int *qualcode, int *tracenode) { + return EN_getqualtype(_defaultModel, qualcode, tracenode); +} +int DLLEXPORT ENgetqualinfo(int *qualcode, char *chemname, char *chemunits, + int *tracenode) { + return EN_getqualinfo(_defaultModel, qualcode, chemname, chemunits, + tracenode); +} +int DLLEXPORT ENgeterror(int errcode, char *errmsg, int n) { + return EN_geterror(errcode, errmsg, n); +} +int DLLEXPORT ENgetstatistic(int code, EN_API_FLOAT_TYPE *value) { + return EN_getstatistic(_defaultModel, code, value); +} +int DLLEXPORT ENgetnodeindex(char *id, int *index) { + return EN_getnodeindex(_defaultModel, id, index); +} +int DLLEXPORT ENgetnodeid(int index, char *id) { + return EN_getnodeid(_defaultModel, index, id); +} +int DLLEXPORT ENgetnodetype(int index, int *code) { + return EN_getnodetype(_defaultModel, index, code); +} +int DLLEXPORT ENgetcoord(int index, EN_API_FLOAT_TYPE *x, + EN_API_FLOAT_TYPE *y) { + return EN_getcoord(_defaultModel, index, x, y); +} +int DLLEXPORT ENsetcoord(int index, EN_API_FLOAT_TYPE x, EN_API_FLOAT_TYPE y) { + return EN_setcoord(_defaultModel, index, x, y); +} +int DLLEXPORT ENgetnodevalue(int index, int code, EN_API_FLOAT_TYPE *value) { + return EN_getnodevalue(_defaultModel, index, code, value); +} +int DLLEXPORT ENgetlinkindex(char *id, int *index) { + return EN_getlinkindex(_defaultModel, id, index); +} +int DLLEXPORT ENgetlinkid(int index, char *id) { + return EN_getlinkid(_defaultModel, index, id); +} +int DLLEXPORT ENgetlinktype(int index, EN_LinkType *code) { + return EN_getlinktype(_defaultModel, index, code); +} +int DLLEXPORT ENgetlinknodes(int index, int *node1, int *node2) { + return EN_getlinknodes(_defaultModel, index, node1, node2); +} +int DLLEXPORT ENgetlinkvalue(int index, int code, EN_API_FLOAT_TYPE *value) { + return EN_getlinkvalue(_defaultModel, index, (EN_LinkProperty)code, value); +} +int DLLEXPORT ENgetcurve(int curveIndex, char *id, int *nValues, + EN_API_FLOAT_TYPE **xValues, + EN_API_FLOAT_TYPE **yValues) { + return EN_getcurve(_defaultModel, curveIndex, id, nValues, xValues, yValues); +} +int DLLEXPORT ENsetcontrol(int cindex, int ctype, int lindex, + EN_API_FLOAT_TYPE setting, int nindex, + EN_API_FLOAT_TYPE level) { + return EN_setcontrol(_defaultModel, cindex, ctype, lindex, setting, nindex, + level); +} +int DLLEXPORT ENsetnodevalue(int index, int code, EN_API_FLOAT_TYPE v) { + return EN_setnodevalue(_defaultModel, index, code, v); +} +int DLLEXPORT ENsetlinkvalue(int index, int code, EN_API_FLOAT_TYPE v) { + return EN_setlinkvalue(_defaultModel, index, code, v); +} +int DLLEXPORT ENaddpattern(char *id) { + return EN_addpattern(_defaultModel, id); +} +int DLLEXPORT ENsetpattern(int index, EN_API_FLOAT_TYPE *f, int n) { + return EN_setpattern(_defaultModel, index, f, n); +} +int DLLEXPORT ENsetpatternvalue(int index, int period, + EN_API_FLOAT_TYPE value) { + return EN_setpatternvalue(_defaultModel, index, period, value); +} +int DLLEXPORT ENaddcurve(char *id) { return EN_addcurve(_defaultModel, id); } +int DLLEXPORT ENsetcurve(int index, EN_API_FLOAT_TYPE *x, EN_API_FLOAT_TYPE *y, + int n) { + return EN_setcurve(_defaultModel, index, x, y, n); +} +int DLLEXPORT ENsetcurvevalue(int index, int pnt, EN_API_FLOAT_TYPE x, + EN_API_FLOAT_TYPE y) { + return EN_setcurvevalue(_defaultModel, index, pnt, x, y); +} +int DLLEXPORT ENsettimeparam(int code, long value) { + return EN_settimeparam(_defaultModel, code, value); +} +int DLLEXPORT ENsetoption(int code, EN_API_FLOAT_TYPE v) { + return EN_setoption(_defaultModel, code, v); +} +int DLLEXPORT ENsetstatusreport(int code) { + return EN_setstatusreport(_defaultModel, code); +} +int DLLEXPORT ENsetqualtype(int qualcode, char *chemname, char *chemunits, + char *tracenode) { + return EN_setqualtype(_defaultModel, qualcode, chemname, chemunits, + tracenode); +} +int DLLEXPORT ENgetheadcurveindex(int index, int *curveindex) { + return EN_getheadcurveindex(_defaultModel, index, curveindex); +} +int DLLEXPORT ENsetheadcurveindex(int index, int curveindex) { + return EN_setheadcurveindex(_defaultModel, index, curveindex); +} +int DLLEXPORT ENgetpumptype(int index, int *type) { + return EN_getpumptype(_defaultModel, index, type); +} +int DLLEXPORT ENgetnumdemands(int nodeIndex, int *numDemands) { + return EN_getnumdemands(_defaultModel, nodeIndex, numDemands); +} +int DLLEXPORT ENgetbasedemand(int nodeIndex, int demandIdx, + EN_API_FLOAT_TYPE *baseDemand) { + return EN_getbasedemand(_defaultModel, nodeIndex, demandIdx, baseDemand); +} +int DLLEXPORT ENsetbasedemand(int nodeIndex, int demandIdx, + EN_API_FLOAT_TYPE baseDemand) { + return EN_setbasedemand(_defaultModel, nodeIndex, demandIdx, baseDemand); +} +int DLLEXPORT ENgetdemandpattern(int nodeIndex, int demandIdx, int *pattIdx) { + return EN_getdemandpattern(_defaultModel, nodeIndex, demandIdx, pattIdx); +} +int DLLEXPORT ENgetaveragepatternvalue(int index, EN_API_FLOAT_TYPE *value) { + return EN_getaveragepatternvalue(_defaultModel, index, value); +} + +int DLLEXPORT ENgetrule(int index, int *nPremises, int *nTrueActions, int *nFalseActions, EN_API_FLOAT_TYPE *priority) { + return EN_getrule(_defaultModel, index, nPremises, nTrueActions, nFalseActions, priority); +} + +int DLLEXPORT ENsetrulepriority(int index, EN_API_FLOAT_TYPE priority){ + return EN_setrulepriority(_defaultModel, index, priority); +} + +int DLLEXPORT ENgetpremise(int indexRule, int indexPremise, int *logop, int *object, int *indexObj, int *variable, int *relop, int *status, EN_API_FLOAT_TYPE *value){ + return EN_getpremise(_defaultModel, indexRule, indexPremise, logop, object, indexObj, variable, relop, status, value); +} + +int DLLEXPORT ENsetpremise(int indexRule, int indexPremise, int logop, int object, int indexObj, int variable, int relop, int status, EN_API_FLOAT_TYPE value){ + return EN_setpremise(_defaultModel, indexRule, indexPremise, logop, object, indexObj, variable, relop, status, value); +} + +int DLLEXPORT ENsetpremiseindex(int indexRule, int indexPremise, int indexObj){ + return EN_setpremiseindex(_defaultModel, indexRule, indexPremise, indexObj); +} + +int DLLEXPORT ENsetpremisestatus(int indexRule, int indexPremise, int status){ + return EN_setpremisestatus(_defaultModel, indexRule, indexPremise, status); +} + +int DLLEXPORT ENsetpremisevalue(int indexRule, int indexPremise, EN_API_FLOAT_TYPE value){ + return EN_setpremisevalue(_defaultModel, indexRule, indexPremise, value); +} + +int DLLEXPORT ENgettrueaction(int indexRule, int indexAction, int *indexLink, int *status, EN_API_FLOAT_TYPE *setting){ + return EN_gettrueaction(_defaultModel, indexRule, indexAction, indexLink, status, setting); +} + +int DLLEXPORT ENsettrueaction(int indexRule, int indexAction, int indexLink, int status, EN_API_FLOAT_TYPE setting){ + return EN_settrueaction(_defaultModel, indexRule, indexAction, indexLink, status, setting); +} + +int DLLEXPORT ENgetfalseaction(int indexRule, int indexAction, int *indexLink, int *status, EN_API_FLOAT_TYPE *setting){ + return EN_getfalseaction(_defaultModel, indexRule, indexAction, indexLink, status, setting); +} + +int DLLEXPORT ENsetfalseaction(int indexRule, int indexAction, int indexLink, int status, EN_API_FLOAT_TYPE setting){ + return EN_setfalseaction(_defaultModel, indexRule, indexAction, indexLink, status, setting); +} + +int DLLEXPORT ENgetruleID(int indexRule, char* id){ + return EN_getruleID(_defaultModel, indexRule, id); +} + +int DLLEXPORT ENsetlinktype(char *id, EN_LinkType toType) { + return EN_setlinktype(_defaultModel, id, toType); +} +int DLLEXPORT ENaddnode(char *id, EN_NodeType nodeType) { + return EN_addnode(_defaultModel, id, nodeType); +} +int DLLEXPORT ENaddlink(char *id, EN_LinkType linkType, char *fromNode, + char *toNode) { + return EN_addlink(_defaultModel, id, linkType, fromNode, toNode); +} +int DLLEXPORT ENdeletelink(int index) { + return EN_deletelink(_defaultModel, index); +} +int DLLEXPORT ENdeletenode(int index) { + return EN_deletenode(_defaultModel, index); +} + +/* +---------------------------------------------------------------- + Functions for opening & closing the EPANET system +---------------------------------------------------------------- +*/ + +/// allocate a project pointer +int DLLEXPORT EN_alloc(EN_Project **p) +{ + EN_Project *project = calloc(1, sizeof(EN_Project)); + *p = project; + return 0; +} + +int DLLEXPORT EN_free(EN_Project *p) +{ + free(p); + return 0; +} + +int DLLEXPORT EN_init(EN_Project *pr, char *f2, char *f3, + EN_FlowUnits UnitsType, EN_FormType HeadlossFormula) +/*---------------------------------------------------------------- + ** Input: + ** f2 = pointer to name of report file + ** f3 = pointer to name of binary output file + UnitsType = flow units flag + HeadlossFormula = headloss formula flag + + ** Output: none + ** Returns: error code + ** Purpose: opens EPANET + **---------------------------------------------------------------- + */ +{ + int errcode = 0; +/*** Updated 9/7/00 ***/ +/* Reset math coprocessor */ +#ifdef DLL + _fpreset(); +#endif + + /* Set system flags */ + pr->Openflag = TRUE; + pr->hydraulics.OpenHflag = FALSE; + pr->quality.OpenQflag = FALSE; + pr->save_options.SaveHflag = FALSE; + pr->save_options.SaveQflag = FALSE; + pr->Warnflag = FALSE; + pr->parser.Coordflag = TRUE; + + /*** Updated 9/7/00 ***/ + pr->report.Messageflag = TRUE; + pr->report.Rptflag = 1; + + /* Initialize global pointers to NULL. */ + initpointers(pr); + + ERRCODE(netsize(pr)); + ERRCODE(allocdata(pr)); + + setdefaults(pr); + + pr->parser.Flowflag = UnitsType; + pr->hydraulics.Formflag = HeadlossFormula; + + adjustdata(pr); + initreport(&pr->report); + initunits(pr); + inittanks(pr); + convertunits(pr); + + pr->parser.MaxPats = 0; + // initialize default pattern + getpatterns(pr); + + return (errcode); +} + +int DLLEXPORT EN_open(EN_Project *p, char *f1, char *f2, char *f3) +/*---------------------------------------------------------------- + ** Input: f1 = pointer to name of input file + ** f2 = pointer to name of report file + ** f3 = pointer to name of binary output file + ** Output: none + ** Returns: error code + ** Purpose: opens EPANET input file & reads in network data + **---------------------------------------------------------------- + */ +{ + int errcode = 0; + +/*** Updated 9/7/00 ***/ +/* Reset math coprocessor */ +#ifdef DLL + _fpreset(); +#endif + + /* Set system flags */ + p->Openflag = FALSE; + p->hydraulics.OpenHflag = FALSE; + p->quality.OpenQflag = FALSE; + p->save_options.SaveHflag = FALSE; + p->save_options.SaveQflag = FALSE; + p->Warnflag = FALSE; + p->parser.Coordflag = TRUE; + + /*** Updated 9/7/00 ***/ + p->report.Messageflag = TRUE; + p->report.Rptflag = 1; + + /* Initialize global pointers to NULL. */ + initpointers(p); + + /* Open input & report files */ + ERRCODE(openfiles(p, f1, f2, f3)); + if (errcode > 0) { + errmsg(p, errcode); + return (errcode); + } + writelogo(p); + + /* Find network size & allocate memory for data */ + writecon(FMT02); + writewin(p->viewprog, FMT100); + ERRCODE(netsize(p)); + ERRCODE(allocdata(p)); + + /* Retrieve input data */ + ERRCODE(getdata(p)); + + /* Free temporary linked lists used for Patterns & Curves */ + freeTmplist(p->parser.Patlist); + freeTmplist(p->parser.Curvelist); + + /* If using previously saved hydraulics then open its file */ + if (p->out_files.Hydflag == USE) { + ERRCODE(openhydfile(p)); + } + + /* Write input summary to report file */ + if (!errcode) { + if (p->report.Summaryflag) { + writesummary(p); + } + writetime(p, FMT104); + p->Openflag = TRUE; + } else + errmsg(p, errcode); + return (errcode); +} + +int DLLEXPORT EN_saveinpfile(EN_Project *p, char *filename) +/*---------------------------------------------------------------- + ** Input: filename = name of INP file + ** Output: none + ** Returns: error code + ** Purpose: saves current data base to file + **---------------------------------------------------------------- + */ +{ + if (!p->Openflag) + return (102); + return (saveinpfile(p, filename)); +} + +int DLLEXPORT EN_close(EN_Project *p) +/*---------------------------------------------------------------- + ** Input: none + ** Output: none + ** Returns: error code + ** Purpose: frees all memory & files used by EPANET + **---------------------------------------------------------------- + */ +{ + out_file_t *out; + + if (p->Openflag) { + writetime(p, FMT105); + } + freedata(p); + + out = &p->out_files; + + if (out->TmpOutFile != out->OutFile) { + if (out->TmpOutFile != NULL) { + fclose(out->TmpOutFile); + } + remove(out->TmpFname); + } + out->TmpOutFile = NULL; + + if (p->parser.InFile != NULL) { + fclose(p->parser.InFile); + p->parser.InFile = NULL; + } + if (p->report.RptFile != NULL && p->report.RptFile != stdout) { + fclose(p->report.RptFile); + p->report.RptFile = NULL; + } + if (out->HydFile != NULL) { + fclose(out->HydFile); + out->HydFile = NULL; + } + if (out->OutFile != NULL) { + fclose(out->OutFile); + out->OutFile = NULL; + } + + if (out->Hydflag == SCRATCH) + remove(out->HydFname); + if (out->Outflag == SCRATCH) + remove(out->OutFname); + + p->Openflag = FALSE; + p->hydraulics.OpenHflag = FALSE; + p->save_options.SaveHflag = FALSE; + p->quality.OpenQflag = FALSE; + p->save_options.SaveQflag = FALSE; + return (0); +} + +/* + ---------------------------------------------------------------- + Functions for running a hydraulic analysis + ---------------------------------------------------------------- + */ + +int DLLEXPORT EN_solveH(EN_Project *p) +/*---------------------------------------------------------------- + ** Input: none + ** Output: none + ** Returns: error code + ** Purpose: solves for network hydraulics in all time periods + **---------------------------------------------------------------- + */ +{ + int errcode; + long t, tstep; + + /* Open hydraulics solver */ + errcode = EN_openH(p); + if (!errcode) { + /* Initialize hydraulics */ + errcode = EN_initH(p, EN_SAVE); + writecon(FMT14); + + /* Analyze each hydraulic period */ + if (!errcode) + do { + + /* Display progress message */ + + /*** Updated 6/24/02 ***/ + sprintf(p->Msg, "%-10s", + clocktime(p->report.Atime, p->time_options.Htime)); + + writecon(p->Msg); + sprintf(p->Msg, FMT101, p->report.Atime); + writewin(p->viewprog, p->Msg); + + /* Solve for hydraulics & advance to next time period */ + tstep = 0; + ERRCODE(EN_runH(p, &t)); + ERRCODE(EN_nextH(p, &tstep)); + + /*** Updated 6/24/02 ***/ + writecon("\b\b\b\b\b\b\b\b\b\b"); + } while (tstep > 0); + } + + /* Close hydraulics solver */ + + /*** Updated 6/24/02 ***/ + writecon("\b\b\b\b\b\b\b\b "); + + EN_closeH(p); + errcode = MAX(errcode, p->Warnflag); + return (errcode); +} + +int DLLEXPORT EN_saveH(EN_Project *p) +/*---------------------------------------------------------------- + ** Input: none + ** Output: none + ** Returns: error code + ** Purpose: saves hydraulic results to binary file. + ** + ** Must be called before ENreport() if no WQ simulation made. + ** Should not be called if ENsolveQ() will be used. + **---------------------------------------------------------------- + */ +{ + char tmpflag; + int errcode; + + /* Check if hydraulic results exist */ + if (!p->save_options.SaveHflag) + return (104); + + /* Temporarily turn off WQ analysis */ + tmpflag = p->quality.Qualflag; + p->quality.Qualflag = NONE; + + /* Call WQ solver to simply transfer results */ + /* from Hydraulics file to Output file at */ + /* fixed length reporting time intervals. */ + errcode = EN_solveQ(p); + + /* Restore WQ analysis option */ + p->quality.Qualflag = tmpflag; + if (errcode) { + errmsg(p, errcode); + } + return (errcode); +} + +int DLLEXPORT EN_openH(EN_Project *p) +/*---------------------------------------------------------------- + ** Input: none + ** Output: none + ** Returns: error code + ** Purpose: sets up data structures for hydraulic analysis + **---------------------------------------------------------------- + */ +{ + int errcode = 0; + + /* Check that input data exists */ + p->hydraulics.OpenHflag = FALSE; + p->save_options.SaveHflag = FALSE; + if (!p->Openflag) { + return (102); + } + + /* Check that previously saved hydraulics file not in use */ + if (p->out_files.Hydflag == USE) { + return (107); + } + + /* Open hydraulics solver */ + ERRCODE(openhyd(p)); + if (!errcode) + p->hydraulics.OpenHflag = TRUE; + else + errmsg(p, errcode); + return (errcode); +} + +/*** Updated 3/1/01 ***/ +int DLLEXPORT EN_initH(EN_Project *p, int flag) +/*---------------------------------------------------------------- + ** Input: flag = 2-digit flag where 1st (left) digit indicates + ** if link flows should be re-initialized (1) or + ** not (0) and 2nd digit indicates if hydraulic + ** results should be saved to file (1) or not (0) + ** Output: none + ** Returns: error code + ** Purpose: initializes hydraulic analysis + **---------------------------------------------------------------- + */ +{ + int errcode = 0; + int sflag, fflag; + + /* Reset status flags */ + p->save_options.SaveHflag = FALSE; + p->Warnflag = FALSE; + + /* Get values of save-to-file flag and reinitialize-flows flag */ + fflag = flag / EN_INITFLOW; + sflag = flag - fflag * EN_INITFLOW; + + /* Check that hydraulics solver was opened */ + if (!p->hydraulics.OpenHflag) + return (103); + + /* Open hydraulics file */ + p->save_options.Saveflag = FALSE; + if (sflag > 0) { + errcode = openhydfile(p); + if (!errcode) + p->save_options.Saveflag = TRUE; + else + errmsg(p, errcode); + } + + /* Initialize hydraulics */ + inithyd(p, fflag); + if (p->report.Statflag > 0) + writeheader(p, STATHDR, 0); + return (errcode); +} + +int DLLEXPORT EN_runH(EN_Project *p, long *t) { + int errcode; + *t = 0; + if (!p->hydraulics.OpenHflag) + return (103); + errcode = runhyd(p, t); + if (errcode) + errmsg(p, errcode); + return (errcode); +} + +int DLLEXPORT EN_nextH(EN_Project *p, long *tstep) { + int errcode; + *tstep = 0; + if (!p->hydraulics.OpenHflag) + return (103); + errcode = nexthyd(p, tstep); + if (errcode) + errmsg(p, errcode); + else if (p->save_options.Saveflag && *tstep == 0) + p->save_options.SaveHflag = TRUE; + return (errcode); +} + +int DLLEXPORT EN_closeH(EN_Project *p) +{ + if (!p->Openflag) { + return (102); + } + if (p->hydraulics.OpenHflag) { + closehyd(p); + } + p->hydraulics.OpenHflag = FALSE; + return (0); +} + +int DLLEXPORT EN_savehydfile(EN_Project *p, char *filename) { + FILE *f; + int c; + FILE *HydFile; + + /* Check that hydraulics results exist */ + if (p->out_files.HydFile == NULL || !p->save_options.SaveHflag) + return (104); + + /* Open file */ + if ((f = fopen(filename, "w+b")) == NULL) + return (305); + + /* Copy from HydFile to f */ + HydFile = p->out_files.HydFile; + fseek(HydFile, 0, SEEK_SET); + while ((c = fgetc(HydFile)) != EOF) { + fputc(c, f); + } + fclose(f); + return (0); +} + +int DLLEXPORT EN_usehydfile(EN_Project *p, char *filename) { + int errcode; + + /* Check that input data exists & hydraulics system closed */ + if (!p->Openflag) + return (102); + if (p->hydraulics.OpenHflag) + return (108); + + /* Try to open hydraulics file */ + strncpy(p->out_files.HydFname, filename, MAXFNAME); + p->out_files.Hydflag = USE; + p->save_options.SaveHflag = TRUE; + errcode = openhydfile(p); + + /* If error, then reset flags */ + if (errcode) { + strcpy(p->out_files.HydFname, ""); + p->out_files.Hydflag = SCRATCH; + p->save_options.SaveHflag = FALSE; + } + return (errcode); +} + +/* + ---------------------------------------------------------------- + Functions for running a WQ analysis + ---------------------------------------------------------------- + */ + +int DLLEXPORT EN_solveQ(EN_Project *p) { + int errcode; + long t, tstep; + + /* Open WQ solver */ + errcode = EN_openQ(p); + if (!errcode) { + /* Initialize WQ */ + errcode = EN_initQ(p, EN_SAVE); + if (p->quality.Qualflag) + writecon(FMT15); + else { + writecon(FMT16); + writewin(p->viewprog, FMT103); + } + + /* Analyze each hydraulic period */ + if (!errcode) + do { + + /* Display progress message */ + + /*** Updated 6/24/02 ***/ + sprintf(p->Msg, "%-10s", + clocktime(p->report.Atime, p->time_options.Htime)); + + writecon(p->Msg); + if (p->quality.Qualflag) { + sprintf(p->Msg, FMT102, p->report.Atime); + writewin(p->viewprog, p->Msg); + } + + /* Retrieve current network solution & update WQ to next time period */ + tstep = 0; + ERRCODE(EN_runQ(p, &t)); + ERRCODE(EN_nextQ(p, &tstep)); + + /*** Updated 6/24/02 ***/ + writecon("\b\b\b\b\b\b\b\b\b\b"); + + } while (tstep > 0); + } + + /* Close WQ solver */ + + /*** Updated 6/24/02 ***/ + writecon("\b\b\b\b\b\b\b\b "); + EN_closeQ(p); + return (errcode); +} + +int DLLEXPORT EN_openQ(EN_Project *p) { + int errcode = 0; + + /* Check that hydraulics results exist */ + p->quality.OpenQflag = FALSE; + p->save_options.SaveQflag = FALSE; + if (!p->Openflag) + return (102); + // !LT! todo - check for p->save_options.SaveHflag / set sequential/step mode + // if (!p->save_options.SaveHflag) return(104); + + /* Open WQ solver */ + ERRCODE(openqual(p)); + if (!errcode) + p->quality.OpenQflag = TRUE; + else + errmsg(p, errcode); + return (errcode); +} + +int DLLEXPORT EN_initQ(EN_Project *p, int saveflag) { + int errcode = 0; + if (!p->quality.OpenQflag) + return (105); + initqual(p); + p->save_options.SaveQflag = FALSE; + p->save_options.Saveflag = FALSE; + if (saveflag) { + errcode = openoutfile(p); + if (!errcode) + p->save_options.Saveflag = TRUE; + } + return (errcode); +} + +int DLLEXPORT EN_runQ(EN_Project *p, long *t) { + int errcode; + *t = 0; + if (!p->quality.OpenQflag) + return (105); + errcode = runqual(p, t); + if (errcode) + errmsg(p, errcode); + return (errcode); +} + +int DLLEXPORT EN_nextQ(EN_Project *p, long *tstep) { + int errcode; + *tstep = 0; + if (!p->quality.OpenQflag) + return (105); + errcode = nextqual(p, tstep); + if (!errcode && p->save_options.Saveflag && *tstep == 0) { + p->save_options.SaveQflag = TRUE; + } + if (errcode) + errmsg(p, errcode); + return (errcode); +} + +int DLLEXPORT EN_stepQ(EN_Project *p, long *tleft) { + int errcode; + *tleft = 0; + if (!p->quality.OpenQflag) + return (105); + errcode = stepqual(p, tleft); + if (!errcode && p->save_options.Saveflag && *tleft == 0) { + p->save_options.SaveQflag = TRUE; + } + if (errcode) + errmsg(p, errcode); + return (errcode); +} + +int DLLEXPORT EN_closeQ(EN_Project *p) + +{ + if (!p->Openflag) + return (102); + closequal(p); + p->quality.OpenQflag = FALSE; + return (0); +} + +/* + ---------------------------------------------------------------- + Functions for generating an output report + ---------------------------------------------------------------- + */ + +int DLLEXPORT EN_writeline(EN_Project *p, char *line) { + if (!p->Openflag) + return (102); + writeline(p, line); + return (0); +} + +int DLLEXPORT EN_report(EN_Project *p) { + int errcode; + + /* Check if results saved to binary output file */ + if (!p->save_options.SaveQflag) + return (106); + errcode = writereport(p); + if (errcode) + errmsg(p, errcode); + return (errcode); +} + +int DLLEXPORT EN_resetreport(EN_Project *p) { + int i; + if (!p->Openflag) + return (102); + initreport(&p->report); + for (i = 1; i <= p->network.Nnodes; i++) + p->network.Node[i].Rpt = 0; + for (i = 1; i <= p->network.Nlinks; i++) + p->network.Link[i].Rpt = 0; + return (0); +} + +int DLLEXPORT EN_setreport(EN_Project *p, char *s) { + char s1[MAXLINE + 1]; + if (!p->Openflag) + return (102); + if (strlen(s) > MAXLINE) + return (250); + strcpy(s1, s); + if (setreport(p, s1) > 0) + return (250); + else + return (0); +} + +/* + ---------------------------------------------------------------- + Functions for retrieving network information + ---------------------------------------------------------------- + */ + +/*** Updated 10/25/00 ***/ +int DLLEXPORT EN_getversion(int *v) +/*---------------------------------------------------------------- + ** Input: none + ** Output: *v = version number of the source code + ** Returns: error code (should always be 0) + ** Purpose: retrieves a number assigned to the most recent + ** update of the source code. This number, set by the + ** constant CODEVERSION found in TYPES.H, is to be + ** interpreted with implied decimals, i.e., "20100" == "2(.)01(.)00" + **---------------------------------------------------------------- + */ +{ + *v = CODEVERSION; + return (0); +} + +int DLLEXPORT EN_getcontrol(EN_Project *pr, int cindex, int *ctype, int *lindex, + EN_API_FLOAT_TYPE *setting, int *nindex, + EN_API_FLOAT_TYPE *level) { + double s, lvl; + + EN_Network *net = &pr->network; + + Scontrol *Control = net->Control; + Snode *Node = net->Node; + Slink *Link = net->Link; + + const int Njuncs = net->Njuncs; + double *Ucf = pr->Ucf; + + s = 0.0; + lvl = 0.0; + *ctype = 0; + *lindex = 0; + *nindex = 0; + if (!pr->Openflag) + return (102); + if (cindex < 1 || cindex > net->Ncontrols) + return (241); + *ctype = Control[cindex].Type; + *lindex = Control[cindex].Link; + s = Control[cindex].Setting; + if (Control[cindex].Setting != MISSING) { + switch (Link[*lindex].Type) { + case EN_PRV: + case EN_PSV: + case EN_PBV: + s *= Ucf[PRESSURE]; + break; + case EN_FCV: + s *= Ucf[FLOW]; + default: + break; + } + } else if (Control[cindex].Status == OPEN) { + s = 1.0; + } + + /*** Updated 3/1/01 ***/ + else + s = 0.0; + + *nindex = Control[cindex].Node; + if (*nindex > Njuncs) + lvl = (Control[cindex].Grade - Node[*nindex].El) * Ucf[ELEV]; + else if (*nindex > 0) + lvl = (Control[cindex].Grade - Node[*nindex].El) * Ucf[PRESSURE]; + else + lvl = (EN_API_FLOAT_TYPE)Control[cindex].Time; + *setting = (EN_API_FLOAT_TYPE)s; + *level = (EN_API_FLOAT_TYPE)lvl; + return (0); +} + +int DLLEXPORT EN_getcount(EN_Project *pr, EN_CountType code, int *count) { + EN_Network *net = &pr->network; + + *count = 0; + if (!pr->Openflag) + return (102); + switch (code) { + case EN_NODECOUNT: + *count = net->Nnodes; + break; + case EN_TANKCOUNT: + *count = net->Ntanks; + break; + case EN_LINKCOUNT: + *count = net->Nlinks; + break; + case EN_PATCOUNT: + *count = net->Npats; + break; + case EN_CURVECOUNT: + *count = net->Ncurves; + break; + case EN_CONTROLCOUNT: + *count = net->Ncontrols; + break; + case EN_RULECOUNT: + *count = net->Nrules; + break; + default: + return (251); + } + return (0); +} + +int DLLEXPORT EN_getoption(EN_Project *pr, EN_Option code, + EN_API_FLOAT_TYPE *value) { + hydraulics_t *hyd = &pr->hydraulics; + quality_t *qu = &pr->quality; + double *Ucf = pr->Ucf; + + double v = 0.0; + *value = 0.0; + if (!pr->Openflag) + return (102); + switch (code) { + case EN_TRIALS: + v = (double)hyd->MaxIter; + break; + case EN_ACCURACY: + v = hyd->Hacc; + break; + case EN_TOLERANCE: + v = qu->Ctol * Ucf[QUALITY]; + break; + case EN_EMITEXPON: + if (hyd->Qexp > 0.0) + v = 1.0 / hyd->Qexp; + break; + case EN_DEMANDMULT: + v = hyd->Dmult; + break; + default: + return (251); + } + *value = (EN_API_FLOAT_TYPE)v; + return (0); +} + +int DLLEXPORT EN_gettimeparam(EN_Project *pr, int code, long *value) { + + int i; + report_options_t *rep = &pr->report; + quality_t *qu = &pr->quality; + time_options_t *time = &pr->time_options; + + + *value = 0; + if (!pr->Openflag) + return (102); + if (code < EN_DURATION || code > EN_NEXTEVENTIDX) + return (251); + switch (code) { + case EN_DURATION: + *value = time->Dur; + break; + case EN_HYDSTEP: + *value = time->Hstep; + break; + case EN_QUALSTEP: + *value = qu->Qstep; + break; + case EN_PATTERNSTEP: + *value = time->Pstep; + break; + case EN_PATTERNSTART: + *value = time->Pstart; + break; + case EN_REPORTSTEP: + *value = time->Rstep; + break; + case EN_REPORTSTART: + *value = time->Rstart; + break; + case EN_STATISTIC: + *value = rep->Tstatflag; + break; + case EN_RULESTEP: + *value = time->Rulestep; + break; + case EN_PERIODS: + *value = rep->Nperiods; + break; + case EN_STARTTIME: + *value = time->Tstart; + break; /* Added TNT 10/2/2009 */ + case EN_HTIME: + *value = time->Htime; + break; + case EN_NEXTEVENT: + *value = time->Hstep; // find the lesser of the hydraulic time step length, or the + // time to next fill/empty + tanktimestep(pr,value); + break; + case EN_NEXTEVENTIDX: + *value = time->Hstep; + i = tanktimestep(pr, value); + *value = i; + break; + } + return (0); +} + +int DLLEXPORT EN_getflowunits(EN_Project *p, int *code) { + *code = -1; + if (!p->Openflag) + return (102); + *code = p->parser.Flowflag; + return (0); +} + +int DLLEXPORT EN_getpatternindex(EN_Project *p, char *id, int *index) { + int i; + *index = 0; + if (!p->Openflag) + return (102); + for (i = 1; i <= p->network.Npats; i++) { + if (strcmp(id, p->network.Pattern[i].ID) == 0) { + *index = i; + return (0); + } + } + *index = 0; + return (205); +} + +int DLLEXPORT EN_getpatternid(EN_Project *p, int index, char *id) { + strcpy(id, ""); + if (!p->Openflag) + return (102); + if (index < 1 || index > p->network.Npats) + return (205); + strcpy(id, p->network.Pattern[index].ID); + return (0); +} + +int DLLEXPORT EN_getpatternlen(EN_Project *p, int index, int *len) { + if (!p->Openflag) + return (102); + if (index < 1 || index > p->network.Npats) + return (205); + *len = p->network.Pattern[index].Length; + return (0); +} + +int DLLEXPORT EN_getpatternvalue(EN_Project *p, int index, int period, + EN_API_FLOAT_TYPE *value) { + *value = 0.0; + if (!p->Openflag) + return (102); + if (index < 1 || index > p->network.Npats) + return (205); + if (period < 1 || period > p->network.Pattern[index].Length) + return (251); + *value = (EN_API_FLOAT_TYPE)p->network.Pattern[index].F[period - 1]; + return (0); +} + +int DLLEXPORT EN_getcurveindex(EN_Project *p, char *id, int *index) { + int i; + *index = 0; + if (!p->Openflag) + return (102); + for (i = 1; i <= p->network.Ncurves; i++) { + if (strcmp(id, p->network.Curve[i].ID) == 0) { + *index = i; + return (0); + } + } + *index = 0; + return (206); +} + +int DLLEXPORT EN_getcurveid(EN_Project *p, int index, char *id) { + strcpy(id, ""); + if (!p->Openflag) + return (102); + if (index < 1 || index > p->network.Ncurves) + return (206); + strcpy(id, p->network.Curve[index].ID); + return (0); +} + +int DLLEXPORT EN_getcurvelen(EN_Project *p, int index, int *len) { + if (!p->Openflag) + return (102); + if (index < 1 || index > p->network.Ncurves) + return (206); + *len = p->network.Curve[index].Npts; + return (0); +} + +int DLLEXPORT EN_getcurvevalue(EN_Project *p, int index, int pnt, + EN_API_FLOAT_TYPE *x, EN_API_FLOAT_TYPE *y) { + *x = 0.0; + *y = 0.0; + if (!p->Openflag) + return (102); + if (index < 1 || index > p->network.Ncurves) + return (206); + if (pnt < 1 || pnt > p->network.Curve[index].Npts) + return (251); + *x = (EN_API_FLOAT_TYPE)p->network.Curve[index].X[pnt - 1]; + *y = (EN_API_FLOAT_TYPE)p->network.Curve[index].Y[pnt - 1]; + return (0); +} + +int DLLEXPORT EN_getqualtype(EN_Project *p, int *qualcode, int *tracenode) { + *tracenode = 0; + if (!p->Openflag) + return (102); + *qualcode = p->quality.Qualflag; + if (p->quality.Qualflag == TRACE) + *tracenode = p->quality.TraceNode; + return (0); +} + +int DLLEXPORT EN_getqualinfo(EN_Project *p, int *qualcode, char *chemname, char *chemunits, int *tracenode) { + ENgetqualtype(qualcode, tracenode); + if (p->quality.Qualflag == TRACE) { + strncpy(chemname, "", MAXID); + strncpy(chemunits, "dimensionless", MAXID); + } else { + strncpy(chemname, p->quality.ChemName, MAXID); + strncpy(chemunits, p->quality.ChemUnits, MAXID); + } + return 0; +} + +int DLLEXPORT EN_geterror(int errcode, char *errmsg, int n) { + char newMsg[MAXMSG+1]; + + switch (errcode) { + case 1: + strncpy(errmsg, WARN1, n); + break; + case 2: + strncpy(errmsg, WARN2, n); + break; + case 3: + strncpy(errmsg, WARN3, n); + break; + case 4: + strncpy(errmsg, WARN4, n); + break; + case 5: + strncpy(errmsg, WARN5, n); + break; + case 6: + strncpy(errmsg, WARN6, n); + break; + default: + geterrmsg(n, newMsg); + strncpy(errmsg, newMsg, n); + } + if (strlen(errmsg) == 0) + return (251); + else + return (0); +} + +int DLLEXPORT EN_getstatistic(EN_Project *p, int code, EN_API_FLOAT_TYPE *value) { + switch (code) { + case EN_ITERATIONS: + *value = (EN_API_FLOAT_TYPE)p->hydraulics.iterations; + break; + case EN_RELATIVEERROR: + *value = (EN_API_FLOAT_TYPE)p->hydraulics.relativeError; + break; + default: + break; + } + return 0; +} + +/* + ---------------------------------------------------------------- + Functions for retrieving node data + ---------------------------------------------------------------- + */ + +int DLLEXPORT EN_getnodeindex(EN_Project *p, char *id, int *index) { + *index = 0; + if (!p->Openflag) + return (102); + *index = findnode(&p->network,id); + if (*index == 0) + return (203); + else + return (0); +} + +int DLLEXPORT EN_getnodeid(EN_Project *p, int index, char *id) { + strcpy(id, ""); + if (!p->Openflag) + return (102); + if (index < 1 || index > p->network.Nnodes) + return (203); + strcpy(id, p->network.Node[index].ID); + return (0); +} + +int DLLEXPORT EN_getnodetype(EN_Project *p, int index, int *code) { + *code = -1; + if (!p->Openflag) + return (102); + if (index < 1 || index > p->network.Nnodes) + return (203); + if (index <= p->network.Njuncs) + *code = EN_JUNCTION; + else { + if (p->network.Tank[index - p->network.Njuncs].A == 0.0) + *code = EN_RESERVOIR; + else + *code = EN_TANK; + } + return (0); +} + +int DLLEXPORT EN_getcoord(EN_Project *p, int index, EN_API_FLOAT_TYPE *x, + EN_API_FLOAT_TYPE *y) { + if (!p->Openflag) + return (102); + if (index < 1 || index > p->network.Nnodes) + return (203); + if (!p->parser.Coordflag) + return (255); + + // check if node have coords + if (p->network.Coord[index].HaveCoords == FALSE) + return (254); + + *x = p->network.Coord[index].X; + *y = p->network.Coord[index].Y; + return 0; +} + +int DLLEXPORT EN_setcoord(EN_Project *p, int index, EN_API_FLOAT_TYPE x, + EN_API_FLOAT_TYPE y) { + if (!p->Openflag) + return (102); + if (index < 1 || index > p->network.Nnodes) + return (203); + if (!p->parser.Coordflag) + return (255); + + p->network.Coord[index].X = x; + p->network.Coord[index].Y = y; + p->network.Coord[index].HaveCoords = TRUE; + return 0; +} + +int DLLEXPORT EN_getnodevalue(EN_Project *p, int index, int code, + EN_API_FLOAT_TYPE *value) { + double v = 0.0; + Pdemand demand; + Psource source; + + + EN_Network *net = &p->network; + hydraulics_t *hyd = &p->hydraulics; + quality_t *qu = &p->quality; + + Snode *Node = net->Node; + Stank *Tank = net->Tank; + + const int Nnodes = net->Nnodes; + const int Njuncs = net->Njuncs; + + double *Ucf = p->Ucf; + double *NodeDemand = hyd->NodeDemand; + double *NodeQual = qu->NodeQual; + + + /* Check for valid arguments */ + *value = 0.0; + if (!p->Openflag) + return (102); + if (index <= 0 || index > Nnodes) + return (203); + + /* Retrieve called-for parameter */ + switch (code) { + case EN_ELEVATION: + v = Node[index].El * Ucf[ELEV]; + break; + + case EN_BASEDEMAND: + v = 0.0; + /* NOTE: primary demand category is last on demand list */ + if (index <= Njuncs) + for (demand = Node[index].D; demand != NULL; demand = demand->next) + v = (demand->Base); + v *= Ucf[FLOW]; + break; + + case EN_PATTERN: + v = 0.0; + /* NOTE: primary demand category is last on demand list */ + if (index <= Njuncs) { + for (demand = Node[index].D; demand != NULL; demand = demand->next) + v = (double)(demand->Pat); + } else + v = (double)(Tank[index - Njuncs].Pat); + break; + + case EN_EMITTER: + v = 0.0; + if (Node[index].Ke > 0.0) + v = Ucf[FLOW] / pow((Ucf[PRESSURE] * Node[index].Ke), (1.0 / hyd->Qexp)); + break; + + case EN_INITQUAL: + v = Node[index].C0 * Ucf[QUALITY]; + break; + + /*** Additional parameters added for retrieval ***/ + case EN_SOURCEQUAL: + case EN_SOURCETYPE: + case EN_SOURCEMASS: + case EN_SOURCEPAT: + source = Node[index].S; + if (source == NULL) + return (240); + if (code == EN_SOURCEQUAL) + v = source->C0; + else if (code == EN_SOURCEMASS) + v = source->Smass * 60.0; + else if (code == EN_SOURCEPAT) + v = source->Pat; + else + v = source->Type; + break; + + case EN_TANKLEVEL: + if (index <= Njuncs) + return (251); + v = (Tank[index - Njuncs].H0 - Node[index].El) * Ucf[ELEV]; + break; + + /*** New parameter added for retrieval ***/ + case EN_INITVOLUME: + v = 0.0; + if (index > Njuncs) + v = Tank[index - Njuncs].V0 * Ucf[VOLUME]; + break; + + /*** New parameter added for retrieval ***/ + case EN_MIXMODEL: + v = MIX1; + if (index > Njuncs) + v = Tank[index - Njuncs].MixModel; + break; + + /*** New parameter added for retrieval ***/ + case EN_MIXZONEVOL: + v = 0.0; + if (index > Njuncs) + v = Tank[index - Njuncs].V1max * Ucf[VOLUME]; + break; + + case EN_DEMAND: + v = NodeDemand[index] * Ucf[FLOW]; + break; + + case EN_HEAD: + v = hyd->NodeHead[index] * Ucf[HEAD]; + break; + + case EN_PRESSURE: + v = (hyd->NodeHead[index] - Node[index].El) * Ucf[PRESSURE]; + break; + + case EN_QUALITY: + v = NodeQual[index] * Ucf[QUALITY]; + break; + + /*** New parameters added for retrieval begins here ***/ + /*** (Thanks to Nicolas Basile of Ecole Polytechnique ***/ + /*** de Montreal for suggesting some of these.) ***/ + + case EN_TANKDIAM: + v = 0.0; + if (index > Njuncs) { + v = sqrt(4.0 / PI * Tank[index - Njuncs].A) * Ucf[ELEV]; + } + break; + + case EN_MINVOLUME: + v = 0.0; + if (index > Njuncs) + v = Tank[index - Njuncs].Vmin * Ucf[VOLUME]; + break; + + case EN_MAXVOLUME: // !sph + v = 0.0; + if (index > Njuncs) + v = Tank[index - Njuncs].Vmax * Ucf[VOLUME]; + break; + + case EN_VOLCURVE: + v = 0.0; + if (index > Njuncs) + v = Tank[index - Njuncs].Vcurve; + break; + + case EN_MINLEVEL: + v = 0.0; + if (index > Njuncs) { + v = (Tank[index - Njuncs].Hmin - Node[index].El) * Ucf[ELEV]; + } + break; + + case EN_MAXLEVEL: + v = 0.0; + if (index > Njuncs) { + v = (Tank[index - Njuncs].Hmax - Node[index].El) * Ucf[ELEV]; + } + break; + + case EN_MIXFRACTION: + v = 1.0; + if (index > Njuncs && Tank[index - Njuncs].Vmax > 0.0) { + v = Tank[index - Njuncs].V1max / Tank[index - Njuncs].Vmax; + } + break; + + case EN_TANK_KBULK: + v = 0.0; + if (index > Njuncs) + v = Tank[index - Njuncs].Kb * SECperDAY; + break; + + /*** New parameter additions ends here. ***/ + + case EN_TANKVOLUME: + if (index <= Njuncs) + return (251); + v = tankvolume(p, index - Njuncs, hyd->NodeHead[index]) * Ucf[VOLUME]; + break; + + default: + return (251); + } + *value = (EN_API_FLOAT_TYPE)v; + return (0); +} + +/* + ---------------------------------------------------------------- + Functions for retrieving link data + ---------------------------------------------------------------- + */ + +int DLLEXPORT EN_getlinkindex(EN_Project *p, char *id, int *index) { + *index = 0; + if (!p->Openflag) + return (102); + *index = findlink(&p->network,id); + if (*index == 0) + return (204); + else + return (0); +} + +int DLLEXPORT EN_getlinkid(EN_Project *p, int index, char *id) { + strcpy(id, ""); + if (!p->Openflag) + return (102); + if (index < 1 || index > p->network.Nlinks) + return (204); + strcpy(id, p->network.Link[index].ID); + return (0); +} + +int DLLEXPORT EN_getlinktype(EN_Project *p, int index, EN_LinkType *code) { + *code = -1; + if (!p->Openflag) + return (102); + if (index < 1 || index > p->network.Nlinks) + return (204); + *code = p->network.Link[index].Type; + return (0); +} + +int DLLEXPORT EN_getlinknodes(EN_Project *p, int index, int *node1, + int *node2) { + *node1 = 0; + *node2 = 0; + if (!p->Openflag) + return (102); + if (index < 1 || index > p->network.Nlinks) + return (204); + *node1 = p->network.Link[index].N1; + *node2 = p->network.Link[index].N2; + return (0); +} + +int DLLEXPORT EN_getlinkvalue(EN_Project *p, int index, EN_LinkProperty code, EN_API_FLOAT_TYPE *value) { + double a, h, q, v = 0.0; + int returnValue = 0; + + EN_Network *net = &p->network; + hydraulics_t *hyd = &p->hydraulics; + + Slink *Link = net->Link; + Spump *Pump = net->Pump; + + const int Nlinks = net->Nlinks; + + double *Ucf = p->Ucf; + double *LinkFlows = hyd->LinkFlows; + double *LinkSetting = hyd->LinkSetting; + + + + /* Check for valid arguments */ + *value = 0.0; + if (!p->Openflag) + return (102); + if (index <= 0 || index > Nlinks) + return (204); + + /* Retrieve called-for parameter */ + switch (code) { + case EN_DIAMETER: + if (Link[index].Type == EN_PUMP) + v = 0.0; + else + v = Link[index].Diam * Ucf[DIAM]; + break; + + case EN_LENGTH: + v = Link[index].Len * Ucf[ELEV]; + break; + + case EN_ROUGHNESS: + if (Link[index].Type <= EN_PIPE) { + if (hyd->Formflag == DW) + v = Link[index].Kc * (1000.0 * Ucf[ELEV]); + else + v = Link[index].Kc; + } else + v = 0.0; + break; + + case EN_MINORLOSS: + if (Link[index].Type != EN_PUMP) { + v = Link[index].Km; + v *= (SQR(Link[index].Diam) * SQR(Link[index].Diam) / 0.02517); + } else + v = 0.0; + break; + + case EN_INITSTATUS: + if (Link[index].Stat <= CLOSED) + v = 0.0; + else + v = 1.0; + break; + + case EN_INITSETTING: + if (Link[index].Type == EN_PIPE || Link[index].Type == EN_CVPIPE) + return (ENgetlinkvalue(index, EN_ROUGHNESS, value)); + v = Link[index].Kc; + switch (Link[index].Type) { + case EN_PRV: + case EN_PSV: + case EN_PBV: + v *= Ucf[PRESSURE]; + break; + case EN_FCV: + v *= Ucf[FLOW]; + default: + break; + } + break; + + case EN_KBULK: + v = Link[index].Kb * SECperDAY; + break; + + case EN_KWALL: + v = Link[index].Kw * SECperDAY; + break; + + case EN_FLOW: + + /*** Updated 10/25/00 ***/ + if (hyd->LinkStatus[index] <= CLOSED) + v = 0.0; + else + v = LinkFlows[index] * Ucf[FLOW]; + break; + + case EN_VELOCITY: + if (Link[index].Type == EN_PUMP) { + v = 0.0; + } + + /*** Updated 11/19/01 ***/ + else if (hyd->LinkStatus[index] <= CLOSED) + v = 0.0; + + else { + q = ABS(LinkFlows[index]); + a = PI * SQR(Link[index].Diam) / 4.0; + v = q / a * Ucf[VELOCITY]; + } + break; + + case EN_HEADLOSS: + + /*** Updated 11/19/01 ***/ + if (hyd->LinkStatus[index] <= CLOSED) + v = 0.0; + + else { + h = hyd->NodeHead[Link[index].N1] - hyd->NodeHead[Link[index].N2]; + if (Link[index].Type != EN_PUMP) + h = ABS(h); + v = h * Ucf[HEADLOSS]; + } + break; + + case EN_STATUS: + if (hyd->LinkStatus[index] <= CLOSED) + v = 0.0; + else + v = 1.0; + break; + + case EN_SETTING: + if (Link[index].Type == EN_PIPE || Link[index].Type == EN_CVPIPE) { + return (ENgetlinkvalue(index, EN_ROUGHNESS, value)); + } + if (LinkSetting[index] == MISSING) { + v = 0.0; + } else { + v = LinkSetting[index]; + } + switch (Link[index].Type) { + case EN_PRV: + case EN_PSV: + case EN_PBV: + v *= Ucf[PRESSURE]; + break; + case EN_FCV: + v *= Ucf[FLOW]; + default: + break; + } + break; + + case EN_ENERGY: + getenergy(p, index, &v, &a); + break; + + case EN_LINKQUAL: + v = avgqual(p, index) * Ucf[LINKQUAL]; + break; + + case EN_LINKPATTERN: + if (Link[index].Type == EN_PUMP) + v = (double)Pump[findpump(&p->network, index)].Upat; + break; + + case EN_EFFICIENCY: + getenergy(p, index, &a, &v); + break; + + case EN_HEADCURVE: + if (Link[index].Type == EN_PUMP) { + v = (double)Pump[findpump(&p->network, index)].Hcurve; + if (v == 0) { + returnValue = 226; + } + } + else { + v = 0; + returnValue = 211; + } + break; + + case EN_EFFICIENCYCURVE: + if (Link[index].Type == EN_PUMP) { + v = (double)Pump[findpump(&p->network, index)].Ecurve; + if (v == 0) { + returnValue = 268; + } + } + else { + v = 0; + returnValue = 211; + } + + default: + v = 0; + returnValue = 251; + } + *value = (EN_API_FLOAT_TYPE)v; + return returnValue; +} + +int DLLEXPORT EN_getcurve(EN_Project *p, int curveIndex, char *id, int *nValues, + EN_API_FLOAT_TYPE **xValues, + EN_API_FLOAT_TYPE **yValues) { + int iPoint, nPoints; + Scurve curve; + EN_API_FLOAT_TYPE *pointX, *pointY; + + /* Check that input file opened */ + if (!p->Openflag) + return (102); + + curve = p->network.Curve[curveIndex]; + nPoints = curve.Npts; + + pointX = calloc(nPoints, sizeof(EN_API_FLOAT_TYPE)); + pointY = calloc(nPoints, sizeof(EN_API_FLOAT_TYPE)); + + for (iPoint = 0; iPoint < nPoints; iPoint++) { + double x = curve.X[iPoint]; + double y = curve.Y[iPoint]; + pointX[iPoint] = (EN_API_FLOAT_TYPE)x; + pointY[iPoint] = (EN_API_FLOAT_TYPE)y; + } + + strncpy(id, "", MAXID); + strncpy(id, curve.ID, MAXID); + *nValues = nPoints; + *xValues = pointX; + *yValues = pointY; + + return (0); +} + +/* + ---------------------------------------------------------------- + Functions for changing network data + ---------------------------------------------------------------- + */ + +int DLLEXPORT EN_setcontrol(EN_Project *p, int cindex, int ctype, int lindex, + EN_API_FLOAT_TYPE setting, int nindex, + EN_API_FLOAT_TYPE level) { + char status = ACTIVE; + long t = 0; + double s = setting, lvl = level; + EN_Network *net; + Snode *Node; + Slink *Link; + Scontrol *Control; + + int Nnodes; + int Njuncs; + int Nlinks; + double *Ucf; + + /* Check that input file opened */ + if (!p->Openflag) + return (102); + + /* Check that control exists */ + if (cindex < 1 || cindex > p->network.Ncontrols) + return (241); + + + net = &p->network; + + Node = net->Node; + Link = net->Link; + Control = net->Control; + + Nnodes = net->Nnodes; + Njuncs = net->Njuncs; + Nlinks = net->Nlinks; + + Ucf = p->Ucf; + + /* Check that controlled link exists */ + if (lindex == 0) { + Control[cindex].Link = 0; + return (0); + } + if (lindex < 0 || lindex > Nlinks) + return (204); + + /* Cannot control check valve. */ + if (Link[lindex].Type == EN_CVPIPE) + return (207); + + /* Check for valid parameters */ + if (ctype < 0 || ctype > EN_TIMEOFDAY) + return (251); + if (ctype == EN_LOWLEVEL || ctype == EN_HILEVEL) { + if (nindex < 1 || nindex > Nnodes) + return (203); + } else + nindex = 0; + if (s < 0.0 || lvl < 0.0) + return (202); + + /* Adjust units of control parameters */ + switch (Link[lindex].Type) { + case EN_PRV: + case EN_PSV: + case EN_PBV: + s /= Ucf[PRESSURE]; + break; + case EN_FCV: + s /= Ucf[FLOW]; + break; + + /*** Updated 9/7/00 ***/ + case EN_GPV: + if (s == 0.0) + status = CLOSED; + else if (s == 1.0) + status = OPEN; + else + return (202); + s = Link[lindex].Kc; + break; + + case EN_PIPE: + case EN_PUMP: + status = OPEN; + if (s == 0.0) + status = CLOSED; + default: + break; + } + if (ctype == LOWLEVEL || ctype == HILEVEL) { + if (nindex > Njuncs) + lvl = Node[nindex].El + level / Ucf[ELEV]; + else + lvl = Node[nindex].El + level / Ucf[PRESSURE]; + } + if (ctype == TIMER) + t = (long)ROUND(lvl); + if (ctype == TIMEOFDAY) + t = (long)ROUND(lvl) % SECperDAY; + + /* Reset control's parameters */ + Control[cindex].Type = (char)ctype; + Control[cindex].Link = lindex; + Control[cindex].Node = nindex; + Control[cindex].Status = status; + Control[cindex].Setting = s; + Control[cindex].Grade = lvl; + Control[cindex].Time = t; + return (0); +} + +int DLLEXPORT EN_setnodevalue(EN_Project *p, int index, int code, EN_API_FLOAT_TYPE v) +/*---------------------------------------------------------------- + ** Input: index = node index + ** code = node parameter code (see EPANET2.H) + ** value = parameter value + ** Output: none + ** Returns: error code + ** Purpose: sets input parameter value for a node + **---------------------------------------------------------------- + */ +{ + EN_Network *net = &p->network; + hydraulics_t *hyd = &p->hydraulics; + quality_t *qu = &p->quality; + + Snode *Node = net->Node; + Stank *Tank = net->Tank; + + const int Nnodes = net->Nnodes; + const int Njuncs = net->Njuncs; + const int Npats = net->Npats; + + double *Ucf = p->Ucf; + + int j; + Pdemand demand; + Psource source; + double Htmp; + double value = v; + + if (!p->Openflag) + return (102); + if (index <= 0 || index > Nnodes) + return (203); + switch (code) { + case EN_ELEVATION: + if (index <= Njuncs) + Node[index].El = value / Ucf[ELEV]; + else { + value = (value / Ucf[ELEV]) - Node[index].El; + j = index - Njuncs; + Tank[j].H0 += value; + Tank[j].Hmin += value; + Tank[j].Hmax += value; + Node[index].El += value; + hyd->NodeHead[index] += value; + } + break; + + case EN_BASEDEMAND: + /* NOTE: primary demand category is last on demand list */ + if (index <= Njuncs) { + for (demand = Node[index].D; demand != NULL; demand = demand->next) { + if (demand->next == NULL) + demand->Base = value / Ucf[FLOW]; + } + } + break; + + case EN_PATTERN: + /* NOTE: primary demand category is last on demand list */ + j = ROUND(value); + if (j < 0 || j > Npats) + return (205); + if (index <= Njuncs) { + for (demand = Node[index].D; demand != NULL; demand = demand->next) { + if (demand->next == NULL) + demand->Pat = j; + } + } else + Tank[index - Njuncs].Pat = j; + break; + + case EN_EMITTER: + if (index > Njuncs) + return (203); + if (value < 0.0) + return (202); + if (value > 0.0) + value = pow((Ucf[FLOW] / value), hyd->Qexp) / Ucf[PRESSURE]; + Node[index].Ke = value; + break; + + case EN_INITQUAL: + if (value < 0.0) + return (202); + Node[index].C0 = value / Ucf[QUALITY]; + if (index > Njuncs) + Tank[index - Njuncs].C = Node[index].C0; + break; + + case EN_SOURCEQUAL: + case EN_SOURCETYPE: + case EN_SOURCEPAT: + if (value < 0.0) + return (202); + source = Node[index].S; + if (source == NULL) { + source = (struct Ssource *)malloc(sizeof(struct Ssource)); + if (source == NULL) + return (101); + source->Type = CONCEN; + source->C0 = 0.0; + source->Pat = 0; + Node[index].S = source; + } + if (code == EN_SOURCEQUAL) { + source->C0 = value; + } else if (code == EN_SOURCEPAT) { + j = ROUND(value); + if (j < 0 || j > Npats) + return (205); + source->Pat = j; + } else // code == EN_SOURCETYPE + { + j = ROUND(value); + if (j < CONCEN || j > FLOWPACED) + return (251); + else + source->Type = (char)j; + } + return (0); + + case EN_TANKLEVEL: + if (index <= Njuncs) + return (251); + j = index - Njuncs; + if (Tank[j].A == 0.0) /* Tank is a reservoir */ + { + Tank[j].H0 = value / Ucf[ELEV]; + Tank[j].Hmin = Tank[j].H0; + Tank[j].Hmax = Tank[j].H0; + Node[index].El = Tank[j].H0; + hyd->NodeHead[index] = Tank[j].H0; + } else { + value = Node[index].El + value / Ucf[ELEV]; + if (value > Tank[j].Hmax || value < Tank[j].Hmin) + return (202); + Tank[j].H0 = value; + Tank[j].V0 = tankvolume(p, j, Tank[j].H0); + // Resetting Volume in addition to initial volume + Tank[j].V = Tank[j].V0; + hyd->NodeHead[index] = Tank[j].H0; + } + break; + + /*** New parameters added for retrieval begins here ***/ + /*** (Thanks to Nicolas Basile of Ecole Polytechnique ***/ + /*** de Montreal for suggesting some of these.) ***/ + + case EN_TANKDIAM: + if (value <= 0.0) + return (202); + if (index <= Njuncs) + return (251); + j = index - Njuncs; + if (j > 0 && Tank[j].A > 0.0) { + value /= Ucf[ELEV]; + Tank[j].A = PI * SQR(value) / 4.0; + Tank[j].Vmin = tankvolume(p, j, Tank[j].Hmin); + Tank[j].V0 = tankvolume(p, j, Tank[j].H0); + Tank[j].Vmax = tankvolume(p, j, Tank[j].Hmax); + } else { + return (251); + } + break; + + case EN_MINVOLUME: + if (value < 0.0) + return (202); + if (index <= Njuncs) + return (251); + j = index - Njuncs; + if (j > 0 && Tank[j].A > 0.0) { + Tank[j].Vmin = value / Ucf[VOLUME]; + Tank[j].V0 = tankvolume(p, j, Tank[j].H0); + Tank[j].Vmax = tankvolume(p, j, Tank[j].Hmax); + } else { + return (251); + } + break; + + case EN_MINLEVEL: + if (value < 0.0) + return (202); + if (index <= Njuncs) + return (251); // not a tank or reservoir + j = index - Njuncs; + if (Tank[j].A == 0.0) + return (251); // node is a reservoir + Htmp = value / Ucf[ELEV] + Node[index].El; + if (Htmp < Tank[j].Hmax && Htmp <= Tank[j].H0) { + if (Tank[j].Vcurve > 0) + return (202); + Tank[j].Hmin = Htmp; + Tank[j].Vmin = tankvolume(p, j, Tank[j].Hmin); + } else { + return (251); + } + break; + + case EN_MAXLEVEL: + if (value < 0.0) + return (202); + if (index <= Njuncs) + return (251); // not a tank or reservoir + j = index - Njuncs; + if (Tank[j].A == 0.0) + return (251); // node is a reservoir + Htmp = value / Ucf[ELEV] + Node[index].El; + if (Htmp > Tank[j].Hmin && Htmp >= Tank[j].H0) { + if (Tank[j].Vcurve > 0) + return (202); + Tank[j].Hmax = Htmp; + Tank[j].Vmax = tankvolume(p, j, Tank[j].Hmax); + } else { + return (251); + } + break; + + case EN_MIXMODEL: + j = ROUND(value); + if (index <= Njuncs) + return (251); + if (j < MIX1 || j > LIFO) + return (202); + if (index > Njuncs && Tank[index - Njuncs].A > 0.0) { + Tank[index - Njuncs].MixModel = (char)j; + } else { + return (251); + } + break; + + case EN_MIXFRACTION: + if (value < 0.0 || value > 1.0) + return (202); + if (index <= Njuncs) + return (251); + j = index - Njuncs; + if (j > 0 && Tank[j].A > 0.0) { + Tank[j].V1max = value * Tank[j].Vmax; + } + break; + + case EN_TANK_KBULK: + if (index <= Njuncs) + return (251); + j = index - Njuncs; + if (j > 0 && Tank[j].A > 0.0) { + Tank[j].Kb = value / SECperDAY; + qu->Reactflag = 1; + } else { + return (251); + } + break; + + /*** New parameter additions ends here. ***/ + + default: + return (251); + } + return (0); +} + +int DLLEXPORT EN_setlinkvalue(EN_Project *p, int index, int code, + EN_API_FLOAT_TYPE v) + +/*---------------------------------------------------------------- + ** Input: index = link index + ** code = link parameter code (see EPANET2.H) + ** v = parameter value + ** Output: none + ** Returns: error code + ** Purpose: sets input parameter value for a link + **---------------------------------------------------------------- + */ +{ + + EN_Network *net = &p->network; + hydraulics_t *hyd = &p->hydraulics; + quality_t *qu = &p->quality; + + Slink *Link = net->Link; + + const int Nlinks = net->Nlinks; + + double *Ucf = p->Ucf; + double *LinkSetting = hyd->LinkSetting; + + char s; + double r, value = v; + + if (!p->Openflag) + return (102); + if (index <= 0 || index > Nlinks) + return (204); + switch (code) { + case EN_DIAMETER: + if (Link[index].Type != EN_PUMP) { + if (value <= 0.0) + return (202); + value /= Ucf[DIAM]; /* Convert to feet */ + r = Link[index].Diam / value; /* Ratio of old to new diam */ + Link[index].Km *= SQR(r) * SQR(r); /* Adjust minor loss factor */ + Link[index].Diam = value; /* Update diameter */ + resistance(p, index); /* Update resistance factor */ + } + break; + + case EN_LENGTH: + if (Link[index].Type <= EN_PIPE) { + if (value <= 0.0) + return (202); + Link[index].Len = value / Ucf[ELEV]; + resistance(p, index); + } + break; + + case EN_ROUGHNESS: + if (Link[index].Type <= EN_PIPE) { + if (value <= 0.0) + return (202); + Link[index].Kc = value; + if (hyd->Formflag == DW) + Link[index].Kc /= (1000.0 * Ucf[ELEV]); + resistance(p, index); + } + break; + + case EN_MINORLOSS: + if (Link[index].Type != EN_PUMP) { + if (value <= 0.0) + return (202); + Link[index].Km = + 0.02517 * value / SQR(Link[index].Diam) / SQR(Link[index].Diam); + } + break; + + case EN_INITSTATUS: + case EN_STATUS: + /* Cannot set status for a check valve */ + if (Link[index].Type == EN_CVPIPE) + return (207); + s = (char)ROUND(value); + if (s < 0 || s > 1) + return (251); + if (code == EN_INITSTATUS) + setlinkstatus(p, index, s, &Link[index].Stat, &Link[index].Kc); + else + setlinkstatus(p, index, s, &hyd->LinkStatus[index], &LinkSetting[index]); + break; + + case EN_INITSETTING: + case EN_SETTING: + if (value < 0.0) + return (202); + if (Link[index].Type == EN_PIPE || Link[index].Type == EN_CVPIPE) + return (ENsetlinkvalue(index, EN_ROUGHNESS, v)); + else { + switch (Link[index].Type) { + case EN_PUMP: + break; + case EN_PRV: + case EN_PSV: + case EN_PBV: + value /= Ucf[PRESSURE]; + break; + case EN_FCV: + value /= Ucf[FLOW]; + break; + case EN_TCV: + break; + + /*** Updated 9/7/00 ***/ + case EN_GPV: + return (202); /* Cannot modify setting for GPV */ + + default: + return (251); + } + if (code == EN_INITSETTING) + setlinksetting(p, index, value, &Link[index].Stat, &Link[index].Kc); + else + setlinksetting(p, index, value, &hyd->LinkStatus[index], + &LinkSetting[index]); + } + break; + + case EN_KBULK: + if (Link[index].Type <= EN_PIPE) { + Link[index].Kb = value / SECperDAY; + qu->Reactflag = 1; + } + break; + + case EN_KWALL: + if (Link[index].Type <= EN_PIPE) { + Link[index].Kw = value / SECperDAY; + qu->Reactflag = 1; + } + break; + + default: + return (251); + } + return (0); +} + +int DLLEXPORT EN_addpattern(EN_Project *p, char *id) { + int i, j, n, err = 0; + Spattern *tmpPat; + + EN_Network *net = &p->network; + parser_data_t *par = &p->parser; + + Spattern *Pattern = net->Pattern; + + const int Npats = net->Npats; + + + /* Check if a pattern with same id already exists */ + + if (!p->Openflag) + return (102); + if (ENgetpatternindex(id, &i) == 0) + return (215); + + /* Check that id name is not too long */ + + if (strlen(id) > MAXID) + return (250); + + /* Allocate memory for a new array of patterns */ + + n = Npats + 1; + tmpPat = (Spattern *)calloc(n + 1, sizeof(Spattern)); + if (tmpPat == NULL) + return (101); + + /* Copy contents of old pattern array to new one */ + + for (i = 0; i <= net->Npats; i++) { + strcpy(tmpPat[i].ID, Pattern[i].ID); + tmpPat[i].Length = Pattern[i].Length; + tmpPat[i].F = (double *)calloc(Pattern[i].Length, sizeof(double)); + if (tmpPat[i].F == NULL) + err = 1; + else + for (j = 0; j < Pattern[i].Length; j++) + tmpPat[i].F[j] = Pattern[i].F[j]; + } + + /* Add the new pattern to the new array of patterns */ + + strcpy(tmpPat[n].ID, id); + tmpPat[n].Length = 1; + tmpPat[n].F = (double *)calloc(tmpPat[n].Length, sizeof(double)); + if (tmpPat[n].F == NULL) + err = 1; + else + tmpPat[n].F[0] = 1.0; + + /* Abort if memory allocation error */ + + if (err) { + for (i = 0; i <= n; i++) + if (tmpPat[i].F) + free(tmpPat[i].F); + free(tmpPat); + return (101); + } + + // Replace old pattern array with new one + + for (i = 0; i <= Npats; i++) + free(Pattern[i].F); + free(Pattern); + Pattern = tmpPat; + net->Npats = n; + par->MaxPats = n; + return 0; +} + +int DLLEXPORT EN_setpattern(EN_Project *p, int index, EN_API_FLOAT_TYPE *f, int n) { + int j; + + EN_Network *net = &p->network; + Spattern *Pattern = net->Pattern; + const int Npats = net->Npats; + + + /* Check for valid arguments */ + if (!p->Openflag) + return (102); + if (index <= 0 || index > Npats) + return (205); + if (n <= 0) + return (202); + + /* Re-set number of time periods & reallocate memory for multipliers */ + Pattern[index].Length = n; + Pattern[index].F = (double *)realloc(Pattern[index].F, n * sizeof(double)); + if (Pattern[index].F == NULL) + return (101); + + /* Load multipliers into pattern */ + for (j = 0; j < n; j++) + Pattern[index].F[j] = f[j]; + return (0); +} + +int DLLEXPORT EN_setpatternvalue(EN_Project *p, int index, int period, EN_API_FLOAT_TYPE value) { + + EN_Network *net = &p->network; + + Spattern *Pattern = net->Pattern; + + const int Npats = net->Npats; + + + if (!p->Openflag) + return (102); + if (index <= 0 || index > Npats) + return (205); + if (period <= 0 || period > Pattern[index].Length) + return (251); + Pattern[index].F[period - 1] = value; + return (0); +} + +int DLLEXPORT EN_addcurve(EN_Project *p, char *id) { + + EN_Network *net = &p->network; + parser_data_t *par = &p->parser; + Scurve *Curve = net->Curve; + + int i, j, n, err = 0; + Scurve *tmpCur; + + /* Check if a curve with same id already exists */ + + if (!p->Openflag) + return (102); + if (ENgetcurveindex(id, &i) == 0) + return (215); + + /* Check that id name is not too long */ + + if (strlen(id) > MAXID) + return (250); + + /* Allocate memory for a new array of curves */ + + n = net->Ncurves + 1; + tmpCur = (Scurve *)calloc(n + 1, sizeof(Scurve)); + if (tmpCur == NULL) + return (101); + + /* Copy contents of old curve array to new one */ + + for (i = 0; i <= net->Ncurves; i++) { + strcpy(tmpCur[i].ID, net->Curve[i].ID); + tmpCur[i].Npts = Curve[i].Npts; + tmpCur[i].X = (double *)calloc(net->Curve[i].Npts, sizeof(double)); + tmpCur[i].Y = (double *)calloc(net->Curve[i].Npts, sizeof(double)); + if (tmpCur[i].X == NULL) + err = 1; + else if (tmpCur[i].Y == NULL) + err = 1; + else + for (j = 0; j < Curve[i].Npts; j++) { + tmpCur[i].X[j] = net->Curve[i].X[j]; + tmpCur[i].Y[j] = net->Curve[i].Y[j]; + } + } + + /* Add the new Curve to the new array of curves */ + + strcpy(tmpCur[n].ID, id); + tmpCur[n].Npts = 1; + tmpCur[n].X = (double *)calloc(tmpCur[n].Npts, sizeof(double)); + tmpCur[n].Y = (double *)calloc(tmpCur[n].Npts, sizeof(double)); + if (tmpCur[n].X == NULL) + err = 1; + else if (tmpCur[n].Y == NULL) + err = 1; + else { + tmpCur[n].X[0] = 1.0; + tmpCur[n].Y[0] = 1.0; + } + + /* Abort if memory allocation error */ + + if (err) { + for (i = 0; i <= n; i++) { + if (tmpCur[i].X) + free(tmpCur[i].X); + if (tmpCur[i].Y) + free(tmpCur[i].Y); + } + free(tmpCur); + return (101); + } + + // Replace old curves array with new one + + for (i = 0; i <= net->Ncurves; i++) { + free(net->Curve[i].X); + free(net->Curve[i].Y); + } + free(net->Curve); + net->Curve = tmpCur; + net->Ncurves = n; + par->MaxCurves = n; + return 0; +} + +int DLLEXPORT EN_setcurve(EN_Project *p, int index, EN_API_FLOAT_TYPE *x, EN_API_FLOAT_TYPE *y, int n) { + + EN_Network *net = &p->network; + Scurve *Curve = net->Curve; + + + int j; + + /* Check for valid arguments */ + if (!p->Openflag) + return (102); + if (index <= 0 || index > net->Ncurves) + return (206); + if (n <= 0) + return (202); + + /* Re-set number of points & reallocate memory for values */ + Curve[index].Npts = n; + Curve[index].X = (double *)realloc(Curve[index].X, n * sizeof(double)); + Curve[index].Y = (double *)realloc(Curve[index].Y, n * sizeof(double)); + if (Curve[index].X == NULL) + return (101); + if (Curve[index].Y == NULL) + return (101); + + /* Load values into curve */ + for (j = 0; j < n; j++) { + Curve[index].X[j] = x[j]; + Curve[index].Y[j] = y[j]; + } + return (0); +} + +int DLLEXPORT EN_setcurvevalue(EN_Project *p, int index, int pnt, EN_API_FLOAT_TYPE x, EN_API_FLOAT_TYPE y) { + + EN_Network *net = &p->network; + + Scurve *Curve = net->Curve; + + const int Ncurves = net->Ncurves; + + + if (!p->Openflag) + return (102); + if (index <= 0 || index > Ncurves) + return (206); + if (pnt <= 0 || pnt > Curve[index].Npts) + return (251); + Curve[index].X[pnt - 1] = x; + Curve[index].Y[pnt - 1] = y; + return (0); +} + +int DLLEXPORT EN_settimeparam(EN_Project *p, int code, long value) +{ + report_options_t *rep = &p->report; + quality_t *qu = &p->quality; + time_options_t *time = &p->time_options; + + if (!p->Openflag) + return (102); + if (p->hydraulics.OpenHflag || p->quality.OpenQflag) { + // --> there's nothing wrong with changing certain time parameters during a + // simulation run, or before the run has started. + // todo -- how to tell? + /* + if (code == EN_DURATION || code == EN_HTIME || code == EN_REPORTSTEP || + code == EN_DURATION || Htime == 0) { + // it's ok + } + else { + return(109); + } + */ + } + if (value < 0) + return (202); + switch (code) { + case EN_DURATION: + time->Dur = value; + if (time->Rstart > time->Dur) + time->Rstart = 0; + break; + + case EN_HYDSTEP: + if (value == 0) + return (202); + time->Hstep = value; + time->Hstep = MIN(time->Pstep, time->Hstep); + time->Hstep = MIN(time->Rstep, time->Hstep); + qu->Qstep = MIN(qu->Qstep, time->Hstep); + break; + + case EN_QUALSTEP: + if (value == 0) + return (202); + qu->Qstep = value; + qu->Qstep = MIN(qu->Qstep, time->Hstep); + break; + + case EN_PATTERNSTEP: + if (value == 0) + return (202); + time->Pstep = value; + if (time->Hstep > time->Pstep) + time->Hstep = time->Pstep; + break; + + case EN_PATTERNSTART: + time->Pstart = value; + break; + + case EN_REPORTSTEP: + if (value == 0) + return (202); + time->Rstep = value; + if (time->Hstep > time->Rstep) + time->Hstep = time->Rstep; + break; + + case EN_REPORTSTART: + if (time->Rstart > time->Dur) + return (202); + time->Rstart = value; + break; + + case EN_RULESTEP: + if (value == 0) + return (202); + time->Rulestep = value; + time->Rulestep = MIN(time->Rulestep, time->Hstep); + break; + + case EN_STATISTIC: + if (value > RANGE) + return (202); + rep->Tstatflag = (char)value; + break; + + case EN_HTIME: + time->Htime = value; + break; + + case EN_QTIME: + qu->Qtime = value; + break; + + default: + return (251); + } + return (0); +} + +int DLLEXPORT EN_setoption(EN_Project *p, int code, EN_API_FLOAT_TYPE v) +/*---------------------------------------------------------------- + ** Input: code = option code (see EPANET2.H) + ** v = option value + ** Output: none + ** Returns: error code + ** Purpose: sets value for an analysis option + **---------------------------------------------------------------- + */ +{ + EN_Network *net = &p->network; + hydraulics_t *hyd = &p->hydraulics; + quality_t *qu = &p->quality; + + Snode *Node = net->Node; + const int Njuncs = net->Njuncs; + + double *Ucf = p->Ucf; + + int i, j; + double Ke, n, ucf, value = v; + if (!p->Openflag) + return (102); + switch (code) { + case EN_TRIALS: + if (value < 1.0) + return (202); + hyd->MaxIter = (int)value; + break; + case EN_ACCURACY: + if (value < 1.e-5 || value > 1.e-1) + return (202); + hyd->Hacc = value; + break; + case EN_TOLERANCE: + if (value < 0.0) + return (202); + qu->Ctol = value / Ucf[QUALITY]; + break; + case EN_EMITEXPON: + if (value <= 0.0) + return (202); + n = 1.0 / value; + ucf = pow(Ucf[FLOW], n) / Ucf[PRESSURE]; + for (i = 1; i <= Njuncs; i++) { + j = ENgetnodevalue(i, EN_EMITTER, &v); + Ke = v; + if (j == 0 && Ke > 0.0) + Node[i].Ke = ucf / pow(Ke, n); + } + hyd->Qexp = n; + break; + case EN_DEMANDMULT: + if (value <= 0.0) + return (202); + hyd->Dmult = value; + break; + default: + return (251); + } + return (0); +} + +int DLLEXPORT EN_setstatusreport(EN_Project *p, int code) { + int errcode = 0; + if (code >= 0 && code <= 2) + p->report.Statflag = (char)code; + else + errcode = 202; + return (errcode); +} + +int DLLEXPORT EN_setqualtype(EN_Project *p, int qualcode, char *chemname, char *chemunits, char *tracenode) { + + EN_Network *net = &p->network; + report_options_t *rep = &p->report; + quality_t *qu = &p->quality; + + double *Ucf = p->Ucf; + int i; + + /*** Updated 3/1/01 ***/ + double ccf = 1.0; + + if (!p->Openflag) + return (102); + if (qualcode < EN_NONE || qualcode > EN_TRACE) + return (251); + qu->Qualflag = (char)qualcode; + qu->Ctol *= Ucf[QUALITY]; + if (qu->Qualflag == CHEM) /* Chemical constituent */ + { + strncpy(qu->ChemName, chemname, MAXID); + strncpy(qu->ChemUnits, chemunits, MAXID); + + /*** Updated 3/1/01 ***/ + strncpy(rep->Field[QUALITY].Units, qu->ChemUnits, MAXID); + strncpy(rep->Field[REACTRATE].Units, qu->ChemUnits, MAXID); + strcat(rep->Field[REACTRATE].Units, t_PERDAY); + ccf = 1.0 / LperFT3; + } + if (qu->Qualflag == TRACE) /* Source tracing option */ + { + qu->TraceNode = findnode(net,tracenode); + if (qu->TraceNode == 0) + return (203); + strncpy(qu->ChemName, u_PERCENT, MAXID); + strncpy(qu->ChemUnits, tracenode, MAXID); + + /*** Updated 3/1/01 ***/ + strcpy(rep->Field[QUALITY].Units, u_PERCENT); + } + if (qu->Qualflag == AGE) /* Water age analysis */ + { + strncpy(qu->ChemName, w_AGE, MAXID); + strncpy(qu->ChemUnits, u_HOURS, MAXID); + + /*** Updated 3/1/01 ***/ + strcpy(rep->Field[QUALITY].Units, u_HOURS); + } + + /* when changing from CHEM to AGE or TRACE, nodes initial quality values must be returned to their original ones */ + if ((qu->Qualflag == AGE || qu->Qualflag == TRACE) & (Ucf[QUALITY] != 1)) { + for (i=1; i<=p->network.Nnodes; i++) { + p->network.Node[i].C0 *= Ucf[QUALITY]; + } + } + + /*** Updated 3/1/01 ***/ + Ucf[QUALITY] = ccf; + Ucf[LINKQUAL] = ccf; + Ucf[REACTRATE] = ccf; + qu->Ctol /= Ucf[QUALITY]; + + return (0); +} + +int DLLEXPORT EN_getheadcurveindex(EN_Project *p, int index, int *curveindex) { + + EN_Network *net = &p->network; + Slink *Link = net->Link; + Spump *Pump = net->Pump; + const int Nlinks = net->Nlinks; + + if (!p->Openflag) + return (102); + if (index < 1 || index > Nlinks || EN_PUMP != Link[index].Type) + return (204); + *curveindex = Pump[findpump(net, index)].Hcurve; + return (0); +} + +int DLLEXPORT EN_setheadcurveindex(EN_Project *p, int index, int curveindex) { + + EN_Network *net = &p->network; + + Slink *Link = net->Link; + const int Nlinks = net->Nlinks; + const int Ncurves = net->Ncurves; + + double *Ucf = p->Ucf; + int pIdx; + Spump *pump; + + if (!p->Openflag) + return (102); + if (index < 1 || index > Nlinks || EN_PUMP != Link[index].Type) { + return (204); + } + if (curveindex <= 0 || curveindex > Ncurves) { + return (206); + } + pIdx = findpump(net, index); + pump = &p->network.Pump[pIdx]; + pump->Ptype = NOCURVE; + pump->Hcurve = curveindex; + // update pump parameters + getpumpparams(p); + // convert units + if (pump->Ptype == POWER_FUNC) { + pump->H0 /= Ucf[HEAD]; + pump->R *= (pow(Ucf[FLOW], pump->N) / Ucf[HEAD]); + } + /* Convert flow range & max. head units */ + pump->Q0 /= Ucf[FLOW]; + pump->Qmax /= Ucf[FLOW]; + pump->Hmax /= Ucf[HEAD]; + + return (0); +} + +int DLLEXPORT EN_getpumptype(EN_Project *p, int index, int *type) { + + EN_Network *net = &p->network; + + Slink *Link = net->Link; + Spump *Pump = net->Pump; + const int Nlinks = net->Nlinks; + + *type = -1; + if (!p->Openflag) + return (102); + if (index < 1 || index > Nlinks || EN_PUMP != Link[index].Type) + return (204); + *type = Pump[findpump(&p->network, index)].Ptype; + return (0); +} + +/* +---------------------------------------------------------------- + Functions for opening files +---------------------------------------------------------------- +*/ + +int openfiles(EN_Project *p, char *f1, char *f2, char *f3) +/*---------------------------------------------------------------- +** Input: f1 = pointer to name of input file +** f2 = pointer to name of report file +** f3 = pointer to name of binary output file +** Output: none +** Returns: error code +** Purpose: opens input & report files +**---------------------------------------------------------------- +*/ +{ + + out_file_t *out = &p->out_files; + report_options_t *rep = &p->report; + parser_data_t *par = &p->parser; + + /* Initialize file pointers to NULL */ + par->InFile = NULL; + rep->RptFile = NULL; + out->OutFile = NULL; + out->HydFile = NULL; + + /* Save file names */ + strncpy(par->InpFname, f1, MAXFNAME); + strncpy(rep->Rpt1Fname, f2, MAXFNAME); + strncpy(out->OutFname, f3, MAXFNAME); + if (strlen(f3) > 0) + out->Outflag = SAVE; + else + out->Outflag = SCRATCH; + + /* Check that file names are not identical */ + if (strcomp(f1, f2) || strcomp(f1, f3) || + (strcomp(f2, f3) && (strlen(f2) > 0 || strlen(f3) > 0))) { + writecon(FMT04); + return (301); + } + + /* Attempt to open input and report files */ + if ((par->InFile = fopen(f1, "rt")) == NULL) { + writecon(FMT05); + writecon(f1); + return (302); + } + if (strlen(f2) == 0) + rep->RptFile = stdout; + else if ((rep->RptFile = fopen(f2, "wt")) == NULL) { + writecon(FMT06); + return (303); + } + + return (0); +} /* End of openfiles */ + +int openhydfile(EN_Project *p) +/*---------------------------------------------------------------- +** Input: none +** Output: none +** Returns: error code +** Purpose: opens file that saves hydraulics solution +**---------------------------------------------------------------- +*/ +{ + + EN_Network *net = &p->network; + out_file_t *out = &p->out_files; + time_options_t *time = &p->time_options; + + const int Nnodes = net->Nnodes; + const int Ntanks = net->Ntanks; + const int Nlinks = net->Nlinks; + const int Nvalves = net->Nvalves; + const int Npumps = net->Npumps; + + INT4 nsize[6]; /* Temporary array */ + INT4 magic; + INT4 version; + int errcode = 0; + + /* If HydFile currently open, then close it if its not a scratch file */ + if (out->HydFile != NULL) { + if (out->Hydflag == SCRATCH) + return (0); + fclose(out->HydFile); + } + + /* Use Hydflag to determine the type of hydraulics file to use. */ + /* Write error message if the file cannot be opened. */ + out->HydFile = NULL; + switch (out->Hydflag) { + case SCRATCH: + getTmpName(p, out->HydFname); + out->HydFile = fopen(out->HydFname, "w+b"); + break; + case SAVE: + out->HydFile = fopen(out->HydFname, "w+b"); + break; + case USE: + out->HydFile = fopen(out->HydFname, "rb"); + break; + } + if (out->HydFile == NULL) + return (305); + + /* If a previous hydraulics solution is not being used, then */ + /* save the current network size parameters to the file. */ + if (out->Hydflag != USE) { + magic = MAGICNUMBER; + version = ENGINE_VERSION; + nsize[0] = Nnodes; + nsize[1] = Nlinks; + nsize[2] = Ntanks; + nsize[3] = Npumps; + nsize[4] = Nvalves; + nsize[5] = (int)time->Dur; + fwrite(&magic, sizeof(INT4), 1, out->HydFile); + fwrite(&version, sizeof(INT4), 1, out->HydFile); + fwrite(nsize, sizeof(INT4), 6, out->HydFile); + } + + /* If a previous hydraulics solution is being used, then */ + /* make sure its network size parameters match those of */ + /* the current network. */ + if (out->Hydflag == USE) { + fread(&magic, sizeof(INT4), 1, out->HydFile); + if (magic != MAGICNUMBER) + return (306); + fread(&version, sizeof(INT4), 1, out->HydFile); + if (version != ENGINE_VERSION) + return (306); + if (fread(nsize, sizeof(INT4), 6, out->HydFile) < 6) + return (306); + if (nsize[0] != Nnodes || nsize[1] != Nlinks || nsize[2] != Ntanks || + nsize[3] != Npumps || nsize[4] != Nvalves || nsize[5] != time->Dur) + return (306); + p->save_options.SaveHflag = TRUE; + } + + /* Save current position in hydraulics file */ + /* where storage of hydraulic results begins */ + out->HydOffset = ftell(out->HydFile); + return (errcode); +} + +int openoutfile(EN_Project *p) +/*---------------------------------------------------------------- +** Input: none +** Output: none +** Returns: error code +** Purpose: opens binary output file. +**---------------------------------------------------------------- +*/ +{ + int errcode = 0; + + out_file_t *out = &p->out_files; + report_options_t *rep = &p->report; + + /* Close output file if already opened */ + if (out->OutFile != NULL) + fclose(out->OutFile); + out->OutFile = NULL; + if (out->TmpOutFile != NULL) + fclose(out->TmpOutFile); + out->TmpOutFile = NULL; + + if (out->Outflag == SCRATCH) { + remove(out->OutFname); + } + remove(out->TmpFname); + + /* If output file name was supplied, then attempt to */ + /* open it. Otherwise open a temporary output file. */ + // if (strlen(OutFname) != 0) + if (out->Outflag == SAVE) + { + if ((out->OutFile = fopen(out->OutFname, "w+b")) == NULL) { + writecon(FMT07); + errcode = 304; + } + } + // else if ( (OutFile = tmpfile()) == NULL) + else + { + getTmpName(p, out->OutFname); + if ((out->OutFile = fopen(out->OutFname, "w+b")) == NULL) + { + writecon(FMT08); + errcode = 304; + } + } + + /* Save basic network data & energy usage results */ + ERRCODE(savenetdata(p)); + out->OutOffset1 = ftell(out->OutFile); + ERRCODE(saveenergy(p)); + out->OutOffset2 = ftell(out->OutFile); + + /* Open temporary file if computing time series statistic */ + if (!errcode) { + if (rep->Tstatflag != SERIES) { + // if ( (TmpOutFile = tmpfile()) == NULL) errcode = 304; + getTmpName(p, out->TmpFname); + out->TmpOutFile = fopen(out->TmpFname, "w+b"); + if (out->TmpOutFile == NULL) + errcode = 304; + } else + out->TmpOutFile = out->OutFile; + } + return (errcode); +} + +/* +---------------------------------------------------------------- + Global memory management functions +---------------------------------------------------------------- +*/ + +void initpointers(EN_Project *p) +/*---------------------------------------------------------------- +** Input: none +** Output: none +** Purpose: initializes global pointers to NULL +**---------------------------------------------------------------- +*/ +{ + + hydraulics_t *hyd = &p->hydraulics; + quality_t *q = &p->quality; + EN_Network *n = &p->network; + parser_data_t *pars = &p->parser; + solver_t *s = &p->hydraulics.solver; + + hyd->NodeDemand = NULL; + q->NodeQual = NULL; + hyd->NodeHead = NULL; + hyd->LinkFlows = NULL; + q->PipeRateCoeff = NULL; + hyd->LinkStatus = NULL; + hyd->LinkSetting = NULL; + hyd->OldStat = NULL; + + n->Node = NULL; + n->Link = NULL; + n->Tank = NULL; + n->Pump = NULL; + n->Valve = NULL; + n->Pattern = NULL; + n->Curve = NULL; + n->Control = NULL; + n->Coord = NULL; + + hyd->X_tmp = NULL; + + pars->Patlist = NULL; + pars->Curvelist = NULL; + n->Adjlist = NULL; + + s->Aii = NULL; + s->Aij = NULL; + s->F = NULL; + s->P = NULL; + s->Y = NULL; + s->Order = NULL; + s->Row = NULL; + s->Ndx = NULL; + s->XLNZ = NULL; + s->NZSUB = NULL; + s->LNZ = NULL; + + n->NodeHashTable = NULL; + n->LinkHashTable = NULL; + initrules(&p->rules); +} + +int allocdata(EN_Project *p) +/*---------------------------------------------------------------- +** Input: none +** Output: none +** Returns: error code +** Purpose: allocates memory for network data structures +**---------------------------------------------------------------- +*/ +{ + int n; + int errcode = 0; + EN_Network *net; + hydraulics_t *hyd; + quality_t *qu; + parser_data_t *par; + + /* Allocate node & link ID hash tables */ + p->network.NodeHashTable = ENHashTableCreate(); + p->network.LinkHashTable = ENHashTableCreate(); + ERRCODE(MEMCHECK(p->network.NodeHashTable)); + ERRCODE(MEMCHECK(p->network.LinkHashTable)); + + net = &p->network; + hyd = &p->hydraulics; + qu = &p->quality; + par = &p->parser; + + /* Allocate memory for network nodes */ + /************************************************************* + NOTE: Because network components of a given type are indexed + starting from 1, their arrays must be sized 1 + element larger than the number of components. + *************************************************************/ + if (!errcode) { + n = par->MaxNodes + 1; + net->Node = (Snode *)calloc(n, sizeof(Snode)); + hyd->NodeDemand = (double *)calloc(n, sizeof(double)); + qu->NodeQual = (double *)calloc(n, sizeof(double)); + hyd->NodeHead = (double *)calloc(n, sizeof(double)); + ERRCODE(MEMCHECK(net->Node)); + ERRCODE(MEMCHECK(hyd->NodeDemand)); + ERRCODE(MEMCHECK(qu->NodeQual)); + ERRCODE(MEMCHECK(hyd->NodeHead)); + } + + /* Allocate memory for network links */ + if (!errcode) { + n = par->MaxLinks + 1; + net->Link = (Slink *)calloc(n, sizeof(Slink)); + hyd->LinkFlows = (double *)calloc(n, sizeof(double)); + hyd->LinkSetting = (double *)calloc(n, sizeof(double)); + hyd->LinkStatus = (StatType *)calloc(n, sizeof(StatType)); + ERRCODE(MEMCHECK(net->Link)); + ERRCODE(MEMCHECK(hyd->LinkFlows)); + ERRCODE(MEMCHECK(hyd->LinkSetting)); + ERRCODE(MEMCHECK(hyd->LinkStatus)); + } + + /* Allocate memory for tanks, sources, pumps, valves, */ + /* controls, demands, time patterns, & operating curves */ + if (!errcode) { + net->Tank = (Stank *)calloc(par->MaxTanks + 1, sizeof(Stank)); + net->Pump = (Spump *)calloc(par->MaxPumps + 1, sizeof(Spump)); + net->Valve = (Svalve *)calloc(par->MaxValves + 1, sizeof(Svalve)); + net->Control = (Scontrol *)calloc(par->MaxControls + 1, sizeof(Scontrol)); + net->Pattern = (Spattern *)calloc(par->MaxPats + 1, sizeof(Spattern)); + net->Curve = (Scurve *)calloc(par->MaxCurves + 1, sizeof(Scurve)); + if (p->parser.Coordflag == TRUE) { + net->Coord = (Scoord *)calloc(par->MaxNodes + 1, sizeof(Scoord)); + } + ERRCODE(MEMCHECK(net->Tank)); + ERRCODE(MEMCHECK(net->Pump)); + ERRCODE(MEMCHECK(net->Valve)); + ERRCODE(MEMCHECK(net->Control)); + ERRCODE(MEMCHECK(net->Pattern)); + ERRCODE(MEMCHECK(net->Curve)); + if (p->parser.Coordflag == TRUE) + ERRCODE(MEMCHECK(net->Coord)); + } + + /* Initialize pointers used in patterns, curves, and demand category lists */ + if (!errcode) { + for (n = 0; n <= par->MaxPats; n++) { + net->Pattern[n].Length = 0; + net->Pattern[n].F = NULL; + } + for (n = 0; n <= par->MaxCurves; n++) { + net->Curve[n].Npts = 0; + net->Curve[n].Type = -1; + net->Curve[n].X = NULL; + net->Curve[n].Y = NULL; + } + + for (n = 0; n <= par->MaxNodes; n++) { + // node demand + net->Node[n].D = NULL; + /* ini coord data */ + if (p->parser.Coordflag == TRUE) { + net->Coord[n].X = 0; + net->Coord[n].Y = 0; + net->Coord[n].HaveCoords = FALSE; + } + } + } + + /* Allocate memory for rule base (see RULES.C) */ + if (!errcode) + errcode = allocrules(p); + return (errcode); +} /* End of allocdata */ + +void freeTmplist(STmplist *t) +/*---------------------------------------------------------------- +** Input: t = pointer to start of a temporary list +** Output: none +** Purpose: frees memory used for temporary storage +** of pattern & curve data +**---------------------------------------------------------------- +*/ +{ + STmplist *tnext; + while (t != NULL) { + tnext = t->next; + freeFloatlist(t->x); + freeFloatlist(t->y); + free(t); + t = tnext; + } +} + +void freeFloatlist(SFloatlist *f) +/*---------------------------------------------------------------- +** Input: f = pointer to start of list of floats +** Output: none +** Purpose: frees memory used for storing list of floats +**---------------------------------------------------------------- +*/ +{ + SFloatlist *fnext; + while (f != NULL) { + fnext = f->next; + free(f); + f = fnext; + } +} + +void freedata(EN_Project *p) +/*---------------------------------------------------------------- +** Input: none +** Output: none +** Purpose: frees memory allocated for network data structures. +**---------------------------------------------------------------- +*/ +{ + + EN_Network *net = &p->network; + hydraulics_t *hyd = &p->hydraulics; + quality_t *qu = &p->quality; + parser_data_t *par = &p->parser; + + int j; + Pdemand demand, nextdemand; + Psource source; + + /* Free memory for computed results */ + free(hyd->NodeDemand); + free(qu->NodeQual); + free(hyd->NodeHead); + free(hyd->LinkFlows); + free(hyd->LinkSetting); + free(hyd->LinkStatus); + + /* Free memory for node data */ + if (net->Node != NULL) { + for (j = 0; j <= par->MaxNodes; j++) { + /* Free memory used for demand category list */ + demand = net->Node[j].D; + while (demand != NULL) { + nextdemand = demand->next; + free(demand); + demand = nextdemand; + } + /* Free memory used for WQ source data */ + source = net->Node[j].S; + if (source != NULL) + free(source); + } + free(net->Node); + } + + /* Free memory for other network objects */ + free(net->Link); + free(net->Tank); + free(net->Pump); + free(net->Valve); + free(net->Control); + + /* Free memory for time patterns */ + if (net->Pattern != NULL) { + for (j = 0; j <= par->MaxPats; j++) + free(net->Pattern[j].F); + free(net->Pattern); + } + + /* Free memory for curves */ + if (net->Curve != NULL) { + for (j = 0; j <= par->MaxCurves; j++) { + free(net->Curve[j].X); + free(net->Curve[j].Y); + } + free(net->Curve); + } + + /* Free memory for node coordinates */ + if (p->parser.Coordflag == TRUE) { + free(net->Coord); + } + + /* Free memory for rule base (see RULES.C) */ + freerules(p); + + /* Free hash table memory */ + if (net->NodeHashTable != NULL) + ENHashTableFree(net->NodeHashTable); + if (net->LinkHashTable != NULL) + ENHashTableFree(net->LinkHashTable); +} + +/* +---------------------------------------------------------------- + General purpose functions +---------------------------------------------------------------- +*/ + +/*** New function for 2.00.12 ***/ +char *getTmpName(EN_Project *p, char *fname) +// +// Input: fname = file name string +// Output: returns pointer to file name +// Purpose: creates a temporary file name with path prepended to it. +// +{ + out_file_t *out = &p->out_files; +// --- for Windows systems: +#ifdef WINDOWS + // --- use system function tmpnam() to create a temporary file name + char name[MAXFNAME + 1]; + int n; + + tmpnam(name); + + // --- if user supplied the name of a temporary directory, + // then make it be the prefix of the full file name + n = (int)strlen(out->TmpDir); + if (n > 0) { + strcpy(fname, out->TmpDir); + if (fname[n - 1] != '\\') + strcat(fname, "\\"); + } + + // --- otherwise, use the relative path notation as the file name + // prefix so that the file will be placed in the current directory + else { + strcpy(fname, ".\\"); + } + + // --- now add the prefix to the file name + strcat(fname, name); + +// --- for non-Windows systems: +#else + // --- use system function mkstemp() to create a temporary file name + strcpy(fname, "enXXXXXX"); + mkstemp(fname); +#endif + return fname; +} + +int strcomp(char *s1, char *s2) +/*--------------------------------------------------------------- +** Input: s1 = character string +** s2 = character string +** Output: none +** Returns: 1 if s1 is same as s2, 0 otherwise +** Purpose: case insensitive comparison of strings s1 & s2 +**--------------------------------------------------------------- +*/ +{ + int i; + for (i = 0; UCHAR(s1[i]) == UCHAR(s2[i]); i++) + if (!s1[i + 1] && !s2[i + 1]) + return (1); + return (0); +} /* End of strcomp */ + +double interp(int n, double x[], double y[], double xx) +/*---------------------------------------------------------------- +** Input: n = number of data pairs defining a curve +** x = x-data values of curve +** y = y-data values of curve +** xx = specified x-value +** Output: none +** Returns: y-value on curve at x = xx +** Purpose: uses linear interpolation to find y-value on a +** data curve corresponding to specified x-value. +** NOTE: does not extrapolate beyond endpoints of curve. +**---------------------------------------------------------------- +*/ +{ + int k, m; + double dx, dy; + + m = n - 1; /* Highest data index */ + if (xx <= x[0]) + return (y[0]); /* xx off low end of curve */ + for (k = 1; k <= m; k++) /* Bracket xx on curve */ + { + if (x[k] >= xx) /* Interp. over interval */ + { + dx = x[k] - x[k - 1]; + dy = y[k] - y[k - 1]; + if (ABS(dx) < TINY) + return (y[k]); + else + return (y[k] - (x[k] - xx) * dy / dx); + } + } + return (y[m]); /* xx off high end of curve */ +} /* End of interp */ + +int findnode(EN_Network *n, char *id) +/*---------------------------------------------------------------- +** Input: id = node ID +** Output: none +** Returns: index of node with given ID, or 0 if ID not found +** Purpose: uses hash table to find index of node with given ID +**---------------------------------------------------------------- +*/ +{ + return (ENHashTableFind(n->NodeHashTable, id)); +} + +int findlink(EN_Network *n, char *id) +/*---------------------------------------------------------------- +** Input: id = link ID +** Output: none +** Returns: index of link with given ID, or 0 if ID not found +** Purpose: uses hash table to find index of link with given ID +**---------------------------------------------------------------- +*/ +{ + return (ENHashTableFind(n->LinkHashTable, id)); +} + +int findtank(EN_Network *n, int index) +/*---------------------------------------------------------------- +** Input: index = node index +** Output: none +** Returns: index of tank with given node id, or NOTFOUND if tank not found +** Purpose: for use in the deletenode function +**---------------------------------------------------------------- +*/ +{ + int i; + for (i = 1; i <= n->Ntanks; i++) { + if (n->Tank[i].Node == index) { + return (i); + } + } + return (NOTFOUND); +} + +int findpump(EN_Network *n, int index) +/*---------------------------------------------------------------- +** Input: index = link ID +** Output: none +** Returns: index of pump with given link id, or NOTFOUND if pump not found +** Purpose: for use in the deletelink function +**---------------------------------------------------------------- +*/ +{ + int i; + for (i = 1; i <= n->Npumps; i++) { + if (n->Pump[i].Link == index) { + return (i); + } + } + return (NOTFOUND); +} + +int findvalve(EN_Network *n, int index) +/*---------------------------------------------------------------- +** Input: index = link ID +** Output: none +** Returns: index of valve with given link id, or NOTFOUND if valve not found +** Purpose: for use in the deletelink function +**---------------------------------------------------------------- +*/ +{ + int i; + for (i = 1; i <= n->Nvalves; i++) { + if (n->Valve[i].Link == index) { + return (i); + } + } + return (NOTFOUND); +} + +char *geterrmsg(int errcode, char *msg) +/*---------------------------------------------------------------- +** Input: errcode = error code +** Output: none +** Returns: pointer to string with error message +** Purpose: retrieves text of error message +**---------------------------------------------------------------- +*/ +{ + switch (errcode) { /* Warnings */ +#define DAT(code,enumer,string) case code: strcpy(msg, string); break; +#include "errors.dat" +#undef DAT + default: + strcpy(msg, ""); + } + return (msg); +} + +void errmsg(EN_Project *p, int errcode) +/*---------------------------------------------------------------- +** Input: errcode = error code +** Output: none +** Purpose: writes error message to report file +**---------------------------------------------------------------- +*/ +{ + if (errcode == 309) /* Report file write error - */ + { /* Do not write msg to file. */ + writecon("\n "); + writecon(geterrmsg(errcode,p->Msg)); + } else if (p->report.RptFile != NULL && p->report.Messageflag) { + writeline(p, geterrmsg(errcode,p->Msg)); + } +} + +void writecon(char *s) +/*---------------------------------------------------------------- +** Input: text string +** Output: none +** Purpose: writes string of characters to console +**---------------------------------------------------------------- +*/ +{ + + fprintf(stdout, "%s", s); + fflush(stdout); +} + +void writewin(void (*vp)(char *), char *s) +/*---------------------------------------------------------------- +** Input: text string +** Output: none +** Purpose: passes character string to viewprog() in +** application which calls the EPANET DLL +**---------------------------------------------------------------- +*/ +{ + char progmsg[MAXMSG + 1]; + if (vp != NULL) { + strncpy(progmsg, s, MAXMSG); + vp(progmsg); + } +} + +int DLLEXPORT EN_getnumdemands(EN_Project *p, int nodeIndex, int *numDemands) { + Pdemand d; + int n = 0; + /* Check for valid arguments */ + if (!p->Openflag) + return (102); + if (nodeIndex <= 0 || nodeIndex > p->network.Nnodes) + return (203); + for (d = p->network.Node[nodeIndex].D; d != NULL; d = d->next) + n++; + *numDemands = n; + return 0; +} + +int DLLEXPORT EN_getbasedemand(EN_Project *p, int nodeIndex, int demandIdx, EN_API_FLOAT_TYPE *baseDemand) { + Pdemand d; + int n = 1; + /* Check for valid arguments */ + if (!p->Openflag) + return (102); + if (nodeIndex <= 0 || nodeIndex > p->network.Nnodes) + return (203); + if (nodeIndex <= p->network.Njuncs) { + for (d = p->network.Node[nodeIndex].D; n < demandIdx && d != NULL; d = d->next) { + n++; + } + if (n != demandIdx) { + return (253); + } + *baseDemand = (EN_API_FLOAT_TYPE)(d->Base * p->Ucf[FLOW]); + } else { + *baseDemand = (EN_API_FLOAT_TYPE)(0.0); + } + return 0; +} + +int DLLEXPORT EN_setbasedemand(EN_Project *pr, int nodeIndex, int demandIdx, EN_API_FLOAT_TYPE baseDemand) { + + EN_Network *net = &pr->network; + Snode *Node = net->Node; + + const int Nnodes = net->Nnodes; + const int Njuncs = net->Njuncs; + + double *Ucf = pr->Ucf; + + Pdemand d; + int n = 1; + /* Check for valid arguments */ + if (!pr->Openflag) + return (102); + if (nodeIndex <= 0 || nodeIndex > Nnodes) + return (203); + if (nodeIndex <= Njuncs) { + for (d = Node[nodeIndex].D; n < demandIdx && d != NULL; d = d->next) + n++; + if (n != demandIdx) + return (253); + d->Base = baseDemand / Ucf[FLOW]; + } + return 0; +} + +int DLLEXPORT EN_getdemandpattern(EN_Project *p, int nodeIndex, int demandIdx, int *pattIdx) { + + EN_Network *net = &p->network; + Snode *Node = net->Node; + const int Nnodes = net->Nnodes; + + Pdemand d; + int n = 1; + /* Check for valid arguments */ + if (!p->Openflag) + return (102); + if (nodeIndex <= 0 || nodeIndex > Nnodes) + return (203); + for (d = Node[nodeIndex].D; n < demandIdx && d != NULL; d = d->next) + n++; + if (n != demandIdx) + return (253); + *pattIdx = d->Pat; + return 0; +} + +int DLLEXPORT EN_getaveragepatternvalue(EN_Project *p, int index, EN_API_FLOAT_TYPE *value) { + + EN_Network *net = &p->network; + + Spattern *Pattern = net->Pattern; + const int Npats = net->Npats; + + int i; + *value = 0.0; + if (!p->Openflag) + return (102); + if (index < 1 || index > Npats) + return (205); + // if (period < 1 || period > Pattern[index].Length) return(251); + for (i = 0; i < Pattern[index].Length; i++) { + *value += Pattern[index].F[i]; + } + *value /= (EN_API_FLOAT_TYPE)Pattern[index].Length; + return (0); +} + +int DLLEXPORT EN_setlinktype(EN_Project *p, char *id, EN_LinkType toType) { + int i; + EN_LinkType fromType; + + EN_Network *net = &p->network; + + if (!p->Openflag) + return (102); + + /* Check if a link with the id exists */ + if (EN_getlinkindex(p, id, &i) != 0) + return (215); + + /* Get the current type of the link */ + ENgetlinktype(i, &fromType); + if (fromType == toType) + return (0); + + /* Change link from Pipe */ + if (toType <= EN_PIPE) { + net->Npipes++; + } else if (toType == EN_PUMP) { + net->Npumps++; + net->Pump[net->Npumps].Link = i; + } else { + net->Nvalves++; + net->Valve[net->Nvalves].Link = i; + } + + if (fromType <= EN_PIPE) { + net->Npipes--; + } else if (fromType == EN_PUMP) { + net->Npumps--; + } + return 0; +} + +int DLLEXPORT EN_addnode(EN_Project *p, char *id, EN_NodeType nodeType) { + int i, nIdx; + int index; + struct Sdemand *demand; + EN_Network *net = &p->network; + hydraulics_t *hyd = &p->hydraulics; + quality_t *qu = &p->quality; + Stank *tank; + Snode *node; + Scoord *coord; + + /* Check if a node with same id already exists */ + if (!p->Openflag) + return (102); + if (ENgetnodeindex(id, &i) == 0) + return (215); + + /* Check that id name is not too long */ + if (strlen(id) > MAXID) + return (250); + + /* Grow arrays to accomodate the new values */ + net->Node = (Snode *)realloc(net->Node, (net->Nnodes + 2) * sizeof(Snode)); + net->Coord = realloc(net->Coord, (net->Nnodes + 2) * sizeof(Scoord)); + hyd->NodeDemand = (double *)realloc(hyd->NodeDemand, (net->Nnodes + 2) * sizeof(double)); + qu->NodeQual = (double *)realloc(qu->NodeQual, (net->Nnodes + 2) * sizeof(double)); + hyd->NodeHead = (double *)realloc(hyd->NodeHead, (net->Nnodes + 2) * sizeof(double)); + + nIdx = net->Nnodes + 1; + node = &net->Node[nIdx]; + coord = &net->Coord[nIdx]; + + if (nodeType == EN_JUNCTION) { + net->Njuncs++; + + demand = (struct Sdemand *)malloc(sizeof(struct Sdemand)); + demand->Base = 0.0; + demand->Pat = 0; + demand->next = NULL; + node->D = demand; + + // shift rest of Node array + for (index = net->Nnodes; index >= net->Njuncs; index--) { + ENHashTableUpdate(net->NodeHashTable, net->Node[index].ID, index + 1); + net->Node[index + 1] = net->Node[index]; + net->Coord[index + 1] = net->Coord[index]; + } + // shift indices of Tank array + for (index = 1; index <= net->Ntanks; index++) { + net->Tank[index].Node += 1; + } + + // shift indices of Links, if necessary + for (index = 1; index <= net->Nlinks; index++) { + if (net->Link[index].N1 > net->Njuncs - 1) { + net->Link[index].N1 += 1; + } + if (net->Link[index].N2 > net->Njuncs - 1) { + net->Link[index].N2 += 1; + } + } + } else { + net->Ntanks++; + + /* resize tanks array */ + net->Tank = (Stank *)realloc(net->Tank, (net->Ntanks + 1) * sizeof(Stank)); + + tank = &net->Tank[net->Ntanks]; + + /* set default values for new tank or reservoir */ + tank->Node = nIdx; + tank->Pat = 0; + if (nodeType == EN_TANK) { + tank->A = 1.0; + } else { + tank->A = 0; + } + tank->Hmin = 0; + tank->Hmax = 0; + tank->H0 = 0; + tank->Vmin = 0; + tank->Vmax = 0; + tank->V0 = 0; + tank->Kb = 0; + tank->V = 0; + tank->C = 0; + tank->Pat = 0; + tank->Vcurve = 0; + tank->MixModel = 0; + tank->V1max = 10000; + } + + net->Nnodes++; + + /* set default values for new node */ + strncpy(node->ID, id, MAXID); + + node->El = 0; + node->S = NULL; + node->C0 = 0; + node->Ke = 0; + node->Rpt = 0; + + + coord->HaveCoords = FALSE; + coord->X = 0; + coord->Y = 0; + + /* Insert new node into hash table */ + ENHashTableInsert(net->NodeHashTable, node->ID, nIdx); /* see HASH.C */ + return (0); +} + +int DLLEXPORT EN_addlink(EN_Project *p, char *id, EN_LinkType linkType, char *fromNode, + char *toNode) { + int i, n; + int N1, N2; + EN_Network *net = &p->network; + hydraulics_t *hyd = &p->hydraulics; + Slink *link; + Spump *pump; + + /* Check if a link with same id already exists */ + if (!p->Openflag) + return (102); + if (ENgetlinkindex(id, &i) == 0) + return (215); + + /* Lookup the from and to nodes */ + N1 = ENHashTableFind(net->NodeHashTable, fromNode); + N2 = ENHashTableFind(net->NodeHashTable, toNode); + + if (N1 == 0 || N2 == 0) { + return (203); + } + + /* Check that id name is not too long */ + if (strlen(id) > MAXID) + return (250); + + net->Nlinks++; + n = net->Nlinks; + + /* Grow arrays to accomodate the new value */ + net->Link = (Slink *)realloc(net->Link, (net->Nlinks + 1) * sizeof(Slink)); + hyd->LinkFlows = (double *)realloc(hyd->LinkFlows, (net->Nlinks + 1) * sizeof(double)); + hyd->LinkSetting = (double *)realloc(hyd->LinkSetting, (net->Nlinks + 1) * sizeof(double)); + hyd->LinkStatus = (StatType *)realloc(hyd->LinkStatus, (net->Nlinks + 1) * sizeof(StatType)); + + link = &net->Link[net->Nlinks]; + + strncpy(net->Link[n].ID, id, MAXID); + + if (linkType <= EN_PIPE) { + net->Npipes++; + } else if (linkType == EN_PUMP) { + net->Npumps++; + /* Grow pump array to accomodate the new value */ + net->Pump = (Spump *)realloc(net->Pump, (net->Npumps + 1) * sizeof(Spump)); + pump = &net->Pump[net->Npumps]; + + pump->Link = n; + pump->Ptype = 0; + pump->Q0 = 0; + pump->Qmax = 0; + pump->Hmax = 0; + pump->H0 = 0; + pump->R = 0; + pump->N = 0; + pump->Hcurve = 0; + pump->Ecurve = 0; + pump->Upat = 0; + pump->Epat = 0; + pump->Ecost = 0; + pump->Energy[5] = MISSING; + + } else { + + /* Grow valve array to accomodate the new value */ + net->Nvalves++; + net->Valve = (Svalve *)realloc(net->Valve, (net->Nvalves + 1) * sizeof(Svalve)); + net->Valve[net->Nvalves].Link = n; + } + + link->Type = linkType; + link->N1 = N1; + link->N2 = N2; + + if (linkType == EN_PUMP) { + link->Kc = 1.0; // Speed factor + link->Km = 0.0; // Horsepower + link->Len = 0.0; + } else if (linkType <= EN_PIPE) { // pipe or cvpipe + link->Diam = 10 / p->Ucf[DIAM]; + link->Kc = 100; // Rough. coeff + link->Km = 0.0; // Loss coeff + link->Len = 1000; + } else { // Valve + link->Diam = 10 / p->Ucf[DIAM]; + link->Kc = 0.0; // Valve setting. + link->Km = 0.0; // Loss coeff + link->Len = 0.0; + } + // link->Len = 0.0; + // link->Kc = 0.01; + // link->Km = 0; + link->Kb = 0; + link->Kw = 0; + link->R = 0; + link->Rc = 0; + link->Rpt = 0; + + if (linkType == EN_CVPIPE) { + link->Stat = OPEN; + } + else { + link->Stat = CLOSED; + } + + ENHashTableInsert(net->LinkHashTable, link->ID, n); + return (0); +} + +int DLLEXPORT EN_deletelink(EN_Project *p, int index) { + int i; + EN_LinkType linkType; + EN_Network *net = &p->network; + Slink *link; + + if (!p->Openflag) + return (102); + if (index <= 0 || index > net->Nlinks) + return (203); + + EN_getlinktype(p, index, &linkType); + + link = &net->Link[index]; + + // remove from hash table + ENHashTableDelete(net->LinkHashTable, link->ID); + ENHashTableFind(net->LinkHashTable, link->ID); + + // shift link and pump arrays to re-sort link indices + net->Nlinks--; + for (i = index; i <= net->Nlinks - 1; i++) { + net->Link[i] = net->Link[i + 1]; + // update hashtable + ENHashTableUpdate(net->LinkHashTable, net->Link[i].ID, i); + } + for (i = 1; i <= net->Npumps; i++) { + if (net->Pump[i].Link > index) { + net->Pump[i].Link -= 1; + } + } + for (i = 1; i <= net->Nvalves; i++) { + if (net->Valve[i].Link > index) { + net->Valve[i].Link -= 1; + } + } + + + // update pumps + if (linkType == EN_PUMP) { + int pumpindex = findpump(net,index); + for (i = pumpindex; i <= net->Npumps - 1; i++) { + net->Pump[i] = net->Pump[i + 1]; + } + net->Npumps--; + } + + // update valves + if (linkType > EN_PUMP) { + int valveindex = findvalve(net,index); + for (i = valveindex; i <= net->Nvalves - 1; i++) { + net->Valve[i] = net->Valve[i + 1]; + } + net->Nvalves--; + } + + return 0; +} + +int DLLEXPORT EN_deletenode(EN_Project *p, int index) { + + EN_Network *net = &p->network; + + int i, nodeType; + + if (!p->Openflag) + return (102); + if (index <= 0 || index > net->Nnodes) + return (203); + + EN_getnodetype(p, index, &nodeType); + + // remove from hash table + ENHashTableDelete(net->NodeHashTable, net->Node[index].ID); + + // shift node and coord array to remove node + for (i = index; i <= net->Nnodes - 1; i++) { + net->Node[i] = net->Node[i + 1]; + net->Coord[i] = net->Coord[i + 1]; + // update hashtable + ENHashTableUpdate(net->NodeHashTable, net->Node[i].ID, i); + } + + // update tank array + if (nodeType != EN_JUNCTION) { + int tankindex = findtank(net, index); + for (i = tankindex; i <= net->Ntanks - 1; i++) { + net->Tank[i] = net->Tank[i + 1]; + } + } + + // update tank node indices + for (i = 1; i <= net->Ntanks; i++) { + if (net->Tank[i].Node > index) { + net->Tank[i].Node -= 1; + } + } + + // gather a list of link ids to remove in reverse order, + // so that re-shuffling doesn't destroy the indexing + for (i = net->Nlinks; i >= 1; i--) { + if (net->Link[i].N1 == index || net->Link[i].N2 == index) { + EN_deletelink(p,i); + } + } + + for (i = 1; i <= net->Nlinks; i++) { + if (net->Link[i].N1 > index) { + net->Link[i].N1 -= 1; + } + if (net->Link[i].N2 > index) { + net->Link[i].N2 -= 1; + } + } + + // update counters + if (nodeType == EN_JUNCTION) { + net->Njuncs--; + } else { + net->Ntanks--; + } + + net->Nnodes--; + + return (0); +} + +int DLLEXPORT EN_getrule(EN_Project *pr, int index, int *nPremises, int *nTrueActions, int *nFalseActions, EN_API_FLOAT_TYPE *priority) +/*---------------------------------------------------------------- +** Input: index = index of the rule +** nPremises = number of conditions (IF AND OR) +** nTrueActions = number of actions with true conditions (THEN) +** nFalseActions = number of actions with false conditions (ELSE) +** priority = rule priority +** Output: none +** Returns: error code +**---------------------------------------------------------------- +*/ +{ + int count; + Premise *p; + Action *c; + + EN_Network *net = &pr->network; + + if (index > net->Nrules) + return (257); + *priority = (EN_API_FLOAT_TYPE)pr->rules.Rule[index].priority; + count = 1; + p = pr->rules.Rule[index].Pchain; + while (p->next != NULL) { + count++; + p = p->next; + } + *nPremises = count; + count = 1; + c = pr->rules.Rule[index].Tchain; + while (c->next != NULL) { + count++; + c = c->next; + } + *nTrueActions = count; + + c = pr->rules.Rule[index].Fchain; + count = 0; + if (c != NULL) { + count = 1; + while (c->next != NULL) { + count++; + c = c->next; + } + } + *nFalseActions = count; + return (0); +} + +int DLLEXPORT EN_getpremise(EN_Project *pr, int indexRule, int idxPremise, int *logop, + int *object, int *indexObj, int *variable, + int *relop, int *status, EN_API_FLOAT_TYPE *value) { + int count = 1, error = 0, nPremises, a, b; + EN_API_FLOAT_TYPE priority; + Premise *p; + + + + if (indexRule > pr->network.Nrules) { + return (257); + } + error = EN_getrule(pr, indexRule, &nPremises, &a, &b, &priority); + if (idxPremise > nPremises) { + return (258); + } + + p = pr->rules.Rule[indexRule].Pchain; + while (count < idxPremise) { + count++; + p = p->next; + } + *logop = p->logop; + *object = p->object; + *indexObj = p->index; + *variable = p->variable; + *relop = p->relop; + *status = p->status; + *value = p[0].value; + return (0); +} + +int DLLEXPORT EN_setrulepriority(EN_Project *pr, int index, EN_API_FLOAT_TYPE priority) +/*---------------------------------------------------------------- +** Input: index = index of the rule +** priority = rule priority +** Output: none +** Returns: error code +**---------------------------------------------------------------- +*/ +{ + if (index > pr->network.Nrules) { + return (257); + } + pr->rules.Rule[index].priority = priority; + + return (0); +} + +int DLLEXPORT EN_setpremise(EN_Project *pr, int indexRule, int indexPremise, int logop, + int object, int indexObj, int variable, int relop, + int status, EN_API_FLOAT_TYPE value) { + int count = 1, error = 0, nPremises, a, b; + EN_API_FLOAT_TYPE priority; + Premise *p; + + + + if (indexRule > pr->network.Nrules) { + return (257); + } + error = EN_getrule(pr, indexRule, &nPremises, &a, &b, &priority); + if (indexPremise > nPremises) { + return (258); + } + p = pr->rules.Rule[indexRule].Pchain; + while (count < indexPremise) { + count++; + p = p->next; + } + p->logop = logop; + p->object = object; + p->index = indexObj; + p->variable = variable; + p->relop = relop; + p->status = status; + p->value = value; + return (0); +} + +int DLLEXPORT EN_setpremiseindex(EN_Project *pr, int indexRule, int indexPremise, int indexObj) { + int count = 1, error = 0, nPremises, a, b; + EN_API_FLOAT_TYPE priority; + Premise *p; + + if (indexRule > pr->network.Nrules) + return (257); + error = EN_getrule(pr, indexRule, &nPremises, &a, &b, &priority); + if (indexPremise > nPremises) { + return (258); + } + p = pr->rules.Rule[indexRule].Pchain; + while (count < indexPremise) { + count++; + p = p->next; + } + p->index = indexObj; + return (0); +} + +int DLLEXPORT EN_setpremisestatus(EN_Project *pr, int indexRule, int indexPremise, int status) { + int count = 1, error = 0, nPremises, a, b; + EN_API_FLOAT_TYPE priority; + Premise *p; + + if (indexRule > pr->network.Nrules) { + return (257); + } + error = EN_getrule(pr, indexRule, &nPremises, &a, &b, &priority); + if (indexPremise > nPremises) { + return (258); + } + p = pr->rules.Rule[indexRule].Pchain; + while (count < indexPremise) { + count++; + p = p->next; + } + p->status = status; + return (0); +} + +int DLLEXPORT EN_setpremisevalue(EN_Project *pr, int indexRule, int indexPremise, + EN_API_FLOAT_TYPE value) { + int count = 1, error = 0, nPremises, a, b; + EN_API_FLOAT_TYPE priority; + Premise *p; + + if (indexRule > pr->network.Nrules) + return (257); + error = EN_getrule(pr, indexRule, &nPremises, &a, &b, &priority); + if (indexPremise > nPremises) { + return (258); + } + p = pr->rules.Rule[indexRule].Pchain; + while (count < indexPremise) { + count++; + p = p->next; + } + p->value = value; + return (0); +} + +int DLLEXPORT EN_gettrueaction(EN_Project *pr, int indexRule, int indexAction, int *indexLink, + int *status, EN_API_FLOAT_TYPE *setting) { + int count = 1, error = 0, nTrueAction, c, b; + EN_API_FLOAT_TYPE priority; + Action *a; + + if (indexRule > pr->network.Nrules) { + return (252); + } + error = EN_getrule(pr, indexRule, &c, &nTrueAction, &b, &priority); + if (indexAction > nTrueAction) { + return (253); + } + a = pr->rules.Rule[indexRule].Tchain; + while (count < indexAction) { + count++; + a = a->next; + } + *indexLink = a->link; + *status = a->status; + *setting = a->setting; + return (0); +} + +int DLLEXPORT EN_settrueaction(EN_Project *pr, int indexRule, int indexAction, int indexLink, + int status, EN_API_FLOAT_TYPE setting) { + int count = 1, error = 0, nTrueAction, c, b; + EN_API_FLOAT_TYPE priority; + Action *a; + + if (indexRule > pr->network.Nrules) { + return (257); + } + error = EN_getrule(pr, indexRule, &c, &nTrueAction, &b, &priority); + if (indexAction > nTrueAction) { + return (258); + } + a = pr->rules.Rule[indexRule].Tchain; + while (count < indexAction) { + count++; + a = a->next; + } + a->link = indexLink; + a->status = status; + a->setting = setting; + return (0); +} + +int DLLEXPORT EN_getfalseaction(EN_Project *pr, int indexRule, int indexAction, int *indexLink, + int *status, EN_API_FLOAT_TYPE *setting) { + int count = 1, error = 0, nFalseAction, c, b; + EN_API_FLOAT_TYPE priority; + Action *a; + + if (indexRule > pr->network.Nrules) { + return (257); + } + error = EN_getrule(pr, indexRule, &c, &b, &nFalseAction, &priority); + if (indexAction > nFalseAction) { + return (258); + } + a = pr->rules.Rule[indexRule].Fchain; + while (count < indexAction) { + count++; + a = a->next; + } + *indexLink = a->link; + *status = a->status; + *setting = a->setting; + return (0); +} + +int DLLEXPORT EN_setfalseaction(EN_Project *pr, int indexRule, int indexAction, int indexLink, + int status, EN_API_FLOAT_TYPE setting) { + int count = 1, error = 0, nFalseAction, c, b; + EN_API_FLOAT_TYPE priority; + Action *a; + + if (indexRule > pr->network.Nrules) { + return (257); + } + error = EN_getrule(pr, indexRule, &c, &b, &nFalseAction, &priority); + if (indexAction > nFalseAction) { + return (258); + } + a = pr->rules.Rule[indexRule].Fchain; + while (count < indexAction) { + count++; + a = a->next; + } + a->link = indexLink; + a->status = status; + a->setting = setting; + + return (0); +} + +int DLLEXPORT EN_getruleID(EN_Project *pr, int indexRule, char *id) { + strcpy(id, ""); + if (!pr->Openflag) + return (102); + if (indexRule < 1 || indexRule > pr->network.Nrules) + return (257); + strcpy(id, pr->rules.Rule[indexRule].label); + return (0); +} + +/*************************** END OF EPANET.C ***************************/ diff --git a/src/errors.dat b/src/errors.dat new file mode 100644 index 0000000..bd91e4b --- /dev/null +++ b/src/errors.dat @@ -0,0 +1,75 @@ + +DAT(0,EN_OK,"ok") +DAT(101,ENERR_INSUF_MEM,"insufficient memory available") +DAT(102,ENERR_NO_NET_DATA,"no network data available") +DAT(103,ENERR_HYD_NOT_INIT,"hydraulics not initialized") +DAT(104,ENERR_NO_HYD,"no hydraulics for water quality analysis") +DAT(105,ENERR_WQ_NOT_INIT,"water quality not initialized") +DAT(106,ENERR_NO_RESULT,"no results saved to report on") +DAT(107,ENERR_HYD_EXT_FILE,"hydraulics supplied from external file") +DAT(108,ENERR_CANT_USE_EXT_FILE,"cannot use external file while hydraulics solver is active") +DAT(109,ENERR_CANT_CHANGE_TIME_PARAM,"cannot change time parameter when solver is active") +DAT(110,ENERR_CANT_SOLVE_HYD,"cannot solve network hydraulic equations") +DAT(120,ENERR_CANT_SOLVE_WQ,"cannot solve water quality transport equations") + +DAT(200,ENERR_INP_ERR,"one or more errors in input file") +DAT(201,ENERR_SYNTAX_LINE,"syntax error in following line of section") +DAT(202,ENERR_ILLEGAL_NUM,"function call contains illegal numeric value") +DAT(203,ENERR_UNDEF_NODE,"function call refers to undefined node") +DAT(204,ENERR_UNDEF_LINK,"function call refers to undefined link") +DAT(205,ENERR_UNDEF_PAT,"function call refers to undefined time pattern") +DAT(206,ENERR_UNDEF_CURVE,"function call refers to undefined curve") +DAT(207,ENERR_CANT_CONTROL_CV,"function call attempts to control a CV") + +DAT(208,ENERR_SPEC_UNDEF_NODE,"undefined Node") +DAT(209,ENERR_ILLEGAL_VAL_NODE,"illegal value for Node") +DAT(210,ENERR_SPEC_UNDEF_LINK,"specified for undefined Link") +DAT(211,ENERR_ILLEGAL_VAL_LINK,"illegal value for Link") +DAT(212,ENERR_TRACE_NODE,"trace node") +DAT(213,ENERR_ILLEGAL_OPT,"illegal option value in section") +DAT(214,ENERR_TOO_MANY_CHAR,"following line of section contains too many characters") +DAT(215,ENERR_DUPLICATE_ID,"duplicate ID") +DAT(216,ENERR_UNDEF_PUMP,"data specified for undefined Pump") +DAT(217,ENERR_INVALID_DATA_PUMP,"invalid data for Pump") +DAT(219,ENERR_ILLEGAL_CON_TANK,"illegally connected to a tank") +DAT(220,ENERR_ILLEGAL_CON_VALVE,"illegally connected to another valve") + +DAT(222,ENERR_SAME_START_END,"same start and end nodes") + +DAT(223,ENERR_NOT_ENOUGH_NODES,"not enough nodes in network") +DAT(224,ENERR_NO_TANKS_RES,"no tanks or reservoirs in network") +DAT(225,ENERR_INVALID_LEVELS,"invalid lower/upper levels for Tank") +DAT(226,ENERR_NO_HEAD_CURVE,"no head curve supplied for Pump") +DAT(227,ENERR_INVALID_HEAD_CURVE,"invalid head curve for Pump") +DAT(230,ENERR_X_NONINCREASING,"Curve has nonincreasing x-values") +DAT(233,ENERR_NODE_UNCONNECTED,"Node is unconnected") +DAT(240,ENERR_UNDEF_SOURCE,"undefined source") +DAT(241,ENERR_UNDEF_CONTROL,"refers to undefined control") +DAT(250,ENERR_INVALID_FORMAT,"function call contains invalid format") +DAT(251,ENERR_INVALID_CODE,"function call contains invalid parameter code") + +DAT(253,ENERR_NO_DEMAND_CAT,"Function call error - No such demand category index") +DAT(254,ENERR_NO_COORDS,"Function call error - Node have no coordinates") +DAT(255,ENERR_COORDS_NOT_LOADED,"Function call error - Coordinates were not loaded") + +DAT(257,ENERR_NO_RULE,"rule does not exist") +DAT(258,ENERR_NO_CONDITION_ACTION,"condition or action index specified in rule does not exist") + +DAT(301,ENERR_FILES_ARE_SAME,"identical file names") +DAT(302,ENERR_CANT_OPEN_INP,"cannot open input file") +DAT(303,ENERR_CANT_OPEN_RPT,"cannot open report file") +DAT(304,ENERR_CANT_OPEN_BIN,"cannot open binary output file") +DAT(305,ENERR_CANT_OPEN_HYD,"cannot open hydraulics file") +DAT(306,ENERR_HYD_FILE_NOMATCH,"hydraulics file does not match network data") +DAT(307,ENERR_CANT_READ_HYD,"cannot read hydraulics file") +DAT(308,ENERR_CANT_SAVE_RES,"cannot save results to file") +DAT(309,ENERR_CANT_SAVE_RPT,"cannot save results to report file") + +DAT(401,ENERR_QSTEP_NOT_DIVISIBLE,"Qstep is not dividable by Hstep") + + +DAT(411,ENERR_NO_MEM_ALLOC,"no memory allocated for results") +DAT(412,ENERR_NO_RESULTS_NO_FILE,"no results; binary file hasn't been opened") +DAT(435,ENERR_RUN_TERMINATED,"run terminated; no results in binary file") + + diff --git a/src/funcs.h b/src/funcs.h index 51ccc20..b006c78 100755 --- a/src/funcs.h +++ b/src/funcs.h @@ -1,288 +1,291 @@ -/* -************************************************************************** - -FUNCS.H -- Function Prototypes for EPANET Program - -VERSION: 2.00 -DATE: 5/8/00 - 9/25/00 - 10/25/00 - 12/29/00 - 3/1/01 - 2/14/08 (2.00.12) -AUTHOR: L. Rossman - US EPA - NRMRL - -************************************************************************** -*/ - -/*****************************************************************/ -/* Most float arguments have been changed to double - 7/3/07 */ -/*****************************************************************/ - -/* ------- EPANET.C --------------------*/ -/* -** NOTE: The exportable functions that can be called -** via the DLL are prototyped in TOOLKIT.H. -*/ - -#ifndef FUNCS_H -#define FUNCS_H - -void initpointers(void); /* Initializes pointers */ -int allocdata(void); /* Allocates memory */ -void freeTmplist(STmplist *); /* Frees items in linked list */ -void freeFloatlist(SFloatlist *); /* Frees list of floats */ -void freedata(void); /* Frees allocated memory */ -int openfiles(char *,char *,char *); /* Opens input & report files */ -int openhydfile(void); /* Opens hydraulics file */ -int openoutfile(void); /* Opens binary output file */ -int strcomp(char *, char *); /* Compares two strings */ -char* getTmpName(char* fname); /* Gets temporary file name */ //(2.00.12 - LR) -double interp(int, double *, /* Interpolates a data curve */ - double *, double); -int findnode(char *); /* Finds node's index from ID */ -int findlink(char *); /* Finds link's index from ID */ -char* geterrmsg(int); /* Gets text of error message */ -void errmsg(int); /* Reports program error */ -void writecon(char *); /* Writes text to console */ -void writewin(char *); /* Passes text to calling app */ - -/* ------- INPUT1.C --------------------*/ -int getdata(void); /* Gets network data */ -void setdefaults(void); /* Sets default values */ -void initreport(void); /* Initializes report options */ -void adjustdata(void); /* Adjusts input data */ -int inittanks(void); /* Initializes tank levels */ -void initunits(void); /* Determines reporting units */ -void convertunits(void); /* Converts data to std. units*/ - -/* -------- INPUT2.C -------------------*/ -int netsize(void); /* Determines network size */ -int readdata(void); /* Reads in network data */ -int newline(int, char *); /* Processes new line of data */ -int addnodeID(int, char *); /* Adds node ID to data base */ -int addlinkID(int, char *); /* Adds link ID to data base */ -int addpattern(char *); /* Adds pattern to data base */ -int addcurve(char *); /* Adds curve to data base */ -STmplist *findID(char *, STmplist *); /* Locates ID on linked list */ -int unlinked(void); /* Checks for unlinked nodes */ -int getpumpparams(void); /* Computes pump curve coeffs.*/ -int getpatterns(void); /* Gets pattern data from list*/ -int getcurves(void); /* Gets curve data from list */ -int findmatch(char *,char *[]); /* Finds keyword in line */ -int match(char *, char *); /* Checks for word match */ -int gettokens(char *); /* Tokenizes input line */ -int getfloat(char *, double *); /* Converts string to double */ -double hour(char *, char *); /* Converts time to hours */ -int setreport(char *); /* Processes reporting command*/ -void inperrmsg(int,int,char *); /* Input error message */ - -/* ---------- INPUT3.C -----------------*/ -int juncdata(void); /* Processes junction data */ -int tankdata(void); /* Processes tank data */ -int pipedata(void); /* Processes pipe data */ -int pumpdata(void); /* Processes pump data */ -int valvedata(void); /* Processes valve data */ -int patterndata(void); /* Processes pattern data */ -int curvedata(void); /* Processes curve data */ -int coordata(void); /* Processes coordinate data */ -int demanddata(void); /* Processes demand data */ -int controldata(void); /* Processes simple controls */ -int energydata(void); /* Processes energy data */ -int sourcedata(void); /* Processes source data */ -int emitterdata(void); /* Processes emitter data */ -int qualdata(void); /* Processes quality data */ -int reactdata(void); /* Processes reaction data */ -int mixingdata(void); /* Processes tank mixing data */ -int statusdata(void); /* Processes link status data */ -int reportdata(void); /* Processes report options */ -int timedata(void); /* Processes time options */ -int optiondata(void); /* Processes analysis options */ -int optionchoice(int); /* Processes option choices */ -int optionvalue(int); /* Processes option values */ -int getpumpcurve(int); /* Constructs a pump curve */ -int powercurve(double, double, double,/* Coeffs. of power pump curve*/ - double, double, double *, - double *, double *); -int valvecheck(int, int, int); /* Checks valve placement */ -void changestatus(int, char, double); /* Changes status of a link */ - -/* -------------- RULES.C --------------*/ -void initrules(void); /* Initializes rule base */ -void addrule(char *); /* Adds rule to rule base */ -int allocrules(void); /* Allocates memory for rule */ -int ruledata(void); /* Processes rule input data */ -int checkrules(long); /* Checks all rules */ -void freerules(void); /* Frees rule base memory */ - -/* ------------- REPORT.C --------------*/ -int writereport(void); /* Writes formatted report */ -void writelogo(void); /* Writes program logo */ -void writesummary(void); /* Writes network summary */ -void writehydstat(int,double); /* Writes hydraulic status */ -void writeenergy(void); /* Writes energy usage */ -int writeresults(void); /* Writes node/link results */ -void writeheader(int,int); /* Writes heading on report */ -void writeline(char *); /* Writes line to report file */ -void writerelerr(int, double); /* Writes convergence error */ -void writestatchange(int,char,char); /* Writes link status change */ -void writecontrolaction(int, int); /* Writes control action taken*/ -void writeruleaction(int, char *); /* Writes rule action taken */ -int writehydwarn(int,double); /* Writes hydraulic warnings */ -void writehyderr(int); /* Writes hydraulic error msg.*/ -int disconnected(void); /* Checks for disconnections */ -void marknodes(int, int *, char *); /* Identifies connected nodes */ -void getclosedlink(int, char *); /* Finds a disconnecting link */ -void writelimits(int,int); /* Writes reporting limits */ -int checklimits(double *,int,int); /* Checks variable limits */ -void writetime(char *); /* Writes current clock time */ -char *clocktime(char *, long); /* Converts time to hrs:min */ -char *fillstr(char *, char, int); /* Fills string with character*/ -int getnodetype(int); /* Determines node type */ - -/* --------- HYDRAUL.C -----------------*/ -int openhyd(void); /* Opens hydraulics solver */ - -/*** Updated 3/1/01 ***/ -void inithyd(int); /* Re-sets initial conditions */ - -int runhyd(long *); /* Solves 1-period hydraulics */ -int nexthyd(long *); /* Moves to next time period */ -void closehyd(void); /* Closes hydraulics solver */ -int allocmatrix(void); /* Allocates matrix coeffs. */ -void freematrix(void); /* Frees matrix coeffs. */ -void initlinkflow(int, char, double); /* Initializes link flow */ -void setlinkflow(int, double); /* Sets link flow via headloss*/ -void setlinkstatus(int, char, char *, /* Sets link status */ - double *); -void setlinksetting(int, double, /* Sets pump/valve setting */ - char *, double *); -void resistance(int); /* Computes resistance coeff. */ -void demands(void); /* Computes current demands */ -int controls(void); /* Controls link settings */ -long timestep(void); /* Computes new time step */ -void tanktimestep(long *); /* Time till tanks fill/drain */ -void controltimestep(long *); /* Time till control action */ -void ruletimestep(long *); /* Time till rule action */ -void addenergy(long); /* Accumulates energy usage */ -void getenergy(int, double *, double *); /* Computes link energy use */ -void tanklevels(long); /* Computes new tank levels */ -double tankvolume(int,double); /* Finds tank vol. from grade */ -double tankgrade(int,double); /* Finds tank grade from vol. */ -int netsolve(int *,double *); /* Solves network equations */ -int badvalve(int); /* Checks for bad valve */ -int valvestatus(void); /* Updates valve status */ -int linkstatus(void); /* Updates link status */ -char cvstatus(char,double,double); /* Updates CV status */ -char pumpstatus(int,double); /* Updates pump status */ -char prvstatus(int,char,double, /* Updates PRV status */ - double,double); -char psvstatus(int,char,double, /* Updates PSV status */ - double,double); -char fcvstatus(int,char,double, /* Updates FCV status */ - double); -void tankstatus(int,int,int); /* Checks if tank full/empty */ -int pswitch(void); /* Pressure switch controls */ -double newflows(void); /* Updates link flows */ -void newcoeffs(void); /* Computes matrix coeffs. */ -void linkcoeffs(void); /* Computes link coeffs. */ -void nodecoeffs(void); /* Computes node coeffs. */ -void valvecoeffs(void); /* Computes valve coeffs. */ -void pipecoeff(int); /* Computes pipe coeff. */ -double DWcoeff(int, double *); /* Computes D-W coeff. */ -void pumpcoeff(int); /* Computes pump coeff. */ - -/*** Updated 10/25/00 ***/ -/*** Updated 12/29/00 ***/ -void curvecoeff(int,double,double *, /* Computes curve coeffs. */ - double *); - -void gpvcoeff(int); /* Computes GPV coeff. */ -void pbvcoeff(int); /* Computes PBV coeff. */ -void tcvcoeff(int); /* Computes TCV coeff. */ -void prvcoeff(int,int,int); /* Computes PRV coeff. */ -void psvcoeff(int,int,int); /* Computes PSV coeff. */ -void fcvcoeff(int,int,int); /* Computes FCV coeff. */ -void emittercoeffs(void); /* Computes emitter coeffs. */ -double emitflowchange(int); /* Computes new emitter flow */ - -/* ----------- SMATRIX.C ---------------*/ -int createsparse(void); /* Creates sparse matrix */ -int allocsparse(void); /* Allocates matrix memory */ -void freesparse(void); /* Frees matrix memory */ -int buildlists(int); /* Builds adjacency lists */ -int paralink(int, int, int); /* Checks for parallel links */ -void xparalinks(void); /* Removes parallel links */ -void freelists(void); /* Frees adjacency lists */ -void countdegree(void); /* Counts links at each node */ -int reordernodes(void); /* Finds a node re-ordering */ -int mindegree(int, int); /* Finds min. degree node */ -int growlist(int); /* Augments adjacency list */ -int newlink(Padjlist); /* Adds fill-ins for a node */ -int linked(int, int); /* Checks if 2 nodes linked */ -int addlink(int, int, int); /* Creates new fill-in */ -int storesparse(int); /* Stores sparse matrix */ -int ordersparse(int); /* Orders matrix storage */ -void transpose(int,int *,int *, /* Transposes sparse matrix */ - int *,int *,int *,int *,int *); -int linsolve(int, double *, double *, /* Solution of linear eqns. */ - double *); /* via Cholesky factorization */ - -/* ----------- QUALITY.C ---------------*/ -int openqual(void); /* Opens WQ solver system */ -void initqual(void); /* Initializes WQ solver */ -int runqual(long *); /* Gets current WQ results */ -int nextqual(long *); /* Updates WQ by hyd.timestep */ -int stepqual(long *); /* Updates WQ by WQ time step */ -int closequal(void); /* Closes WQ solver system */ -int gethyd(long *, long *); /* Gets next hyd. results */ -char setReactflag(void); /* Checks for reactive chem. */ -void transport(long); /* Transports mass in network */ -void initsegs(void); /* Initializes WQ segments */ -void reorientsegs(void); /* Re-orients WQ segments */ -void updatesegs(long); /* Updates quality in segments*/ -void removesegs(int); /* Removes a WQ segment */ -void addseg(int,double,double); /* Adds a WQ segment to pipe */ -void accumulate(long); /* Sums mass flow into node */ -void updatenodes(long); /* Updates WQ at nodes */ -void sourceinput(long); /* Computes source inputs */ -void release(long); /* Releases mass from nodes */ -void updatetanks(long); /* Updates WQ in tanks */ -void updatesourcenodes(long); /* Updates WQ at source nodes */ -void tankmix1(int, long); /* Complete mix tank model */ -void tankmix2(int, long); /* 2-compartment tank model */ -void tankmix3(int, long); /* FIFO tank model */ -void tankmix4(int, long); /* LIFO tank model */ -double sourcequal(Psource); /* Finds WQ input from source */ -double avgqual(int); /* Finds avg. quality in pipe */ -void ratecoeffs(void); /* Finds wall react. coeffs. */ -double piperate(int); /* Finds wall react. coeff. */ -double pipereact(int,double,double,long);/* Reacts water in a pipe */ -double tankreact(double,double,double, - long); /* Reacts water in a tank */ -double bulkrate(double,double,double); /* Finds bulk reaction rate */ -double wallrate(double,double,double,double);/* Finds wall reaction rate */ - - -/* ------------ OUTPUT.C ---------------*/ -int savenetdata(void); /* Saves basic data to file */ -int savehyd(long *); /* Saves hydraulic solution */ -int savehydstep(long *); /* Saves hydraulic timestep */ -int saveenergy(void); /* Saves energy usage */ -int readhyd(long *); /* Reads hydraulics from file */ -int readhydstep(long *); /* Reads time step from file */ -int saveoutput(void); /* Saves results to file */ -int nodeoutput(int, REAL4 *, double); /* Saves node results to file */ -int linkoutput(int, REAL4 *, double); /* Saves link results to file */ -int savefinaloutput(void); /* Finishes saving output */ -int savetimestat(REAL4 *, char); /* Saves time stats to file */ -int savenetreacts(double, double, - double, double); /* Saves react. rates to file */ -int saveepilog(void); /* Saves output file epilog */ - - -/* ------------ INPFILE.C --------------*/ -int saveinpfile(char *); /* Saves network to text file */ - -#endif +/* +************************************************************************** + +FUNCS.H -- Function Prototypes for EPANET Program + +VERSION: 2.00 +DATE: 5/8/00 + 9/25/00 + 10/25/00 + 12/29/00 + 3/1/01 + 2/14/08 (2.00.12) +AUTHOR: L. Rossman + US EPA - NRMRL + +************************************************************************** +*/ + +/*****************************************************************/ +/* Most float arguments have been changed to double - 7/3/07 */ +/*****************************************************************/ + +/* ------- EPANET.C --------------------*/ +/* +** NOTE: The exportable functions that can be called +** via the DLL are prototyped in TOOLKIT.H. +*/ + +#ifndef FUNCS_H +#define FUNCS_H + +#include "types.h" + +void initpointers(EN_Project *p); /* Initializes pointers */ +int allocdata(EN_Project *p); /* Allocates memory */ +void freeTmplist(STmplist *); /* Frees items in linked list */ +void freeFloatlist(SFloatlist *); /* Frees list of floats */ +void freedata(EN_Project *p); /* Frees allocated memory */ +int openfiles(EN_Project *p, char *,char *,char *); /* Opens input & report files */ +int openhydfile(EN_Project *p); /* Opens hydraulics file */ +int openoutfile(EN_Project *p); /* Opens binary output file */ +int strcomp(char *, char *); /* Compares two strings */ +char* getTmpName(EN_Project *p, char* fname); /* Gets temporary file name */ +double interp(int, double *,double *, double); /* Interpolates a data curve */ + +int findnode(EN_Network *n, char *); /* Finds node's index from ID */ +int findlink(EN_Network *n, char *); /* Finds link's index from ID */ +int findtank(EN_Network *n, int); /* Find tank index from node index */ // (AH) +int findvalve(EN_Network *n, int); /* Find valve index from node index */ // (AH) +int findpump(EN_Network *n, int); /* Find pump index from node index */ // (AH) +char *geterrmsg(int errcode, char *msg); /* Gets text of error message */ +void errmsg(EN_Project *p, int); /* Reports program error */ +void writecon(char *); /* Writes text to console */ +void writewin(void (*vp)(char *), char *); /* Passes text to calling app */ + +/* ------- INPUT1.C --------------------*/ +int getdata(EN_Project *pr); /* Gets network data */ +void setdefaults(EN_Project *pr); /* Sets default values */ +void initreport(report_options_t *r); /* Initializes report options */ +void adjustdata(EN_Project *pr); /* Adjusts input data */ +int inittanks(EN_Project *pr); /* Initializes tank levels */ +void initunits(EN_Project *pr); /* Determines reporting units */ +void convertunits(EN_Project *pr); /* Converts data to std. units*/ + +/* -------- INPUT2.C -------------------*/ +int netsize(EN_Project *pr); /* Determines network size */ +int readdata(EN_Project *pr); /* Reads in network data */ +int newline(EN_Project *pr, int, char *); /* Processes new line of data */ +int addnodeID(EN_Network *n, int, char *); /* Adds node ID to data base */ +int addlinkID(EN_Network *n, int, char *); /* Adds link ID to data base */ +int addpattern(parser_data_t *par, char *); /* Adds pattern to data base */ +int addcurve(parser_data_t *par, char *); /* Adds curve to data base */ +STmplist *findID(char *, STmplist *); /* Locates ID on linked list */ +int unlinked(EN_Project *pr); /* Checks for unlinked nodes */ +int getpumpparams(EN_Project *pr); /* Computes pump curve coeffs.*/ +int getpatterns(EN_Project *pr); /* Gets pattern data from list*/ +int getcurves(EN_Project *pr); /* Gets curve data from list */ +int findmatch(char *, char *[]); /* Finds keyword in line */ +int match(const char *, const char *); /* Checks for word match */ +int gettokens(char *s, char** Tok, int maxToks, char *comment); /* Tokenizes input line */ +int getfloat(char *, double *); /* Converts string to double */ +double hour(char *, char *); /* Converts time to hours */ +int setreport(EN_Project *pr, char *); /* Processes reporting command*/ +void inperrmsg(EN_Project *pr, int,int,char *); /* Input error message */ + +/* ---------- INPUT3.C -----------------*/ +int juncdata(EN_Project *pr); /* Processes junction data */ +int tankdata(EN_Project *pr); /* Processes tank data */ +int pipedata(EN_Project *pr); /* Processes pipe data */ +int pumpdata(EN_Project *pr); /* Processes pump data */ +int valvedata(EN_Project *pr); /* Processes valve data */ +int patterndata(EN_Project *pr); /* Processes pattern data */ +int curvedata(EN_Project *pr); /* Processes curve data */ +int coordata(EN_Project *pr); /* Processes coordinate data */ +int demanddata(EN_Project *pr); /* Processes demand data */ +int controldata(EN_Project *pr); /* Processes simple controls */ +int energydata(EN_Project *pr); /* Processes energy data */ +int sourcedata(EN_Project *pr); /* Processes source data */ +int emitterdata(EN_Project *pr); /* Processes emitter data */ +int qualdata(EN_Project *pr); /* Processes quality data */ +int reactdata(EN_Project *pr); /* Processes reaction data */ +int mixingdata(EN_Project *pr); /* Processes tank mixing data */ +int statusdata(EN_Project *pr); /* Processes link status data */ +int reportdata(EN_Project *pr); /* Processes report options */ +int timedata(EN_Project *pr); /* Processes time options */ +int optiondata(EN_Project *pr); /* Processes analysis options */ +int optionchoice(EN_Project *pr, int); /* Processes option choices */ +int optionvalue(EN_Project *pr, int); /* Processes option values */ +int getpumpcurve(EN_Project *pr, int); /* Constructs a pump curve */ +int powercurve(double, double, double,/* Coeffs. of power pump curve*/ + double, double, double *, + double *, double *); +int valvecheck(EN_Project *pr, int, int, int); /* Checks valve placement */ +void changestatus(EN_Network *net, int, StatType, double); /* Changes status of a link */ + +/* -------------- RULES.C --------------*/ +void initrules(rules_t *rules); /* Initializes rule base */ +void addrule(parser_data_t *par, char *); /* Adds rule to rule base */ +int allocrules(EN_Project *pr); /* Allocates memory for rule */ +int ruledata(EN_Project *pr); /* Processes rule input data */ +int checkrules(EN_Project *pr, long); /* Checks all rules */ +void freerules(EN_Project *pr); /* Frees rule base memory */ + +/* ------------- REPORT.C --------------*/ +int writereport(EN_Project *pr); /* Writes formatted report */ +void writelogo(EN_Project *pr); /* Writes program logo */ +void writesummary(EN_Project *pr); /* Writes network summary */ +void writehydstat(EN_Project *pr, int,double); /* Writes hydraulic status */ +void writeenergy(EN_Project *pr); /* Writes energy usage */ +int writeresults(EN_Project *pr); /* Writes node/link results */ +void writeheader(EN_Project *pr, int,int); /* Writes heading on report */ +void writeline(EN_Project *pr, char *); /* Writes line to report file */ +void writerelerr(EN_Project *pr, int, double); /* Writes convergence error */ +void writestatchange(EN_Project *pr, int,char,char); /* Writes link status change */ +void writecontrolaction(EN_Project *pr, int, int); /* Writes control action taken*/ +void writeruleaction(EN_Project *pr, int, char *); /* Writes rule action taken */ +int writehydwarn(EN_Project *pr, int,double); /* Writes hydraulic warnings */ +void writehyderr(EN_Project *pr, int); /* Writes hydraulic error msg.*/ +int disconnected(EN_Project *pr); /* Checks for disconnections */ +void marknodes(EN_Project *pr, int, int *, char *); /* Identifies connected nodes */ +void getclosedlink(EN_Project *pr, int, char *); /* Finds a disconnecting link */ +void writelimits(EN_Project *pr, int,int); /* Writes reporting limits */ +int checklimits(report_options_t *rep, double *,int,int); /* Checks variable limits */ +void writetime(EN_Project *pr, char *); /* Writes current clock time */ +char *clocktime(char *, long); /* Converts time to hrs:min */ +char *fillstr(char *, char, int); /* Fills string with character*/ +int getnodetype(EN_Network *net, int); /* Determines node type */ + +/* --------- HYDRAUL.C -----------------*/ +int openhyd(EN_Project *pr); /* Opens hydraulics solver */ + +/*** Updated 3/1/01 ***/ +void inithyd(EN_Project *pr, int initFlags); /* Re-sets initial conditions */ + +int runhyd(EN_Project *pr, long *); /* Solves 1-period hydraulics */ +int nexthyd(EN_Project *pr, long *); /* Moves to next time period */ +void closehyd(EN_Project *pr); /* Closes hydraulics solver */ +int allocmatrix(EN_Project *pr); /* Allocates matrix coeffs. */ +void freematrix(EN_Project *pr); /* Frees matrix coeffs. */ +void initlinkflow(EN_Project *pr, int, char, double); /* Initializes link flow */ +void setlinkflow(EN_Project *pr, int, double); /* Sets link flow via headloss*/ +void setlinkstatus(EN_Project *pr, int, char, StatType *, double *); /* Sets link status */ + +void setlinksetting(EN_Project *pr, int, double, StatType *, double *); /* Sets pump/valve setting */ + +void resistance(EN_Project *pr, int); /* Computes resistance coeff. */ +void demands(EN_Project *pr); /* Computes current demands */ +int controls(EN_Project *pr); /* Controls link settings */ +long timestep(EN_Project *pr); /* Computes new time step */ +int tanktimestep(EN_Project *pr, long *); /* Time till tanks fill/drain */ +void controltimestep(EN_Project *pr, long *); /* Time till control action */ +void ruletimestep(EN_Project *pr, long *); /* Time till rule action */ +void addenergy(EN_Project *pr, long); /* Accumulates energy usage */ +void getenergy(EN_Project *pr, int, double *, double *); /* Computes link energy use */ +void tanklevels(EN_Project *pr, long); /* Computes new tank levels */ +double tankvolume(EN_Project *pr, int,double); /* Finds tank vol. from grade */ +double tankgrade(EN_Project *pr, int,double); /* Finds tank grade from vol. */ +int netsolve(EN_Project *pr, int *,double *); /* Solves network equations */ +int badvalve(EN_Project *pr, int); /* Checks for bad valve */ +int valvestatus(EN_Project *pr); /* Updates valve status */ +int linkstatus(EN_Project *pr); /* Updates link status */ +StatType cvstatus(EN_Project *pr, StatType,double,double); /* Updates CV status */ +StatType pumpstatus(EN_Project *pr, int,double); /* Updates pump status */ +StatType prvstatus(EN_Project *pr, int,StatType,double,double,double); /* Updates PRV status */ + +StatType psvstatus(EN_Project *pr, int,StatType,double,double,double); /* Updates PSV status */ + +StatType fcvstatus(EN_Project *pr, int,StatType,double,double); /* Updates FCV status */ + +void tankstatus(EN_Project *pr, int,int,int); /* Checks if tank full/empty */ +int pswitch(EN_Project *pr); /* Pressure switch controls */ +double newflows(EN_Project *pr); /* Updates link flows */ +void newcoeffs(EN_Project *pr); /* Computes matrix coeffs. */ +void linkcoeffs(EN_Project *pr); /* Computes link coeffs. */ +void nodecoeffs(EN_Project *pr); /* Computes node coeffs. */ +void valvecoeffs(EN_Project *pr); /* Computes valve coeffs. */ +void pipecoeff(EN_Project *pr, int); /* Computes pipe coeff. */ +double DWcoeff(EN_Project *pr, int, double *); /* Computes D-W coeff. */ +void pumpcoeff(EN_Project *pr, int); /* Computes pump coeff. */ + +/*** Updated 10/25/00 ***/ +/*** Updated 12/29/00 ***/ +void curvecoeff(EN_Project *pr, int,double,double *,double *); /* Computes curve coeffs. */ + + +void gpvcoeff(EN_Project *pr, int iLink); /* Computes GPV coeff. */ +void pbvcoeff(EN_Project *pr, int iLink); /* Computes PBV coeff. */ +void tcvcoeff(EN_Project *pr, int iLink); /* Computes TCV coeff. */ +void prvcoeff(EN_Project *pr, int iLink, int n1, int n2); /* Computes PRV coeff. */ +void psvcoeff(EN_Project *pr, int iLink, int n1, int n2); /* Computes PSV coeff. */ +void fcvcoeff(EN_Project *pr, int iLink, int n1, int n2); /* Computes FCV coeff. */ +void emittercoeffs(EN_Project *pr); /* Computes emitter coeffs. */ +double emitflowchange(EN_Project *pr, int); /* Computes new emitter flow */ + +/* ----------- SMATRIX.C ---------------*/ +int createsparse(EN_Project *pr); /* Creates sparse matrix */ +int allocsparse(EN_Project *pr); /* Allocates matrix memory */ +void freesparse(EN_Project *pr); /* Frees matrix memory */ +int buildlists(EN_Project *pr, int); /* Builds adjacency lists */ +int paralink(EN_Project *pr, int, int, int); /* Checks for parallel links */ +void xparalinks(EN_Project *pr); /* Removes parallel links */ +void freelists(EN_Project *pr); /* Frees adjacency lists */ +void countdegree(EN_Project *pr); /* Counts links at each node */ +int reordernodes(EN_Project *pr); /* Finds a node re-ordering */ +int mindegree(solver_t *s, int, int); /* Finds min. degree node */ +int growlist(EN_Project *pr, int); /* Augments adjacency list */ +int newlink(EN_Project *pr, Padjlist); /* Adds fill-ins for a node */ +int linked(EN_Network *net, int, int); /* Checks if 2 nodes linked */ +int addlink(EN_Network *net, int, int, int); /* Creates new fill-in */ +int storesparse(EN_Project *pr, int); /* Stores sparse matrix */ +int ordersparse(hydraulics_t *h, int); /* Orders matrix storage */ +void transpose(int,int *,int *, /* Transposes sparse matrix */ + int *,int *,int *,int *,int *); +int linsolve(solver_t *s, int); /* via Cholesky factorization */ + +/* ----------- QUALITY.C ---------------*/ +int openqual(EN_Project *pr); /* Opens WQ solver system */ +void initqual(EN_Project *pr); /* Initializes WQ solver */ +int runqual(EN_Project *pr, long *); /* Gets current WQ results */ +int nextqual(EN_Project *pr, long *); /* Updates WQ by hyd.timestep */ +int stepqual(EN_Project *pr, long *); /* Updates WQ by WQ time step */ +int closequal(EN_Project *pr); /* Closes WQ solver system */ +int gethyd(EN_Project *pr, long *, long *); /* Gets next hyd. results */ +char setReactflag(EN_Project *pr); /* Checks for reactive chem. */ +void transport(EN_Project *pr, long); /* Transports mass in network */ +void initsegs(EN_Project *pr); /* Initializes WQ segments */ +void reorientsegs(EN_Project *pr); /* Re-orients WQ segments */ +void updatesegs(EN_Project *pr, long); /* Updates quality in segments*/ +void removesegs(EN_Project *pr, int); /* Removes a WQ segment */ +void addseg(EN_Project *pr, int,double,double); /* Adds a WQ segment to pipe */ +void accumulate(EN_Project *pr, long); /* Sums mass flow into node */ +void updatenodes(EN_Project *pr, long); /* Updates WQ at nodes */ +void sourceinput(EN_Project *pr, long); /* Computes source inputs */ +void release(EN_Project *pr, long); /* Releases mass from nodes */ +void updatetanks(EN_Project *pr, long); /* Updates WQ in tanks */ +void updatesourcenodes(EN_Project *pr, long); /* Updates WQ at source nodes */ +void tankmix1(EN_Project *pr, int, long); /* Complete mix tank model */ +void tankmix2(EN_Project *pr, int, long); /* 2-compartment tank model */ +void tankmix3(EN_Project *pr, int, long); /* FIFO tank model */ +void tankmix4(EN_Project *pr, int, long); /* LIFO tank model */ +double sourcequal(EN_Project *pr, Psource); /* Finds WQ input from source */ +double avgqual(EN_Project *pr, int); /* Finds avg. quality in pipe */ +void ratecoeffs(EN_Project *pr); /* Finds wall react. coeffs. */ +double piperate(EN_Project *pr, int); /* Finds wall react. coeff. */ +double pipereact(EN_Project *pr, int,double,double,long);/* Reacts water in a pipe */ +double tankreact(EN_Project *pr, double,double,double,long); /* Reacts water in a tank */ +double bulkrate(EN_Project *pr, double,double,double); /* Finds bulk reaction rate */ +double wallrate(EN_Project *pr, double,double,double,double);/* Finds wall reaction rate */ + + +/* ------------ OUTPUT.C ---------------*/ +int savenetdata(EN_Project *pr); /* Saves basic data to file */ +int savehyd(EN_Project *pr, long *); /* Saves hydraulic solution */ +int savehydstep(EN_Project *pr, long *); /* Saves hydraulic timestep */ +int saveenergy(EN_Project *pr); /* Saves energy usage */ +int readhyd(EN_Project *pr, long *); /* Reads hydraulics from file */ +int readhydstep(FILE *hydFile, long *); /* Reads time step from file */ +int saveoutput(EN_Project *pr); /* Saves results to file */ +int nodeoutput(EN_Project *pr, int, REAL4 *, double); /* Saves node results to file */ +int linkoutput(EN_Project *pr, int, REAL4 *, double); /* Saves link results to file */ +int savefinaloutput(EN_Project *pr); /* Finishes saving output */ +int savetimestat(EN_Project *pr, REAL4 *, HdrType); /* Saves time stats to file */ +int savenetreacts(EN_Project *pr, double, double,double, double); + /* Saves react. rates to file */ +int saveepilog(EN_Project *pr); /* Saves output file epilog */ + + +/* ------------ INPFILE.C --------------*/ +int saveinpfile(EN_Project *pr, char *); /* Saves network to text file */ + +#endif diff --git a/src/hash.c b/src/hash.c index 425031f..ac31246 100755 --- a/src/hash.c +++ b/src/hash.c @@ -36,6 +36,7 @@ unsigned int _enHash(char *str) hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ } retHash = hash % ENHASHTABLEMAXSIZE; + return retHash; } @@ -72,13 +73,54 @@ int ENHashTableInsert(ENHashTable *ht, char *key, int data) return(1); } -int ENHashTableFind(ENHashTable *ht, char *key) +/* Abel Heinsbroek: Added function to update the hash table value for a given key */ +int ENHashTableUpdate(ENHashTable *ht, char *key, int new_data) { unsigned int i = _enHash(key); ENHashEntry *entry; if ( i >= ENHASHTABLEMAXSIZE ) { return(NOTFOUND); } + entry = ht[i]; + while (entry != NULL) + { + if ( strcmp(entry->key,key) == 0 ) { + entry->data = new_data; + return(1); + } + entry = entry->next; + } + return(NOTFOUND); +} + +int ENHashTableDelete(ENHashTable *ht, char *key) { + unsigned int i = _enHash(key); + ENHashEntry *entry; + if ( i >= ENHASHTABLEMAXSIZE ) { + return(NOTFOUND); + } + entry = ht[i]; + while (entry != NULL) + { + if (strcmp(entry->key, key) == 0) { + entry->key = ""; + return(1); + } + entry = entry->next; + } + + return(NOTFOUND); +} + +int ENHashTableFind(ENHashTable *ht, char *key) +{ + unsigned int i = _enHash(key); + + ENHashEntry *entry; + if ( i >= ENHASHTABLEMAXSIZE ) { + return(NOTFOUND); + } + entry = ht[i]; while (entry != NULL) { diff --git a/src/hash.h b/src/hash.h index 2952ae6..1285c11 100755 --- a/src/hash.h +++ b/src/hash.h @@ -24,5 +24,7 @@ int ENHashTableInsert(ENHashTable *, char *, int); int ENHashTableFind(ENHashTable *, char *); char *ENHashTableFindKey(ENHashTable *, char *); void ENHashTableFree(ENHashTable *); +int ENHashTableUpdate(ENHashTable *ht, char *key, int new_data); +int ENHashTableDelete(ENHashTable *ht, char *key); -#endif \ No newline at end of file +#endif diff --git a/src/hydraul.c b/src/hydraul.c index 83ab227..8f1bcd7 100755 --- a/src/hydraul.c +++ b/src/hydraul.c @@ -1,2513 +1,2951 @@ -/* -********************************************************************* - -HYDRAUL.C -- Hydraulic Simulator for EPANET Program - -VERSION: 2.00 -DATE: 6/5/00 - 9/7/00 - 10/25/00 - 12/29/00 - 3/1/01 - 11/19/01 - 6/24/02 - 8/15/07 (2.00.11) - 2/14/08 (2.00.12) -AUTHOR: L. Rossman - US EPA - NRMRL - - This module contains the network hydraulic simulator. - It simulates the network's hydraulic behavior over an - an extended period of time and writes its results to the - binary file HydFile. - - The entry points for this module are: - openhyd() -- called from ENopenH() in EPANET.C - inithyd() -- called from ENinitH() in EPANET.C - runhyd() -- called from ENrunH() in EPANET.C - nexthyd() -- called from ENnextH() in EPANET.C - closehyd() -- called from ENcloseH() in EPANET.C - tankvolume() -- called from ENsetnodevalue() in EPANET.C - setlinkstatus(), - setlinksetting(), - resistance()-- all called from ENsetlinkvalue() in EPANET.C - - External functions called by this module are: - createsparse() -- see SMATRIX.C - freesparse() -- see SMATRIX.C - linsolve() -- see SMATRIX.C - checkrules() -- see RULES.C - interp() -- see EPANET.C - savehyd() -- see OUTPUT.C - savehydstep() -- see OUTPUT.C - writehydstat() -- see REPORT.C - writehyderr() -- see REPORT.C - writehydwarn() -- see REPORT.C -******************************************************************* -*/ - -#include -#include -#ifndef __APPLE__ -#include -#else -#include -#endif -#include -#include "hash.h" -#include "text.h" -#include "types.h" -#include "funcs.h" -#define EXTERN extern -#include "vars.h" - -#define QZERO 1.e-6 /* Equivalent to zero flow */ -#define CBIG 1.e8 /* Big coefficient */ -#define CSMALL 1.e-6 /* Small coefficient */ - -/* Constants used for computing Darcy-Weisbach friction factor */ -#define A1 0.314159265359e04 /* 1000*PI */ -#define A2 0.157079632679e04 /* 500*PI */ -#define A3 0.502654824574e02 /* 16*PI */ -#define A4 6.283185307 /* 2*PI */ -#define A8 4.61841319859 /* 5.74*(PI/4)^.9 */ -#define A9 -8.685889638e-01 /* -2/ln(10) */ -#define AA -1.5634601348 /* -2*.9*2/ln(10) */ -#define AB 3.28895476345e-03 /* 5.74/(4000^.9) */ -#define AC -5.14214965799e-03 /* AA*AB */ - -/*** Updated 3/1/01 ***/ -/* Flag used to halt taking further time steps */ -int Haltflag; - -/* Relaxation factor used for updating flow changes */ //(2.00.11 - LR) -double RelaxFactor; //(2.00.11 - LR) - -/* Function to find flow coeffs. through open/closed valves */ //(2.00.11 - LR) -void valvecoeff(int k); //(2.00.11 - LR) - - -int openhyd() -/* - *-------------------------------------------------------------- - * Input: none - * Output: returns error code - * Purpose: opens hydraulics solver system - *-------------------------------------------------------------- -*/ -{ - int i; - int errcode = 0; - ERRCODE(createsparse()); /* See SMATRIX.C */ - ERRCODE(allocmatrix()); /* Allocate solution matrices */ - for (i=1; i<=Nlinks; i++) /* Initialize flows */ - initlinkflow(i,Link[i].Stat,Link[i].Kc); - return(errcode); -} - - -/*** Updated 3/1/01 ***/ -void inithyd(int initflag) -/* -**-------------------------------------------------------------- -** Input: initflag > 0 if link flows should be re-initialized -** = 0 if not -** Output: none -** Purpose: initializes hydraulics solver system -**-------------------------------------------------------------- -*/ -{ - int i,j; - - /* Initialize tanks */ - for (i=1; i<=Ntanks; i++) - { - Tank[i].V = Tank[i].V0; - NodeHead[Tank[i].Node] = Tank[i].H0; - -/*** Updated 10/25/00 ***/ - NodeDemand[Tank[i].Node] = 0.0; - - OldStat[Nlinks+i] = TEMPCLOSED; - } - - /* Initialize emitter flows */ - memset(E,0,(Nnodes+1)*sizeof(double)); - for (i=1; i<=Njuncs; i++) - if (Node[i].Ke > 0.0) E[i] = 1.0; - - /* Initialize links */ - for (i=1; i<=Nlinks; i++) - { - /* Initialize status and setting */ - LinkStatus[i] = Link[i].Stat; - LinkSetting[i] = Link[i].Kc; - - /* Start active control valves in ACTIVE position */ //(2.00.11 - LR) - if ( - (Link[i].Type == PRV || Link[i].Type == PSV - || Link[i].Type == FCV) //(2.00.11 - LR) - && (Link[i].Kc != MISSING) - ) LinkStatus[i] = ACTIVE; //(2.00.11 - LR) - -/*** Updated 3/1/01 ***/ - /* Initialize flows if necessary */ - if (LinkStatus[i] <= CLOSED) Q[i] = QZERO; - else if (ABS(Q[i]) <= QZERO || initflag > 0) - initlinkflow(i, LinkStatus[i], LinkSetting[i]); - - /* Save initial status */ - OldStat[i] = LinkStatus[i]; - } - - /* Reset pump energy usage */ - for (i=1; i<=Npumps; i++) - { - for (j=0; j<6; j++) Pump[i].Energy[j] = 0.0; - } - - /* Re-position hydraulics file */ - if (Saveflag) fseek(HydFile,HydOffset,SEEK_SET); - -/*** Updated 3/1/01 ***/ - /* Initialize current time */ - Haltflag = 0; - Htime = 0; - Hydstep = 0; - Rtime = Rstep; -} - - -int runhyd(long *t) -/* -**-------------------------------------------------------------- -** Input: none -** Output: t = pointer to current time (in seconds) -** Returns: error code -** Purpose: solves network hydraulics in a single time period -**-------------------------------------------------------------- -*/ -{ - int iter; /* Iteration count */ - int errcode; /* Error code */ - double relerr; /* Solution accuracy */ - - /* Find new demands & control actions */ - *t = Htime; - demands(); - controls(); - - /* Solve network hydraulic equations */ - errcode = netsolve(&iter,&relerr); - if (!errcode) - { - /* Report new status & save results */ - if (Statflag) writehydstat(iter,relerr); - - /* solution info */ - _relativeError = relerr; - _iterations = iter; - -/*** Updated 3/1/01 ***/ - /* If system unbalanced and no extra trials */ - /* allowed, then activate the Haltflag. */ - if (relerr > Hacc && ExtraIter == -1) Haltflag = 1; - - /* Report any warning conditions */ - if (!errcode) errcode = writehydwarn(iter,relerr); - } - return(errcode); -} /* end of runhyd */ - - -int nexthyd(long *tstep) -/* -**-------------------------------------------------------------- -** Input: none -** Output: tstep = pointer to time step (in seconds) -** Returns: error code -** Purpose: finds length of next time step & updates tank -** levels and rule-based contol actions -**-------------------------------------------------------------- -*/ -{ - long hydstep; /* Actual time step */ - int errcode = 0; /* Error code */ - -/*** Updated 3/1/01 ***/ - /* Save current results to hydraulics file and */ - /* force end of simulation if Haltflag is active */ - if (Saveflag) errcode = savehyd(&Htime); - if (Haltflag) Htime = Dur; - - /* Compute next time step & update tank levels */ - *tstep = 0; - hydstep = 0; - if (Htime < Dur) hydstep = timestep(); - if (Saveflag) errcode = savehydstep(&hydstep); - - /* Compute pumping energy */ - if (Dur == 0) addenergy(0); - else if (Htime < Dur) addenergy(hydstep); - - /* Update current time. */ - if (Htime < Dur) /* More time remains */ - { - Htime += hydstep; - if (Htime >= Rtime) Rtime += Rstep; - } - else - { - Htime++; /* Force completion of analysis */ - if (OpenQflag) { - Qtime++; // force completion of wq analysis too - } - } - *tstep = hydstep; - return(errcode); -} - - -void closehyd() -/* -**-------------------------------------------------------------- -** Input: none -** Output: returns error code -** Purpose: closes hydraulics solver system -**-------------------------------------------------------------- -*/ -{ - freesparse(); /* see SMATRIX.C */ - freematrix(); -} - - -int allocmatrix() -/* -**-------------------------------------------------------------- -** Input: none -** Output: returns error code -** Purpose: allocates memory used for solution matrix coeffs. -**-------------------------------------------------------------- -*/ -{ - int errcode = 0; - Aii = (double *) calloc(Nnodes+1,sizeof(double)); - Aij = (double *) calloc(Ncoeffs+1,sizeof(double)); - F = (double *) calloc(Nnodes+1,sizeof(double)); - E = (double *) calloc(Nnodes+1,sizeof(double)); - P = (double *) calloc(Nlinks+1,sizeof(double)); - Y = (double *) calloc(Nlinks+1,sizeof(double)); - X = (double *) calloc(MAX((Nnodes+1),(Nlinks+1)),sizeof(double)); - OldStat = (char *) calloc(Nlinks+Ntanks+1, sizeof(char)); - ERRCODE(MEMCHECK(Aii)); - ERRCODE(MEMCHECK(Aij)); - ERRCODE(MEMCHECK(F)); - ERRCODE(MEMCHECK(E)); - ERRCODE(MEMCHECK(P)); - ERRCODE(MEMCHECK(Y)); - ERRCODE(MEMCHECK(X)); - ERRCODE(MEMCHECK(OldStat)); - return(errcode); -} /* end of allocmatrix */ - - -void freematrix() -/* -**-------------------------------------------------------------- -** Input: none -** Output: none -** Purpose: frees memory used for solution matrix coeffs. -**-------------------------------------------------------------- -*/ -{ - free(Aii); - free(Aij); - free(F); - free(E); - free(P); - free(Y); - free(X); - free(OldStat); -} /* end of freematrix */ - - -void initlinkflow(int i, char s, double k) -/* -**-------------------------------------------------------------------- -** Input: i = link index -** s = link status -** k = link setting (i.e., pump speed) -** Output: none -** Purpose: sets initial flow in link to QZERO if link is closed, -** to design flow for a pump, or to flow at velocity of -** 1 fps for other links. -**-------------------------------------------------------------------- -*/ -{ - if (s == CLOSED) Q[i] = QZERO; - else if (Link[i].Type == PUMP) Q[i] = k*Pump[PUMPINDEX(i)].Q0; - else Q[i] = PI*SQR(Link[i].Diam)/4.0; -} - - -/*** Updated 9/7/00 ***/ -void setlinkflow(int k, double dh) -/* -**-------------------------------------------------------------- -** Input: k = link index -** dh = headloss across link -** Output: none -** Purpose: sets flow in link based on current headloss -**-------------------------------------------------------------- -*/ -{ - int i,p; - double h0; - double x,y; - - switch (Link[k].Type) - { - case CV: - case PIPE: - - /* For Darcy-Weisbach formula: */ - /* use approx. inverse of formula. */ - if (Formflag == DW) - { - x = -log(LinkSetting[k]/3.7/Link[k].Diam); - y = sqrt(ABS(dh)/Link[k].R/1.32547); - Q[k] = x*y; - } - - /* For Hazen-Williams or Manning formulas: */ - /* use inverse of formula. */ - else - { - x = ABS(dh)/Link[k].R; - y = 1.0/Hexp; - Q[k] = pow(x,y); - } - - /* Change sign of flow to match sign of headloss */ - if (dh < 0.0) Q[k] = -Q[k]; - break; - - case PUMP: - - /* Convert headloss to pump head gain */ - dh = -dh; - p = PUMPINDEX(k); - - /* For custom pump curve, interpolate from curve */ - if (Pump[p].Ptype == CUSTOM) - { - dh = -dh*Ucf[HEAD]/SQR(LinkSetting[k]); - i = Pump[p].Hcurve; - Q[k] = interp(Curve[i].Npts,Curve[i].Y,Curve[i].X, - dh)*LinkSetting[k]/Ucf[FLOW]; - } - - /* Otherwise use inverse of power curve */ - else - { - h0 = -SQR(LinkSetting[k])*Pump[p].H0; - x = pow(LinkSetting[k],2.0-Pump[p].N); - x = ABS(h0-dh)/(Pump[p].R*x), - y = 1.0/Pump[p].N; - Q[k] = pow(x,y); - } - break; - } -} - - -void setlinkstatus(int index, char value, char *s, double *k) -/*---------------------------------------------------------------- -** Input: index = link index -** value = 0 (CLOSED) or 1 (OPEN) -** s = pointer to link status -** k = pointer to link setting -** Output: none -** Purpose: sets link status to OPEN or CLOSED -**---------------------------------------------------------------- -*/ -{ - /* Status set to open */ - if (value == 1) - { - /* Adjust link setting for pumps & valves */ - if (Link[index].Type == PUMP) *k = 1.0; - -/*** Updated 9/7/00 ***/ - if (Link[index].Type > PUMP - && Link[index].Type != GPV) *k = MISSING; - - /* Reset link flow if it was originally closed */ -// if (*s <= CLOSED) initlinkflow(index, OPEN, *k); - *s = OPEN; - } - - /* Status set to closed */ - else if (value == 0) - { - /* Adjust link setting for pumps & valves */ - if (Link[index].Type == PUMP) *k = 0.0; - -/*** Updated 9/7/00 ***/ - if (Link[index].Type > PUMP - && Link[index].Type != GPV) *k = MISSING; - - /* Reset link flow if it was originally open */ -// if (*s > CLOSED) initlinkflow(index, CLOSED, *k); - *s = CLOSED; - } -} - - -void setlinksetting(int index, double value, char *s, double *k) -/*---------------------------------------------------------------- -** Input: index = link index -** value = pump speed or valve setting -** s = pointer to link status -** k = pointer to link setting -** Output: none -** Purpose: sets pump speed or valve setting, adjusting link -** status and flow when necessary -**---------------------------------------------------------------- -*/ -{ - /* For a pump, status is OPEN if speed > 0, CLOSED otherwise */ - if (Link[index].Type == PUMP) - { - *k = value; - if (value > 0 && *s <= CLOSED) - { - *s = OPEN; -// initlinkflow(index, OPEN, value); - } - if (value == 0 && *s > CLOSED) - { - *s = CLOSED; -// initlinkflow(index, CLOSED, value); - } - } - -/*** Updated 9/7/00 ***/ - /* For FCV, activate it */ - else if (Link[index].Type == FCV) - { -// if (*s <= CLOSED) initlinkflow(index, OPEN, value); - *k = value; - *s = ACTIVE; - } - - /* Open closed control valve with fixed status (setting = MISSING) */ - else - { - if (*k == MISSING && *s <= CLOSED) - { -// initlinkflow(index, OPEN, value); - *s = OPEN; - } - *k = value; - } -} - - -void resistance(int k) -/* -**-------------------------------------------------------------------- -** Input: k = link index -** Output: none -** Purpose: computes link flow resistance -**-------------------------------------------------------------------- -*/ -{ - double e,d,L; - Link[k].R = CSMALL; - //if (Link[k].Type == PIPE || Link[k].Type == CV) //(2.00.11 - LR) - switch (Link[k].Type) - { - - /* Link is a pipe. Compute resistance based on headloss formula. */ - /* Friction factor for D-W formula gets included during solution */ - /* process in pipecoeff() function. */ - case CV: - case PIPE: - e = Link[k].Kc; /* Roughness coeff. */ - d = Link[k].Diam; /* Diameter */ - L = Link[k].Len; /* Length */ - switch(Formflag) - { - case HW: Link[k].R = 4.727*L/pow(e,Hexp)/pow(d,4.871); - break; - case DW: Link[k].R = L/2.0/32.2/d/SQR(PI*SQR(d)/4.0); - break; - case CM: Link[k].R = SQR(4.0*e/(1.49*PI*d*d))* - pow((d/4.0),-1.333)*L; - } - break; - - /* Link is a pump. Use negligible resistance. */ - case PUMP: - Link[k].R = CBIG; //CSMALL; - break; - - - /* Link is a valve. Compute resistance for open valve assuming */ - /* length is 2*diameter and friction factor is 0.02. Use with */ - /* other formulas as well since resistance should be negligible.*/ - -/*** This way of treating valve resistance has been deprecated ***/ //(2.00.11 - LR) -/*** since resulting resistance is not always negligible. ***/ //(2.00.11 - LR) -/* - default: - d = Link[k].Diam; - L = 2.0*d; - Link[k].R = 0.02*L/2.0/32.2/d/SQR(PI*SQR(d)/4.0); - break; -*/ - } -} - - -void demands() -/* -**-------------------------------------------------------------------- -** Input: none -** Output: none -** Purpose: computes demands at nodes during current time period -**-------------------------------------------------------------------- -*/ -{ - int i,j,n; - long k,p; - double djunc, sum; - Pdemand demand; - - /* Determine total elapsed number of pattern periods */ - p = (Htime+Pstart)/Pstep; - - /* Update demand at each node according to its assigned pattern */ - Dsystem = 0.0; /* System-wide demand */ - for (i=1; i<=Njuncs; i++) - { - sum = 0.0; - for (demand = Node[i].D; demand != NULL; demand = demand->next) - { - /* - pattern period (k) = (elapsed periods) modulus - (periods per pattern) - */ - j = demand->Pat; - k = p % (long) Pattern[j].Length; - djunc = (demand->Base)*Pattern[j].F[k]*Dmult; - if (djunc > 0.0) Dsystem += djunc; - sum += djunc; - } - NodeDemand[i] = sum; - } - - /* Update head at fixed grade nodes with time patterns. */ - for (n=1; n<=Ntanks; n++) - { - if (Tank[n].A == 0.0) - { - j = Tank[n].Pat; - if (j > 0) - { - k = p % (long) Pattern[j].Length; - i = Tank[n].Node; - NodeHead[i] = Node[i].El*Pattern[j].F[k]; - } - } - } - - /* Update status of pumps with utilization patterns */ - for (n=1; n<=Npumps; n++) - { - j = Pump[n].Upat; - if (j > 0) - { - i = Pump[n].Link; - k = p % (long) Pattern[j].Length; - setlinksetting(i, Pattern[j].F[k], &LinkStatus[i], &LinkSetting[i]); - } - } -} /* End of demands */ - - -int controls() -/* -**--------------------------------------------------------------------- -** Input: none -** Output: number of links whose setting changes -** Purpose: implements simple controls based on time or tank levels -**--------------------------------------------------------------------- -*/ -{ - int i, k, n, reset, setsum; - double h, vplus; - double v1, v2; - double k1, k2; - char s1, s2; - - /* Examine each control statement */ - setsum = 0; - for (i=1; i<=Ncontrols; i++) - { - /* Make sure that link is defined */ - reset = 0; - if ( (k = Control[i].Link) <= 0) continue; - - /* Link is controlled by tank level */ - if ((n = Control[i].Node) > 0 && n > Njuncs) - { - h = NodeHead[n]; - vplus = ABS(NodeDemand[n]); - v1 = tankvolume(n-Njuncs,h); - v2 = tankvolume(n-Njuncs,Control[i].Grade); - if (Control[i].Type == LOWLEVEL && v1 <= v2 + vplus) - reset = 1; - if (Control[i].Type == HILEVEL && v1 >= v2 - vplus) - reset = 1; - } - - /* Link is time-controlled */ - if (Control[i].Type == TIMER) - { - if (Control[i].Time == Htime) reset = 1; - } - - /* Link is time-of-day controlled */ - if (Control[i].Type == TIMEOFDAY) - { - if ((Htime + Tstart) % SECperDAY == Control[i].Time) reset = 1; - } - - /* Update link status & pump speed or valve setting */ - if (reset == 1) - { - if (LinkStatus[k] <= CLOSED) s1 = CLOSED; - else s1 = OPEN; - s2 = Control[i].Status; - k1 = LinkSetting[k]; - k2 = k1; - if (Link[k].Type > PIPE) k2 = Control[i].Setting; - if (s1 != s2 || k1 != k2) - { - LinkStatus[k] = s2; - LinkSetting[k] = k2; - if (Statflag) writecontrolaction(k,i); - // if (s1 != s2) initlinkflow(k, S[k], K[k]); - setsum++; - } - } - } - return(setsum); -} /* End of controls */ - - -long timestep(void) -/* -**---------------------------------------------------------------- -** Input: none -** Output: returns time step until next change in hydraulics -** Purpose: computes time step to advance hydraulic simulation -**---------------------------------------------------------------- -*/ -{ - long n,t,tstep; - - /* Normal time step is hydraulic time step */ - tstep = Hstep; - - /* Revise time step based on time until next demand period */ - n = ((Htime+Pstart)/Pstep) + 1; /* Next pattern period */ - t = n*Pstep - Htime; /* Time till next period */ - if (t > 0 && t < tstep) tstep = t; - - /* Revise time step based on time until next reporting period */ - t = Rtime - Htime; - if (t > 0 && t < tstep) tstep = t; - - /* Revise time step based on smallest time to fill or drain a tank */ - tanktimestep(&tstep); - - /* Revise time step based on smallest time to activate a control */ - controltimestep(&tstep); - - /* Evaluate rule-based controls (which will also update tank levels) */ - if (Nrules > 0) ruletimestep(&tstep); - else tanklevels(tstep); - return(tstep); -} - - -void tanktimestep(long *tstep) -/* -**----------------------------------------------------------------- -** Input: *tstep = current time step -** Output: *tstep = modified current time step -** Purpose: revises time step based on shortest time to fill or -** drain a tank -**----------------------------------------------------------------- -*/ -{ - int i,n; - double h,q,v; - long t; - - /* (D[n] is net flow rate into (+) or out of (-) tank at node n) */ - for (i=1; i<=Ntanks; i++) - { - if (Tank[i].A == 0.0) continue; /* Skip reservoirs */ - n = Tank[i].Node; - h = NodeHead[n]; /* Current tank grade */ - q = NodeDemand[n]; /* Flow into tank */ - if (ABS(q) <= QZERO) continue; - if (q > 0.0 && h < Tank[i].Hmax) - { - v = Tank[i].Vmax - Tank[i].V; /* Volume to fill */ - } - else if (q < 0.0 && h > Tank[i].Hmin) - { - v = Tank[i].Vmin - Tank[i].V; /* Volume to drain (-) */ - } - else continue; - t = (long)ROUND(v/q); /* Time to fill/drain */ - if (t > 0 && t < *tstep) *tstep = t; - } -} - - -void controltimestep(long *tstep) -/* -**------------------------------------------------------------------ -** Input: *tstep = current time step -** Output: *tstep = modified current time step -** Purpose: revises time step based on shortest time to activate -** a simple control -**------------------------------------------------------------------ -*/ -{ - int i,j,k,n; - double h,q,v; - long t,t1,t2; - - for (i=1; i<=Ncontrols; i++) - { - t = 0; - if ( (n = Control[i].Node) > 0) /* Node control: */ - { - if ((j = n-Njuncs) <= 0) continue; /* Node is a tank */ - h = NodeHead[n]; /* Current tank grade */ - q = NodeDemand[n]; /* Flow into tank */ - if (ABS(q) <= QZERO) continue; - if - ( (h < Control[i].Grade && - Control[i].Type == HILEVEL && /* Tank below hi level */ - q > 0.0) /* & is filling */ - || (h > Control[i].Grade && - Control[i].Type == LOWLEVEL && /* Tank above low level */ - q < 0.0) /* & is emptying */ - ) - { /* Time to reach level */ - v = tankvolume(j,Control[i].Grade)-Tank[j].V; - t = (long)ROUND(v/q); - } - } - - if (Control[i].Type == TIMER) /* Time control: */ - { - if (Control[i].Time > Htime) - t = Control[i].Time - Htime; - } - - if (Control[i].Type == TIMEOFDAY) /* Time-of-day control: */ - { - t1 = (Htime + Tstart) % SECperDAY; - t2 = Control[i].Time; - if (t2 >= t1) t = t2 - t1; - else t = SECperDAY - t1 + t2; - } - - if (t > 0 && t < *tstep) /* Revise time step */ - { - /* Check if rule actually changes link status or setting */ - k = Control[i].Link; - if ( - (Link[k].Type > PIPE && LinkSetting[k] != Control[i].Setting) || - (LinkStatus[k] != Control[i].Status) - ) - *tstep = t; - } - } -} /* End of timestep */ - - -void ruletimestep(long *tstep) -/* -**-------------------------------------------------------------- -** Input: *tstep = current time step (sec) -** Output: *tstep = modified time step -** Purpose: updates next time step by checking if any rules -** will fire before then; also updates tank levels. -**-------------------------------------------------------------- -*/ -{ - long tnow, /* Start of time interval for rule evaluation */ - tmax, /* End of time interval for rule evaluation */ - dt, /* Normal time increment for rule evaluation */ - dt1; /* Actual time increment for rule evaluation */ - - /* Find interval of time for rule evaluation */ - tnow = Htime; - tmax = tnow + *tstep; - - /* If no rules, then time increment equals current time step */ - if (Nrules == 0) - { - dt = *tstep; - dt1 = dt; - } - - /* Otherwise, time increment equals rule evaluation time step and */ - /* first actual increment equals time until next even multiple of */ - /* Rulestep occurs. */ - else - { - dt = Rulestep; - dt1 = Rulestep - (tnow % Rulestep); - } - - /* Make sure time increment is no larger than current time step */ - dt = MIN(dt, *tstep); - dt1 = MIN(dt1, *tstep); - if (dt1 == 0) dt1 = dt; - - /* Step through time, updating tank levels, until either */ - /* a rule fires or we reach the end of evaluation period. */ - /* - ** Note: we are updating the global simulation time (Htime) - ** here because it is used by functions in RULES.C - ** to evaluate rules when checkrules() is called. - ** It is restored to its original value after the - ** rule evaluation process is completed (see below). - ** Also note that dt1 will equal dt after the first - ** time increment is taken. - */ - do - { - Htime += dt1; /* Update simulation clock */ - tanklevels(dt1); /* Find new tank levels */ - if (checkrules(dt1)) break; /* Stop if rules fire */ - dt = MIN(dt, tmax - Htime); /* Update time increment */ - dt1 = dt; /* Update actual increment */ - } while (dt > 0); /* Stop if no time left */ - - /* Compute an updated simulation time step (*tstep) */ - /* and return simulation time to its original value */ - *tstep = Htime - tnow; - Htime = tnow; -} - - -void addenergy(long hstep) -/* -**------------------------------------------------------------- -** Input: hstep = time step (sec) -** Output: none -** Purpose: accumulates pump energy usage -**------------------------------------------------------------- -*/ -{ - int i,j,k; - long m,n; - double c0,c, /* Energy cost (cost/kwh) */ - f0, /* Energy cost factor */ - dt, /* Time interval (hr) */ - e, /* Pump efficiency (fraction) */ - q, /* Pump flow (cfs) */ - p, /* Pump energy (kw) */ - psum = 0.0; /* Total energy (kw) */ - - /* Determine current time interval in hours */ - if (Dur == 0) dt = 1.0; - else if (Htime < Dur) dt = (double) hstep / 3600.0; - else dt = 0.0; - if (dt == 0.0) return; - n = (Htime+Pstart)/Pstep; - - /* Compute default energy cost at current time */ - c0 = Ecost; - f0 = 1.0; - if (Epat > 0) - { - m = n % (long)Pattern[Epat].Length; - f0 = Pattern[Epat].F[m]; - } - - /* Examine each pump */ - for (j=1; j<=Npumps; j++) - { - /* Skip closed pumps */ - k = Pump[j].Link; - if (LinkStatus[k] <= CLOSED) continue; - q = MAX(QZERO, ABS(Q[k])); - - /* Find pump-specific energy cost */ - if (Pump[j].Ecost > 0.0) c = Pump[j].Ecost; - else c = c0; - - if ( (i = Pump[j].Epat) > 0) - { - m = n % (long)Pattern[i].Length; - c *= Pattern[i].F[m]; - } - else c *= f0; - - /* Find pump energy & efficiency */ - getenergy(k,&p,&e); - psum += p; - - /* Update pump's cumulative statistics */ - Pump[j].Energy[0] += dt; /* Time on-line */ - Pump[j].Energy[1] += e*dt; /* Effic.-hrs */ - Pump[j].Energy[2] += p/q*dt; /* kw/cfs-hrs */ - Pump[j].Energy[3] += p*dt; /* kw-hrs */ - Pump[j].Energy[4] = MAX(Pump[j].Energy[4],p); - Pump[j].Energy[5] += c*p*dt; /* cost-hrs. */ - } - - /* Update maximum kw value */ - Emax = MAX(Emax,psum); -} /* End of pumpenergy */ - - -void getenergy(int k, double *kw, double *eff) -/* -**---------------------------------------------------------------- -** Input: k = link index -** Output: *kw = kwatt energy used -** *eff = efficiency (pumps only) -** Purpose: computes flow energy associated with link k -**---------------------------------------------------------------- -*/ -{ - int i,j; - double dh, q, e; - double q4eff; //q4eff=flow at nominal speed to compute efficiency - -/*** Updated 6/24/02 ***/ - /* No energy if link is closed */ - if (LinkStatus[k] <= CLOSED) - { - *kw = 0.0; - *eff = 0.0; - return; - } -/*** End of update ***/ - - /* Determine flow and head difference */ - q = ABS(Q[k]); - dh = ABS(NodeHead[Link[k].N1] - NodeHead[Link[k].N2]); - - /* For pumps, find effic. at current flow */ - if (Link[k].Type == PUMP) - { - j = PUMPINDEX(k); - e = Epump; - if ( (i = Pump[j].Ecurve) > 0) - { - q4eff = q/LinkSetting[k]; - e = interp(Curve[i].Npts,Curve[i].X, Curve[i].Y, q4eff*Ucf[FLOW]); - } - // e = interp(Curve[i].Npts,Curve[i].X,Curve[i].Y,q*Ucf[FLOW]); //old line of code - e = MIN(e, 100.0); - e = MAX(e, 1.0); - e /= 100.0; - } - else e = 1.0; - - /* Compute energy */ - *kw = dh*q*SpGrav/8.814/e*KWperHP; - *eff = e; -} - - -void tanklevels(long tstep) -/* -**---------------------------------------------------------------- -** Input: tstep = current time step -** Output: none -** Purpose: computes new water levels in tanks after current -** time step -**---------------------------------------------------------------- -*/ -{ - int i,n; - double dv; - - for (i=1; i<=Ntanks; i++) - { - - /* Skip reservoirs */ - if (Tank[i].A == 0.0) continue; - - /* Update the tank's volume & water elevation */ - n = Tank[i].Node; - dv = NodeDemand[n]*tstep; - Tank[i].V += dv; - - /*** Updated 6/24/02 ***/ - /* Check if tank full/empty within next second */ - if (Tank[i].V + NodeDemand[n] >= Tank[i].Vmax) { - Tank[i].V = Tank[i].Vmax; - } - else if (Tank[i].V - NodeDemand[n] <= Tank[i].Vmin) { - Tank[i].V = Tank[i].Vmin; - } - NodeHead[n] = tankgrade(i,Tank[i].V); - } -} /* End of tanklevels */ - - -double tankvolume(int i, double h) -/* -**-------------------------------------------------------------------- -** Input: i = tank index -** h = water elevation in tank -** Output: returns water volume in tank -** Purpose: finds water volume in tank i corresponding to elev. h. -**-------------------------------------------------------------------- -*/ -{ - int j; - - /* Use level*area if no volume curve */ - j = Tank[i].Vcurve; - if (j == 0) return(Tank[i].Vmin + (h - Tank[i].Hmin)*Tank[i].A); - - /* If curve exists, interpolate on h to find volume v */ - /* remembering that volume curve is in original units.*/ - else return(interp(Curve[j].Npts, Curve[j].X, Curve[j].Y, - (h-Node[Tank[i].Node].El)*Ucf[HEAD])/Ucf[VOLUME]); - -} /* End of tankvolume */ - - -double tankgrade(int i, double v) -/* -**------------------------------------------------------------------- -** Input: i = tank index -** v = volume in tank -** Output: returns water level in tank -** Purpose: finds water level in tank i corresponding to volume v. -**------------------------------------------------------------------- -*/ -{ - int j; - - /* Use area if no volume curve */ - j = Tank[i].Vcurve; - if (j == 0) return(Tank[i].Hmin + (v - Tank[i].Vmin)/Tank[i].A); - - /* If curve exists, interpolate on volume (originally the Y-variable */ - /* but used here as the X-variable) to find new level above bottom. */ - /* Remember that volume curve is stored in original units. */ - else return(Node[Tank[i].Node].El + - interp(Curve[j].Npts, Curve[j].Y, Curve[j].X, v*Ucf[VOLUME])/Ucf[HEAD]); - -} /* End of tankgrade */ - - -int netsolve(int *iter, double *relerr) -/* -**------------------------------------------------------------------- -** Input: none -** Output: *iter = # of iterations to reach solution -** *relerr = convergence error in solution -** returns error code -** Purpose: solves network nodal equations for heads and flows -** using Todini's Gradient algorithm -** -*** Updated 9/7/00 *** -*** Updated 2.00.11 *** -*** Updated 2.00.12 *** -** Notes: Status checks on CVs, pumps and pipes to tanks are made -** every CheckFreq iteration, up until MaxCheck iterations -** are reached. Status checks on control valves are made -** every iteration if DampLimit = 0 or only when the -** convergence error is at or below DampLimit. If DampLimit -** is > 0 then future computed flow changes are only 60% of -** their full value. A complete status check on all links -** is made when convergence is achieved. If convergence is -** not achieved in MaxIter trials and ExtraIter > 0 then -** another ExtraIter trials are made with no status changes -** made to any links and a warning message is generated. -** -** This procedure calls linsolve() which appears in SMATRIX.C. -**------------------------------------------------------------------- -*/ -{ - int i; /* Node index */ - int errcode = 0; /* Node causing solution error */ - int nextcheck; /* Next status check trial */ - int maxtrials; /* Max. trials for convergence */ - double newerr; /* New convergence error */ - int valveChange; /* Valve status change flag */ - int statChange; - - /* Initialize status checking & relaxation factor */ - nextcheck = CheckFreq; - RelaxFactor = 1.0; - - /* Repeat iterations until convergence or trial limit is exceeded. */ - /* (ExtraIter used to increase trials in case of status cycling.) */ - if (Statflag == FULL) writerelerr(0,0); - maxtrials = MaxIter; - if (ExtraIter > 0) maxtrials += ExtraIter; - *iter = 1; - while (*iter <= maxtrials) - { - /* - ** Compute coefficient matrices A & F and solve A*H = F - ** where H = heads, A = Jacobian coeffs. derived from - ** head loss gradients, & F = flow correction terms. - ** Solution for H is returned in F from call to linsolve(). - */ - newcoeffs(); - errcode = linsolve(Njuncs,Aii,Aij,F); - - /* Take action depending on error code */ - if (errcode < 0) break; /* Memory allocation problem */ - if (errcode > 0) /* Ill-conditioning problem */ - { - /* If control valve causing problem, fix its status & continue, */ - /* otherwise end the iterations with no solution. */ - if (badvalve(Order[errcode])) continue; - else break; - } - - /* Update current solution. */ - /* (Row[i] = row of solution matrix corresponding to node i). */ - for (i=1; i<=Njuncs; i++) NodeHead[i] = F[Row[i]]; /* Update heads */ - newerr = newflows(); /* Update flows */ - *relerr = newerr; - - /* Write convergence error to status report if called for */ - if (Statflag == FULL) writerelerr(*iter,*relerr); - - /* Apply solution damping & check for change in valve status */ - RelaxFactor = 1.0; - valveChange = FALSE; - if ( DampLimit > 0.0 ) - { - if( *relerr <= DampLimit ) - { - RelaxFactor = 0.6; - valveChange = valvestatus(); - } - } - else valveChange = valvestatus(); - - /* Check for convergence */ - if (*relerr <= Hacc) - { - /* We have convergence. Quit if we are into extra iterations. */ - if (*iter > MaxIter) break; - - /* Quit if no status changes occur. */ - statChange = FALSE; - if (valveChange) statChange = TRUE; - if (linkstatus()) statChange = TRUE; - if (pswitch()) statChange = TRUE; - if (!statChange) break; - - /* We have a status change so continue the iterations */ - nextcheck = *iter + CheckFreq; - } - - /* No convergence yet. See if its time for a periodic status */ - /* check on pumps, CV's, and pipes connected to tanks. */ - else if (*iter <= MaxCheck && *iter == nextcheck) - { - linkstatus(); - nextcheck += CheckFreq; - } - (*iter)++; - } - - /* Iterations ended. Report any errors. */ - if (errcode < 0) errcode = 101; /* Memory allocation error */ - else if (errcode > 0) - { - writehyderr(Order[errcode]); /* Ill-conditioned eqns. error */ - errcode = 110; - } - - /* Add any emitter flows to junction demands */ - for (i=1; i<=Njuncs; i++) NodeDemand[i] += E[i]; - return(errcode); -} /* End of netsolve */ - - -int badvalve(int n) -/* -**----------------------------------------------------------------- -** Input: n = node index -** Output: returns 1 if node n belongs to an active control valve, -** 0 otherwise -** Purpose: determines if a node belongs to an active control valve -** whose setting causes an inconsistent set of eqns. If so, -** the valve status is fixed open and a warning condition -** is generated. -**----------------------------------------------------------------- -*/ -{ - int i,k,n1,n2; - for (i=1; i<=Nvalves; i++) - { - k = Valve[i].Link; - n1 = Link[k].N1; - n2 = Link[k].N2; - if (n == n1 || n == n2) - { - if (Link[k].Type == PRV || - Link[k].Type == PSV || - Link[k].Type == FCV) - { - if (LinkStatus[k] == ACTIVE) - { - if (Statflag == FULL) - { - sprintf(Msg,FMT61,clocktime(Atime,Htime),Link[k].ID); - writeline(Msg); - } - if (Link[k].Type == FCV) LinkStatus[k] = XFCV; - else LinkStatus[k] = XPRESSURE; - return(1); - } - } - return(0); - } - } - return(0); -} - - -int valvestatus() -/* -**----------------------------------------------------------------- -** Input: none -** Output: returns 1 if any pressure or flow control valve //(2.00.11 - LR) -** changes status, 0 otherwise //(2.00.11 - LR) -** Purpose: updates status for PRVs & PSVs whose status //(2.00.12 - LR) -** is not fixed to OPEN/CLOSED -**----------------------------------------------------------------- -*/ -{ - int change = FALSE, /* Status change flag */ - i,k, /* Valve & link indexes */ - n1,n2; /* Start & end nodes */ - char s; /* Valve status settings */ - double hset; /* Valve head setting */ - - for (i=1; i<=Nvalves; i++) /* Examine each valve */ - { - k = Valve[i].Link; /* Link index of valve */ - if (LinkSetting[k] == MISSING) continue; /* Valve status fixed */ - n1 = Link[k].N1; /* Start & end nodes */ - n2 = Link[k].N2; - s = LinkStatus[k]; /* Save current status */ - -// if (s != CLOSED /* No change if flow is */ //(2.00.11 - LR) -// && ABS(Q[k]) < Qtol) continue; /* negligible. */ //(2.00.11 - LR) - - switch (Link[k].Type) /* Evaluate new status: */ - { - case PRV: hset = Node[n2].El + LinkSetting[k]; - LinkStatus[k] = prvstatus(k,s,hset,NodeHead[n1],NodeHead[n2]); - break; - case PSV: hset = Node[n1].El + LinkSetting[k]; - LinkStatus[k] = psvstatus(k,s,hset,NodeHead[n1],NodeHead[n2]); - break; - -//// FCV status checks moved back into the linkstatus() function //// //(2.00.12 - LR) -// case FCV: S[k] = fcvstatus(k,s,NodeHead[n1],NodeHead[n2]); //(2.00.12 - LR) -// break; //(2.00.12 - LR) - - default: continue; - } - -/*** Updated 9/7/00 ***/ - /* Do not reset flow in valve if its status changes. */ - /* This strategy improves convergence. */ - - /* Check for status change */ - if (s != LinkStatus[k]) - { - if (Statflag == FULL) writestatchange(k,s,LinkStatus[k]); - change = TRUE; - } - } - return(change); -} /* End of valvestatus() */ - - -/*** Updated 9/7/00 ***/ -int linkstatus() -/* -**-------------------------------------------------------------- -** Input: none -** Output: returns 1 if any link changes status, 0 otherwise -** Purpose: determines new status for pumps, CVs, FCVs & pipes //(2.00.12 - LR) -** to tanks. -**-------------------------------------------------------------- -*/ -{ - int change = FALSE, /* Status change flag */ - k, /* Link index */ - n1, /* Start node index */ - n2; /* End node index */ - double dh; /* Head difference */ - char status; /* Current status */ - - /* Examine each link */ - for (k=1; k<=Nlinks; k++) - { - n1 = Link[k].N1; - n2 = Link[k].N2; - dh = NodeHead[n1] - NodeHead[n2]; - - /* Re-open temporarily closed links (status = XHEAD or TEMPCLOSED) */ - status = LinkStatus[k]; - if (status == XHEAD || status == TEMPCLOSED) LinkStatus[k] = OPEN; - - /* Check for status changes in CVs and pumps */ - if (Link[k].Type == CV) LinkStatus[k] = cvstatus(LinkStatus[k],dh,Q[k]); - if (Link[k].Type == PUMP && LinkStatus[k] >= OPEN && LinkSetting[k] > 0.0) //(2.00.11 - LR) - LinkStatus[k] = pumpstatus(k,-dh); - - /* Check for status changes in non-fixed FCVs */ - if (Link[k].Type == FCV && LinkSetting[k] != MISSING) //(2.00.12 - LR)// - LinkStatus[k] = fcvstatus(k,status,NodeHead[n1],NodeHead[n2]); //(2.00.12 - LR)// - - /* Check for flow into (out of) full (empty) tanks */ - if (n1 > Njuncs || n2 > Njuncs) tankstatus(k,n1,n2); - - /* Note change in link status; do not revise link flow */ //(2.00.11 - LR) - if (status != LinkStatus[k]) - { - change = TRUE; - if (Statflag == FULL) writestatchange(k,status,LinkStatus[k]); - - //if (S[k] <= CLOSED) Q[k] = QZERO; //(2.00.11 - LR) - //else setlinkflow(k, dh); //(2.00.11 - LR) - } - } - return(change); -} /* End of linkstatus */ - - -char cvstatus(char s, double dh, double q) -/* -**-------------------------------------------------- -** Input: s = current status -** dh = headloss -** q = flow -** Output: returns new link status -** Purpose: updates status of a check valve. -**-------------------------------------------------- -*/ -{ - /* Prevent reverse flow through CVs */ - if (ABS(dh) > Htol) - { - if (dh < -Htol) return(CLOSED); - else if (q < -Qtol) return(CLOSED); - else return(OPEN); - } - else - { - if (q < -Qtol) return(CLOSED); - else return(s); - } -} - - -char pumpstatus(int k, double dh) -/* -**-------------------------------------------------- -** Input: k = link index -** dh = head gain -** Output: returns new pump status -** Purpose: updates status of an open pump. -**-------------------------------------------------- -*/ -{ - int p; - double hmax; - - /* Prevent reverse flow through pump */ - p = PUMPINDEX(k); - if (Pump[p].Ptype == CONST_HP) hmax = BIG; - else hmax = SQR(LinkSetting[k])*Pump[p].Hmax; - if (dh > hmax + Htol) return(XHEAD); - -/*** Flow higher than pump curve no longer results in a status change ***/ //(2.00.11 - LR) - /* Check if pump cannot deliver flow */ //(2.00.11 - LR) - //if (Q[k] > K[k]*Pump[p].Qmax + Qtol) return(XFLOW); //(2.00.11 - LR) - return(OPEN); -} - - -char prvstatus(int k, char s, double hset, double h1, double h2) -/* -**----------------------------------------------------------- -** Input: k = link index -** s = current status -** hset = valve head setting -** h1 = head at upstream node -** h2 = head at downstream node -** Output: returns new valve status -** Purpose: updates status of a pressure reducing valve. -**----------------------------------------------------------- -*/ -{ - char status; /* New valve status */ - double hml; /* Minor headloss */ - double htol = Htol; - - status = s; - if (LinkSetting[k] == MISSING) return(status); /* Status fixed by user */ - hml = Link[k].Km*SQR(Q[k]); /* Head loss when open */ - -/*** Status rules below have changed. ***/ //(2.00.11 - LR) - - switch (s) - { - case ACTIVE: - if (Q[k] < -Qtol) status = CLOSED; - else if (h1-hml < hset-htol) status = OPEN; //(2.00.11 - LR) - else status = ACTIVE; - break; - case OPEN: - if (Q[k] < -Qtol) status = CLOSED; - else if (h2 >= hset+htol) status = ACTIVE; //(2.00.11 - LR) - else status = OPEN; - break; - case CLOSED: - if ( h1 >= hset+htol //(2.00.11 - LR) - && h2 < hset-htol) status = ACTIVE; //(2.00.11 - LR) - else if (h1 < hset-htol //(2.00.11 - LR) - && h1 > h2+htol) status = OPEN; //(2.00.11 - LR) - else status = CLOSED; - break; - case XPRESSURE: - if (Q[k] < -Qtol) status = CLOSED; - break; - } - return(status); -} - - -char psvstatus(int k, char s, double hset, double h1, double h2) -/* -**----------------------------------------------------------- -** Input: k = link index -** s = current status -** hset = valve head setting -** h1 = head at upstream node -** h2 = head at downstream node -** Output: returns new valve status -** Purpose: updates status of a pressure sustaining valve. -**----------------------------------------------------------- -*/ -{ - char status; /* New valve status */ - double hml; /* Minor headloss */ - double htol = Htol; - - status = s; - if (LinkSetting[k] == MISSING) return(status); /* Status fixed by user */ - hml = Link[k].Km*SQR(Q[k]); /* Head loss when open */ - -/*** Status rules below have changed. ***/ //(2.00.11 - LR) - - switch (s) - { - case ACTIVE: - if (Q[k] < -Qtol) status = CLOSED; - else if (h2+hml > hset+htol) status = OPEN; //(2.00.11 - LR) - else status = ACTIVE; - break; - case OPEN: - if (Q[k] < -Qtol) status = CLOSED; - else if (h1 < hset-htol) status = ACTIVE; //(2.00.11 - LR) - else status = OPEN; - break; - case CLOSED: - if (h2 > hset+htol //(2.00.11 - LR) - && h1 > h2+htol) status = OPEN; //(2.00.11 - LR) - else if (h1 >= hset+htol //(2.00.11 - LR) - && h1 > h2+htol) status = ACTIVE; //(2.00.11 - LR) - else status = CLOSED; - break; - case XPRESSURE: - if (Q[k] < -Qtol) status = CLOSED; - break; - } - return(status); -} - - -char fcvstatus(int k, char s, double h1, double h2) -/* -**----------------------------------------------------------- -** Input: k = link index -** s = current status -** h1 = head at upstream node -** h2 = head at downstream node -** Output: returns new valve status -** Purpose: updates status of a flow control valve. -** -** Valve status changes to XFCV if flow reversal. -** If current status is XFCV and current flow is -** above setting, then valve becomes active. -** If current status is XFCV, and current flow -** positive but still below valve setting, then -** status remains same. -**----------------------------------------------------------- -*/ -{ - char status; /* New valve status */ - status = s; - if (h1 - h2 < -Htol) { - status = XFCV; - } - else if ( Q[k] < -Qtol ) { - status = XFCV; //(2.00.11 - LR) - } - else if (s == XFCV && Q[k] >= LinkSetting[k]) { - status = ACTIVE; - } - return(status); -} - - -/*** Updated 9/7/00 ***/ -/*** Updated 11/19/01 ***/ -void tankstatus(int k, int n1, int n2) -/* -**---------------------------------------------------------------- -** Input: k = link index -** n1 = start node of link -** n2 = end node of link -** Output: none -** Purpose: closes link flowing into full or out of empty tank -**---------------------------------------------------------------- -*/ -{ - int i,n; - double h,q; - - /* Make node n1 be the tank */ - q = Q[k]; - i = n1 - Njuncs; - if (i <= 0) - { - i = n2 - Njuncs; - if (i <= 0) return; - n = n1; - n1 = n2; - n2 = n; - q = -q; - } - h = NodeHead[n1] - NodeHead[n2]; - - /* Skip reservoirs & closed links */ - if (Tank[i].A == 0.0 || LinkStatus[k] <= CLOSED) return; - - /* If tank full, then prevent flow into it */ - if (NodeHead[n1] >= Tank[i].Hmax - Htol) - { - - /* Case 1: Link is a pump discharging into tank */ - if ( Link[k].Type == PUMP ) - { - if (Link[k].N2 == n1) LinkStatus[k] = TEMPCLOSED; - } - - /* Case 2: Downstream head > tank head */ - /* (i.e., an open outflow check valve would close) */ - else if (cvstatus(OPEN, h, q) == CLOSED) LinkStatus[k] = TEMPCLOSED; - } - - /* If tank empty, then prevent flow out of it */ - if (NodeHead[n1] <= Tank[i].Hmin + Htol) - { - - /* Case 1: Link is a pump discharging from tank */ - if ( Link[k].Type == PUMP) - { - if (Link[k].N1 == n1) LinkStatus[k] = TEMPCLOSED; - } - - /* Case 2: Tank head > downstream head */ - /* (i.e., a closed outflow check valve would open) */ - else if (cvstatus(CLOSED, h, q) == OPEN) LinkStatus[k] = TEMPCLOSED; - } -} /* End of tankstatus */ - - -int pswitch() -/* -**-------------------------------------------------------------- -** Input: none -** Output: returns 1 if status of any link changes, 0 if not -** Purpose: adjusts settings of links controlled by junction -** pressures after a hydraulic solution is found -**-------------------------------------------------------------- -*/ -{ - int i, /* Control statement index */ - k, /* Link being controlled */ - n, /* Node controlling link */ - reset, /* Flag on control conditions */ - change, /* Flag for status or setting change */ - anychange = 0; /* Flag for 1 or more changes */ - char s; /* Current link status */ - - /* Check each control statement */ - for (i=1; i<=Ncontrols; i++) - { - reset = 0; - if ( (k = Control[i].Link) <= 0) continue; - - /* Determine if control based on a junction, not a tank */ - if ( (n = Control[i].Node) > 0 && n <= Njuncs) - { - /* Determine if control conditions are satisfied */ - if (Control[i].Type == LOWLEVEL - && NodeHead[n] <= Control[i].Grade + Htol ) - reset = 1; - if (Control[i].Type == HILEVEL - && NodeHead[n] >= Control[i].Grade - Htol ) - reset = 1; - } - - /* Determine if control forces a status or setting change */ - if (reset == 1) - { - change = 0; - s = LinkStatus[k]; - if (Link[k].Type == PIPE) - { - if (s != Control[i].Status) change = 1; - } - if (Link[k].Type == PUMP) - { - if (LinkSetting[k] != Control[i].Setting) change = 1; - } - if (Link[k].Type >= PRV) - { - if (LinkSetting[k] != Control[i].Setting) change = 1; - else if (LinkSetting[k] == MISSING && - s != Control[i].Status) change = 1; - } - - /* If a change occurs, update status & setting */ - if (change) - { - LinkStatus[k] = Control[i].Status; - if (Link[k].Type > PIPE) LinkSetting[k] = Control[i].Setting; - if (Statflag == FULL) writestatchange(k,s,LinkStatus[k]); - - /* Re-set flow if status has changed */ -// if (S[k] != s) initlinkflow(k, S[k], K[k]); - anychange = 1; - } - } - } - return(anychange); -} /* End of pswitch */ - - -double newflows() -/* -**---------------------------------------------------------------- -** Input: none -** Output: returns solution convergence error -** Purpose: updates link flows after new nodal heads computed -**---------------------------------------------------------------- -*/ -{ - double dh, /* Link head loss */ - dq; /* Link flow change */ - double dqsum, /* Network flow change */ - qsum; /* Network total flow */ - int k, n, n1, n2; - - /* Initialize net inflows (i.e., demands) at tanks */ - for (n=Njuncs+1; n <= Nnodes; n++) NodeDemand[n] = 0.0; - - /* Initialize sum of flows & corrections */ - qsum = 0.0; - dqsum = 0.0; - - /* Update flows in all links */ - for (k=1; k<=Nlinks; k++) - { - - /* - ** Apply flow update formula: - ** dq = Y - P*(new head loss) - ** P = 1/(dh/dq) - ** Y = P*(head loss based on current flow) - ** where P & Y were computed in newcoeffs(). - */ - - n1 = Link[k].N1; - n2 = Link[k].N2; - dh = NodeHead[n1] - NodeHead[n2]; - dq = Y[k] - P[k]*dh; - - /* Adjust flow change by the relaxation factor */ //(2.00.11 - LR) - dq *= RelaxFactor; //(2.00.11 - LR) - - /* Prevent flow in constant HP pumps from going negative */ - if (Link[k].Type == PUMP) - { - n = PUMPINDEX(k); - if (Pump[n].Ptype == CONST_HP && dq > Q[k]) dq = Q[k]/2.0; - } - Q[k] -= dq; - - /* Update sum of absolute flows & flow corrections */ - qsum += ABS(Q[k]); - dqsum += ABS(dq); - - /* Update net flows to tanks */ - if ( LinkStatus[k] > CLOSED ) //(2.00.12 - LR) - { - if (n1 > Njuncs) NodeDemand[n1] -= Q[k]; - if (n2 > Njuncs) NodeDemand[n2] += Q[k]; - } - - } - - /* Update emitter flows */ - for (k=1; k<=Njuncs; k++) - { - if (Node[k].Ke == 0.0) continue; - dq = emitflowchange(k); - E[k] -= dq; - qsum += ABS(E[k]); - dqsum += ABS(dq); - } - - /* Return ratio of total flow corrections to total flow */ - if (qsum > Hacc) return(dqsum/qsum); - else return(dqsum); - -} /* End of newflows */ - - -void newcoeffs() -/* -**-------------------------------------------------------------- -** Input: none -** Output: none -** Purpose: computes coefficients of linearized network eqns. -**-------------------------------------------------------------- -*/ -{ - memset(Aii,0,(Nnodes+1)*sizeof(double)); /* Reset coeffs. to 0 */ - memset(Aij,0,(Ncoeffs+1)*sizeof(double)); - memset(F,0,(Nnodes+1)*sizeof(double)); - memset(X,0,(Nnodes+1)*sizeof(double)); - memset(P,0,(Nlinks+1)*sizeof(double)); - memset(Y,0,(Nlinks+1)*sizeof(double)); - linkcoeffs(); /* Compute link coeffs. */ - emittercoeffs(); /* Compute emitter coeffs.*/ - nodecoeffs(); /* Compute node coeffs. */ - valvecoeffs(); /* Compute valve coeffs. */ -} /* End of newcoeffs */ - - -void linkcoeffs() -/* -**-------------------------------------------------------------- -** Input: none -** Output: none -** Purpose: computes solution matrix coefficients for links -**-------------------------------------------------------------- -*/ -{ - int k,n1,n2; - - /* Examine each link of network */ - for (k=1; k<=Nlinks; k++) - { - n1 = Link[k].N1; /* Start node of link */ - n2 = Link[k].N2; /* End node of link */ - - /* Compute P[k] = 1 / (dh/dQ) and Y[k] = h * P[k] */ - /* for each link k (where h = link head loss). */ - /* FCVs, PRVs, and PSVs with non-fixed status */ - /* are analyzed later. */ - - switch (Link[k].Type) - { - case CV: - case PIPE: pipecoeff(k); break; - case PUMP: pumpcoeff(k); break; - case PBV: pbvcoeff(k); break; - case TCV: tcvcoeff(k); break; - case GPV: gpvcoeff(k); break; - case FCV: - case PRV: - case PSV: /* If valve status fixed then treat as pipe */ - /* otherwise ignore the valve for now. */ - if (LinkSetting[k] == MISSING) valvecoeff(k); //pipecoeff(k); //(2.00.11 - LR) - else continue; - break; - default: continue; - } - - /* Update net nodal inflows (X), solution matrix (A) and RHS array (F) */ - /* (Use covention that flow out of node is (-), flow into node is (+)) */ - X[n1] -= Q[k]; - X[n2] += Q[k]; - Aij[Ndx[k]] -= P[k]; /* Off-diagonal coeff. */ - if (n1 <= Njuncs) /* Node n1 is junction */ - { - Aii[Row[n1]] += P[k]; /* Diagonal coeff. */ - F[Row[n1]] += Y[k]; /* RHS coeff. */ - } - else F[Row[n2]] += (P[k]*NodeHead[n1]); /* Node n1 is a tank */ - if (n2 <= Njuncs) /* Node n2 is junction */ - { - Aii[Row[n2]] += P[k]; /* Diagonal coeff. */ - F[Row[n2]] -= Y[k]; /* RHS coeff. */ - } - else F[Row[n1]] += (P[k]*NodeHead[n2]); /* Node n2 is a tank */ - } -} /* End of linkcoeffs */ - - -void nodecoeffs() -/* -**---------------------------------------------------------------- -** Input: none -** Output: none -** Purpose: completes calculation of nodal flow imbalance (X) -** & flow correction (F) arrays -**---------------------------------------------------------------- -*/ -{ - int i; - - /* For junction nodes, subtract demand flow from net */ - /* flow imbalance & add imbalance to RHS array F. */ - for (i=1; i<=Njuncs; i++) - { - X[i] -= NodeDemand[i]; - F[Row[i]] += X[i]; - } -} /* End of nodecoeffs */ - - -void valvecoeffs() -/* -**-------------------------------------------------------------- -** Input: none -** Output: none -** Purpose: computes matrix coeffs. for PRVs, PSVs & FCVs -** whose status is not fixed to OPEN/CLOSED -**-------------------------------------------------------------- -*/ -{ - int i,k,n1,n2; - - for (i=1; i<=Nvalves; i++) /* Examine each valve */ - { - k = Valve[i].Link; /* Link index of valve */ - if (LinkSetting[k] == MISSING) continue; /* Valve status fixed */ - n1 = Link[k].N1; /* Start & end nodes */ - n2 = Link[k].N2; - switch (Link[k].Type) /* Call valve-specific */ - { /* function */ - case PRV: prvcoeff(k,n1,n2); break; - case PSV: psvcoeff(k,n1,n2); break; - case FCV: fcvcoeff(k,n1,n2); break; - default: continue; - } - } -} /* End of valvecoeffs */ - - -void emittercoeffs() -/* -**-------------------------------------------------------------- -** Input: none -** Output: none -** Purpose: computes matrix coeffs. for emitters -** -** Note: Emitters consist of a fictitious pipe connected to -** a fictitious reservoir whose elevation equals that -** of the junction. The headloss through this pipe is -** Ke*(Flow)^Qexp, where Ke = emitter headloss coeff. -**-------------------------------------------------------------- -*/ -{ - int i; - double ke; - double p; - double q; - double y; - double z; - for (i=1; i<=Njuncs; i++) - { - if (Node[i].Ke == 0.0) continue; - ke = MAX(CSMALL, Node[i].Ke); - q = E[i]; - z = ke*pow(ABS(q),Qexp); - p = Qexp*z/ABS(q); - if (p < RQtol) p = 1.0/RQtol; - else p = 1.0/p; - y = SGN(q)*z*p; - Aii[Row[i]] += p; - F[Row[i]] += y + p*Node[i].El; - X[i] -= q; - } -} - - -double emitflowchange(int i) -/* -**-------------------------------------------------------------- -** Input: i = node index -** Output: returns change in flow at an emitter node -** Purpose: computes flow change at an emitter node -**-------------------------------------------------------------- -*/ -{ - double ke, p; - ke = MAX(CSMALL, Node[i].Ke); - p = Qexp*ke*pow(ABS(E[i]),(Qexp-1.0)); - if (p < RQtol) - p = 1/RQtol; - else - p = 1.0/p; - return(E[i]/Qexp - p*(NodeHead[i] - Node[i].El)); -} - - -void pipecoeff(int k) -/* -**-------------------------------------------------------------- -** Input: k = link index -** Output: none -** Purpose: computes P & Y coefficients for pipe k -** -** P = inverse head loss gradient = 1/(dh/dQ) -** Y = flow correction term = h*P -**-------------------------------------------------------------- -*/ -{ - double hpipe, /* Normal head loss */ - hml, /* Minor head loss */ - ml, /* Minor loss coeff. */ - p, /* q*(dh/dq) */ - q, /* Abs. value of flow */ - r, /* Resistance coeff. */ - r1, /* Total resistance factor */ - f, /* D-W friction factor */ - dfdq; /* Derivative of fric. fact. */ - - /* For closed pipe use headloss formula: h = CBIG*q */ - if (LinkStatus[k] <= CLOSED) - { - P[k] = 1.0/CBIG; - Y[k] = Q[k]; - return; - } - - /* Evaluate headloss coefficients */ - q = ABS(Q[k]); /* Absolute flow */ - ml = Link[k].Km; /* Minor loss coeff. */ - r = Link[k].R; /* Resistance coeff. */ - f = 1.0; /* D-W friction factor */ - if (Formflag == DW) f = DWcoeff(k,&dfdq); - r1 = f*r+ml; - - /* Use large P coefficient for small flow resistance product */ - if (r1*q < RQtol) - { - P[k] = 1.0/RQtol; - Y[k] = Q[k]/Hexp; - return; - } - - /* Compute P and Y coefficients */ - if (Formflag == DW) /* D-W eqn. */ - { - hpipe = r1*SQR(q); /* Total head loss */ - p = 2.0*r1*q; /* |dh/dQ| */ - /* + dfdq*r*q*q;*/ /* Ignore df/dQ term */ - p = 1.0/p; - P[k] = p; - Y[k] = SGN(Q[k])*hpipe*p; - } - else /* H-W or C-M eqn. */ - { - hpipe = r*pow(q,Hexp); /* Friction head loss */ - p = Hexp*hpipe; /* Q*dh(friction)/dQ */ - if (ml > 0.0) - { - hml = ml*q*q; /* Minor head loss */ - p += 2.0*hml; /* Q*dh(Total)/dQ */ - } - else hml = 0.0; - p = Q[k]/p; /* 1 / (dh/dQ) */ - P[k] = ABS(p); - Y[k] = p*(hpipe + hml); - } -} /* End of pipecoeff */ - - -double DWcoeff(int k, double *dfdq) -/* -**-------------------------------------------------------------- -** Input: k = link index -** Output: returns Darcy-Weisbach friction factor -** Purpose: computes Darcy-Weisbach friction factor -** -** Uses interpolating polynomials developed by -** E. Dunlop for transition flow from 2000 < Re < 4000. -** -** df/dq term is ignored as it slows convergence rate. -**-------------------------------------------------------------- -*/ -{ - double q, /* Abs. value of flow */ - f; /* Friction factor */ - double x1,x2,x3,x4, - y1,y2,y3, - fa,fb,r; - double s,w; - - *dfdq = 0.0; - if (Link[k].Type > PIPE) return(1.0); /* Only apply to pipes */ - q = ABS(Q[k]); - s = Viscos*Link[k].Diam; - w = q/s; /* w = Re(Pi/4) */ - if (w >= A1) /* Re >= 4000; Colebrook Formula */ - { - y1 = A8/pow(w,0.9); - y2 = Link[k].Kc/(3.7*Link[k].Diam) + y1; - y3 = A9*log(y2); - f = 1.0/SQR(y3); - /* *dfdq = (2.0+AA*y1/(y2*y3))*f; */ /* df/dq */ - } - else if (w > A2) /* Re > 2000; Interpolation formula */ - { - y2 = Link[k].Kc/(3.7*Link[k].Diam) + AB; - y3 = A9*log(y2); - fa = 1.0/SQR(y3); - fb = (2.0+AC/(y2*y3))*fa; - r = w/A2; - x1 = 7.0*fa - fb; - x2 = 0.128 - 17.0*fa + 2.5*fb; - x3 = -0.128 + 13.0*fa - (fb+fb); - x4 = r*(0.032 - 3.0*fa + 0.5*fb); - f = x1 + r*(x2 + r*(x3+x4)); - /* *dfdq = (x1 + x1 + r*(3.0*x2 + r*(4.0*x3 + 5.0*x4))); */ - } - else if (w > A4) /* Laminar flow: Hagen-Poiseuille Formula */ - { - f = A3*s/q; - /* *dfdq = A3*s; */ - } - else - { - f = 8.0; - *dfdq = 0.0; - } - return(f); -} /* End of DWcoeff */ - - -/*** Updated 10/25/00 ***/ -/*** Updated 12/29/00 ***/ -void pumpcoeff(int k) -/* -**-------------------------------------------------------------- -** Input: k = link index -** Output: none -** Purpose: computes P & Y coeffs. for pump in link k -**-------------------------------------------------------------- -*/ -{ - int p; /* Pump index */ - double h0, /* Shutoff head */ - q, /* Abs. value of flow */ - r, /* Flow resistance coeff. */ - n; /* Flow exponent coeff. */ - - double setting = LinkSetting[k]; - - /* Use high resistance pipe if pump closed or cannot deliver head */ - if (LinkStatus[k] <= CLOSED || setting == 0.0) - { - //pipecoeff(k); //(2.00.11 - LR) - P[k] = 1.0/CBIG; //(2.00.11 - LR) - Y[k] = Q[k]; //(2.00.11 - LR) - return; - } - - q = ABS(Q[k]); - q = MAX(q,TINY); - p = PUMPINDEX(k); - - /* Get pump curve coefficients for custom pump curve. */ - if (Pump[p].Ptype == CUSTOM) - { - /* Find intercept (h0) & slope (r) of pump curve */ - /* line segment which contains speed-adjusted flow. */ - curvecoeff(Pump[p].Hcurve, q/setting, &h0, &r); - - /* Determine head loss coefficients. */ - Pump[p].H0 = -h0; - Pump[p].R = -r; - Pump[p].N = 1.0; - } - - /* Adjust head loss coefficients for pump speed. */ - h0 = SQR(setting)*Pump[p].H0; - n = Pump[p].N; - r = Pump[p].R*pow(setting,2.0-n); - if (n != 1.0) r = n*r*pow(q,n-1.0); - - /* Compute inverse headloss gradient (P) and flow correction factor (Y) */ - P[k] = 1.0/MAX(r,RQtol); - Y[k] = Q[k]/n + P[k]*h0; -} /* End of pumpcoeff */ - - -/*** Updated 10/25/00 ***/ -/*** Updated 12/29/00 ***/ -void curvecoeff(int i, double q, double *h0, double *r) -/* -**------------------------------------------------------------------- -** Input: i = curve index -** q = flow rate -** Output: *h0 = head at zero flow (y-intercept) -** *r = dHead/dFlow (slope) -** Purpose: computes intercept and slope of head v. flow curve -** at current flow. -**------------------------------------------------------------------- -*/ -{ - int k1, k2, npts; - double *x, *y; - - /* Remember that curve is stored in untransformed units */ - q *= Ucf[FLOW]; - x = Curve[i].X; /* x = flow */ - y = Curve[i].Y; /* y = head */ - npts = Curve[i].Npts; - - /* Find linear segment of curve that brackets flow q */ - k2 = 0; - while (k2 < npts && x[k2] < q) k2++; - if (k2 == 0) k2++; - else if (k2 == npts) k2--; - k1 = k2 - 1; - - /* Compute slope and intercept of this segment */ - *r = (y[k2]-y[k1])/(x[k2]-x[k1]); - *h0 = y[k1] - (*r)*x[k1]; - - /* Convert units */ - *h0 = (*h0)/Ucf[HEAD]; - *r = (*r)*Ucf[FLOW]/Ucf[HEAD]; -} /* End of curvecoeff */ - - -void gpvcoeff(int k) -/* -**-------------------------------------------------------------- -** Input: k = link index -** Output: none -** Purpose: computes P & Y coeffs. for general purpose valve -**-------------------------------------------------------------- -*/ -{ - double h0, /* Headloss curve intercept */ - q, /* Abs. value of flow */ - r; /* Flow resistance coeff. */ - -/*** Updated 9/7/00 ***/ - /* Treat as a pipe if valve closed */ - if (LinkStatus[k] == CLOSED) valvecoeff(k); //pipecoeff(k); //(2.00.11 - LR) - - /* Otherwise utilize headloss curve */ - /* whose index is stored in K */ - else - { - /* Find slope & intercept of headloss curve. */ - q = ABS(Q[k]); - q = MAX(q,TINY); - -/*** Updated 10/25/00 ***/ -/*** Updated 12/29/00 ***/ - curvecoeff((int)ROUND(LinkSetting[k]),q,&h0,&r); - - /* Compute inverse headloss gradient (P) */ - /* and flow correction factor (Y). */ - P[k] = 1.0 / MAX(r,RQtol); - Y[k] = P[k]*(h0 + r*q)*SGN(Q[k]); //(2.00.11 - LR) - } -} - - -void pbvcoeff(int k) -/* -**-------------------------------------------------------------- -** Input: k = link index -** Output: none -** Purpose: computes P & Y coeffs. for pressure breaker valve -**-------------------------------------------------------------- -*/ -{ - /* If valve fixed OPEN or CLOSED then treat as a pipe */ - if (LinkSetting[k] == MISSING || LinkSetting[k] == 0.0) valvecoeff(k); //pipecoeff(k); //(2.00.11 - LR) - - /* If valve is active */ - else - { - /* Treat as a pipe if minor loss > valve setting */ - if (Link[k].Km*SQR(Q[k]) > LinkSetting[k]) valvecoeff(k); //pipecoeff(k); //(2.00.11 - LR) - - /* Otherwise force headloss across valve to be equal to setting */ - else - { - P[k] = CBIG; - Y[k] = LinkSetting[k]*CBIG; - } - } -} /* End of pbvcoeff */ - - -void tcvcoeff(int k) -/* -**-------------------------------------------------------------- -** Input: k = link index -** Output: none -** Purpose: computes P & Y coeffs. for throttle control valve -**-------------------------------------------------------------- -*/ -{ - double km; - - /* Save original loss coeff. for open valve */ - km = Link[k].Km; - - /* If valve not fixed OPEN or CLOSED, compute its loss coeff. */ - if (LinkSetting[k] != MISSING) - Link[k].Km = 0.02517*LinkSetting[k]/(SQR(Link[k].Diam)*SQR(Link[k].Diam)); - - /* Then apply usual pipe formulas */ - valvecoeff(k); //pipecoeff(k); //(2.00.11 - LR) - - /* Restore original loss coeff. */ - Link[k].Km = km; -} /* End of tcvcoeff */ - - -void prvcoeff(int k, int n1, int n2) -/* -**-------------------------------------------------------------- -** Input: k = link index -** n1 = upstream node of valve -** n2 = downstream node of valve -** Output: none -** Purpose: computes solution matrix coeffs. for pressure -** reducing valves -**-------------------------------------------------------------- -*/ -{ - int i,j; /* Rows of solution matrix */ - double hset; /* Valve head setting */ - i = Row[n1]; /* Matrix rows of nodes */ - j = Row[n2]; - hset = Node[n2].El + LinkSetting[k]; /* Valve setting */ - - if (LinkStatus[k] == ACTIVE) - { - /* - Set coeffs. to force head at downstream - node equal to valve setting & force flow (when updated in - newflows()) equal to flow imbalance at downstream node. - */ - P[k] = 0.0; - Y[k] = Q[k] + X[n2]; /* Force flow balance */ - F[j] += (hset*CBIG); /* Force head = hset */ - Aii[j] += CBIG; /* at downstream node */ - if (X[n2] < 0.0) F[i] += X[n2]; - return; - } - - /* - For OPEN, CLOSED, or XPRESSURE valve - compute matrix coeffs. using the valvecoeff() function. //(2.00.11 - LR) - */ - valvecoeff(k); /*pipecoeff(k);*/ //(2.00.11 - LR) - Aij[Ndx[k]] -= P[k]; - Aii[i] += P[k]; - Aii[j] += P[k]; - F[i] += (Y[k]-Q[k]); - F[j] -= (Y[k]-Q[k]); -} /* End of prvcoeff */ - - -void psvcoeff(int k, int n1, int n2) -/* -**-------------------------------------------------------------- -** Input: k = link index -** n1 = upstream node of valve -** n2 = downstream node of valve -** Output: none -** Purpose: computes solution matrix coeffs. for pressure -** sustaining valve -**-------------------------------------------------------------- -*/ -{ - int i,j; /* Rows of solution matrix */ - double hset; /* Valve head setting */ - i = Row[n1]; /* Matrix rows of nodes */ - j = Row[n2]; - hset = Node[n1].El + LinkSetting[k]; /* Valve setting */ - - if (LinkStatus[k] == ACTIVE) - { - /* - Set coeffs. to force head at upstream - node equal to valve setting & force flow (when updated in - newflows()) equal to flow imbalance at upstream node. - */ - P[k] = 0.0; - Y[k] = Q[k] - X[n1]; /* Force flow balance */ - F[i] += (hset*CBIG); /* Force head = hset */ - Aii[i] += CBIG; /* at upstream node */ - if (X[n1] > 0.0) F[j] += X[n1]; - return; - } - - /* - For OPEN, CLOSED, or XPRESSURE valve - compute matrix coeffs. using the valvecoeff() function. //(2.00.11 - LR) - */ - valvecoeff(k); /*pipecoeff(k);*/ //(2.00.11 - LR) - Aij[Ndx[k]] -= P[k]; - Aii[i] += P[k]; - Aii[j] += P[k]; - F[i] += (Y[k]-Q[k]); - F[j] -= (Y[k]-Q[k]); -} /* End of psvcoeff */ - - -void fcvcoeff(int k, int n1, int n2) -/* -**-------------------------------------------------------------- -** Input: k = link index -** n1 = upstream node of valve -** n2 = downstream node of valve -** Output: none -** Purpose: computes solution matrix coeffs. for flow control -** valve -**-------------------------------------------------------------- -*/ -{ - int i,j; /* Rows in solution matrix */ - double q; /* Valve flow setting */ - q = LinkSetting[k]; - i = Row[n1]; - j = Row[n2]; - - /* - If valve active, break network at valve and treat - flow setting as external demand at upstream node - and external supply at downstream node. - */ - if (LinkStatus[k] == ACTIVE) - { - X[n1] -= q; - F[i] -= q; - X[n2] += q; - F[j] += q; - /*P[k] = 0.0;*/ - P[k] = 1.0/CBIG; //(2.00.11 - LR) - Aij[Ndx[k]] -= P[k]; //(2.00.11 - LR) - Aii[i] += P[k]; //(2.00.11 - LR) - Aii[j] += P[k]; //(2.00.11 - LR) - Y[k] = Q[k] - q; - } - /* - Otherwise treat valve as an open pipe - */ - else - { - valvecoeff(k); //pipecoeff(k); //(2.00.11 - LR) - Aij[Ndx[k]] -= P[k]; - Aii[i] += P[k]; - Aii[j] += P[k]; - F[i] += (Y[k]-Q[k]); - F[j] -= (Y[k]-Q[k]); - } -} /* End of fcvcoeff */ - - -/*** New function added. ***/ //(2.00.11 - LR) -void valvecoeff(int k) -/* -**-------------------------------------------------------------- -** Input: k = link index -** Output: none -** Purpose: computes solution matrix coeffs. for a completely -** open, closed, or throttled control valve. -**-------------------------------------------------------------- -*/ -{ - double p; - - // Valve is closed. Use a very small matrix coeff. - if (LinkStatus[k] <= CLOSED) - { - P[k] = 1.0/CBIG; - Y[k] = Q[k]; - return; - } - - // Account for any minor headloss through the valve - if (Link[k].Km > 0.0) - { - p = 2.0*Link[k].Km*fabs(Q[k]); - if ( p < RQtol ) p = RQtol; - P[k] = 1.0/p; - Y[k] = Q[k]/2.0; - } - else - { - P[k] = 1.0/RQtol; - Y[k] = Q[k]; - } -} - -/**************** END OF HYDRAUL.C ***************/ - +/* +********************************************************************* + +HYDRAUL.C -- Hydraulic Simulator for EPANET Program + +VERSION: 2.00 +DATE: 6/5/00 + 9/7/00 + 10/25/00 + 12/29/00 + 3/1/01 + 11/19/01 + 6/24/02 + 8/15/07 (2.00.11) + 2/14/08 (2.00.12) +AUTHOR: L. Rossman + US EPA - NRMRL + + This module contains the network hydraulic simulator. + It simulates the network's hydraulic behavior over an + an extended period of time and writes its results to the + binary file HydFile. + + The entry points for this module are: + openhyd() -- called from ENopenH() in EPANET.C + inithyd() -- called from ENinitH() in EPANET.C + runhyd() -- called from ENrunH() in EPANET.C + nexthyd() -- called from ENnextH() in EPANET.C + closehyd() -- called from ENcloseH() in EPANET.C + tankvolume() -- called from ENsetnodevalue() in EPANET.C + setlinkstatus(), + setlinksetting(), + resistance()-- all called from ENsetlinkvalue() in EPANET.C + + External functions called by this module are: + createsparse() -- see SMATRIX.C + freesparse() -- see SMATRIX.C + linsolve() -- see SMATRIX.C + checkrules() -- see RULES.C + interp() -- see EPANET.C + savehyd() -- see OUTPUT.C + savehydstep() -- see OUTPUT.C + writehydstat() -- see REPORT.C + writehyderr() -- see REPORT.C + writehydwarn() -- see REPORT.C +******************************************************************* +*/ + +#include +#include +#ifndef __APPLE__ +#include +#else +#include +#endif +#include +#include "hash.h" +#include "text.h" +#include "types.h" +#include "epanet2.h" +#include "funcs.h" +#define EXTERN extern +#include "vars.h" + +#define QZERO 1.e-6 /* Equivalent to zero flow */ +#define CBIG 1.e8 /* Big coefficient */ +#define CSMALL 1.e-6 /* Small coefficient */ + +/* Constants used for computing Darcy-Weisbach friction factor */ +#define A1 0.314159265359e04 /* 1000*PI */ +#define A2 0.157079632679e04 /* 500*PI */ +#define A3 0.502654824574e02 /* 16*PI */ +#define A4 6.283185307 /* 2*PI */ +#define A8 4.61841319859 /* 5.74*(PI/4)^.9 */ +#define A9 -8.685889638e-01 /* -2/ln(10) */ +#define AA -1.5634601348 /* -2*.9*2/ln(10) */ +#define AB 3.28895476345e-03 /* 5.74/(4000^.9) */ +#define AC -5.14214965799e-03 /* AA*AB */ + +/*** Updated 3/1/01 ***/ + +/* Function to find flow coeffs. through open/closed valves */ +void valvecoeff(EN_Project *pr, int k); + + +int openhyd(EN_Project *pr) +/* + *-------------------------------------------------------------- + * Input: none + * Output: returns error code + * Purpose: opens hydraulics solver system + *-------------------------------------------------------------- +*/ +{ + int i; + int errcode = 0; + ERRCODE(createsparse(pr)); /* See SMATRIX.C */ + ERRCODE(allocmatrix(pr)); /* Allocate solution matrices */ + for (i=1; i <= pr->network.Nlinks; i++) { /* Initialize flows */ + Slink *link = &pr->network.Link[i]; + initlinkflow(pr, i, link->Stat, link->Kc); + } + return(errcode); +} + + +/*** Updated 3/1/01 ***/ +void inithyd(EN_Project *pr, int initflag) +/* +**-------------------------------------------------------------- +** Input: initflag > 0 if link flows should be re-initialized +** = 0 if not +** Output: none +** Purpose: initializes hydraulics solver system +**-------------------------------------------------------------- +*/ +{ + int i,j; + + time_options_t *time = &pr->time_options; + EN_Network *net = &pr->network; + hydraulics_t *hyd = &pr->hydraulics; + out_file_t *out = &pr->out_files; + + /* Initialize tanks */ + for (i=1; i <= net->Ntanks; i++) { + Stank *tank = &net->Tank[i]; + tank->V = tank->V0; + hyd->NodeHead[tank->Node] = tank->H0; + hyd->NodeDemand[tank->Node] = 0.0; + hyd->OldStat[net->Nlinks+i] = TEMPCLOSED; + } + + /* Initialize emitter flows */ + memset(hyd->EmitterFlows,0,(net->Nnodes+1)*sizeof(double)); + for (i=1; i <= net->Njuncs; i++) { + if (net->Node[i].Ke > 0.0) { + hyd->EmitterFlows[i] = 1.0; + } + } + /* Initialize links */ + for (i=1; i <= net->Nlinks; i++) { + Slink *link = &net->Link[i]; + /* Initialize status and setting */ + hyd->LinkStatus[i] = link->Stat; + hyd->LinkSetting[i] = link->Kc; + + /* Start active control valves in ACTIVE position */ + if ( + (link->Type == EN_PRV || link->Type == EN_PSV + || link->Type == EN_FCV) + && (link->Kc != MISSING) + ) hyd->LinkStatus[i] = ACTIVE; + +/*** Updated 3/1/01 ***/ + /* Initialize flows if necessary */ + if (hyd->LinkStatus[i] <= CLOSED) hyd->LinkFlows[i] = QZERO; + else if (ABS(hyd->LinkFlows[i]) <= QZERO || initflag > 0) + initlinkflow(pr, i, hyd->LinkStatus[i], hyd->LinkSetting[i]); + + /* Save initial status */ + hyd->OldStat[i] = hyd->LinkStatus[i]; + } + + /* Reset pump energy usage */ + for (i=1; i <= net->Npumps; i++) + { + for (j=0; j<6; j++) { + net->Pump[i].Energy[j] = 0.0; + } + } + + /* Re-position hydraulics file */ + if (pr->save_options.Saveflag) { + fseek(out->HydFile,out->HydOffset,SEEK_SET); + } + +/*** Updated 3/1/01 ***/ + /* Initialize current time */ + hyd->Haltflag = 0; + time->Htime = 0; + time->Hydstep = 0; + time->Rtime = time->Rstep; +} + + +int runhyd(EN_Project *pr, long *t) +/* +**-------------------------------------------------------------- +** Input: none +** Output: t = pointer to current time (in seconds) +** Returns: error code +** Purpose: solves network hydraulics in a single time period +**-------------------------------------------------------------- +*/ +{ + int iter; /* Iteration count */ + int errcode; /* Error code */ + double relerr; /* Solution accuracy */ + + hydraulics_t *hyd = &pr->hydraulics; + time_options_t *time = &pr->time_options; + report_options_t *rep = &pr->report; + + /* Find new demands & control actions */ + *t = time->Htime; + demands(pr); + controls(pr); + + /* Solve network hydraulic equations */ + errcode = netsolve(pr,&iter,&relerr); + if (!errcode) { + /* Report new status & save results */ + if (rep->Statflag) { + writehydstat(pr,iter,relerr); + } + + /* solution info */ + hyd->relativeError = relerr; + hyd->iterations = iter; + +/*** Updated 3/1/01 ***/ + /* If system unbalanced and no extra trials */ + /* allowed, then activate the Haltflag. */ + if (relerr > hyd->Hacc && hyd->ExtraIter == -1) { + hyd->Haltflag = 1; + } + + /* Report any warning conditions */ + if (!errcode) { + errcode = writehydwarn(pr,iter,relerr); + } + } + return(errcode); +} /* end of runhyd */ + + +int nexthyd(EN_Project *pr, long *tstep) +/* +**-------------------------------------------------------------- +** Input: none +** Output: tstep = pointer to time step (in seconds) +** Returns: error code +** Purpose: finds length of next time step & updates tank +** levels and rule-based contol actions +**-------------------------------------------------------------- +*/ +{ + long hydstep; /* Actual time step */ + int errcode = 0; /* Error code */ + + hydraulics_t *hyd = &pr->hydraulics; + time_options_t *top = &pr->time_options; + +/*** Updated 3/1/01 ***/ + /* Save current results to hydraulics file and */ + /* force end of simulation if Haltflag is active */ + if (pr->save_options.Saveflag) { + errcode = savehyd(pr,&top->Htime); + } + if (hyd->Haltflag) { + top->Htime = top->Dur; + } + + /* Compute next time step & update tank levels */ + *tstep = 0; + hydstep = 0; + if (top->Htime < top->Dur) { + hydstep = timestep(pr); + } + if (pr->save_options.Saveflag) { + errcode = savehydstep(pr,&hydstep); + } + + /* Compute pumping energy */ + if (top->Dur == 0) { + addenergy(pr,0); + } + else if (top->Htime < top->Dur) { + addenergy(pr,hydstep); + } + + /* Update current time. */ + if (top->Htime < top->Dur) /* More time remains */ + { + top->Htime += hydstep; + if (top->Htime >= top->Rtime) { + top->Rtime += top->Rstep; + } + } + else + { + top->Htime++; /* Force completion of analysis */ + if (pr->quality.OpenQflag) { + pr->quality.Qtime++; // force completion of wq analysis too + } + } + *tstep = hydstep; + return(errcode); +} + + +void closehyd(EN_Project *pr) +/* +**-------------------------------------------------------------- +** Input: none +** Output: returns error code +** Purpose: closes hydraulics solver system +**-------------------------------------------------------------- +*/ +{ + freesparse(pr); /* see SMATRIX.C */ + freematrix(pr); +} + + +int allocmatrix(EN_Project *pr) +/* +**-------------------------------------------------------------- +** Input: none +** Output: returns error code +** Purpose: allocates memory used for solution matrix coeffs. +**-------------------------------------------------------------- +*/ +{ + EN_Network *net = &pr->network; + hydraulics_t *hyd = &pr->hydraulics; + solver_t *s = &hyd->solver; + + int errcode = 0; + s->Aii = (double *) calloc(net->Nnodes+1,sizeof(double)); + s->Aij = (double *) calloc(hyd->Ncoeffs+1,sizeof(double)); + s->F = (double *) calloc(net->Nnodes+1,sizeof(double)); + hyd->EmitterFlows = (double *) calloc(net->Nnodes+1,sizeof(double)); + s->P = (double *) calloc(net->Nlinks+1,sizeof(double)); + s->Y = (double *) calloc(net->Nlinks+1,sizeof(double)); + hyd->X_tmp = (double *) calloc(MAX((net->Nnodes+1),(net->Nlinks+1)),sizeof(double)); + hyd->OldStat = (StatType *) calloc(net->Nlinks+net->Ntanks+1, sizeof(StatType)); + ERRCODE(MEMCHECK(s->Aii)); + ERRCODE(MEMCHECK(s->Aij)); + ERRCODE(MEMCHECK(s->F)); + ERRCODE(MEMCHECK(hyd->EmitterFlows)); + ERRCODE(MEMCHECK(s->P)); + ERRCODE(MEMCHECK(s->Y)); + ERRCODE(MEMCHECK(hyd->X_tmp)); + ERRCODE(MEMCHECK(hyd->OldStat)); + return(errcode); +} /* end of allocmatrix */ + + +void freematrix(EN_Project *pr) +/* +**-------------------------------------------------------------- +** Input: none +** Output: none +** Purpose: frees memory used for solution matrix coeffs. +**-------------------------------------------------------------- +*/ +{ + hydraulics_t *hyd = &pr->hydraulics; + solver_t *s = &hyd->solver; + + free(s->Aii); + free(s->Aij); + free(s->F); + free(hyd->EmitterFlows); + free(s->P); + free(s->Y); + free(hyd->X_tmp); + free(hyd->OldStat); +} /* end of freematrix */ + + +void initlinkflow(EN_Project *pr, int i, char s, double k) +/* +**-------------------------------------------------------------------- +** Input: i = link index +** s = link status +** k = link setting (i.e., pump speed) +** Output: none +** Purpose: sets initial flow in link to QZERO if link is closed, +** to design flow for a pump, or to flow at velocity of +** 1 fps for other links. +**-------------------------------------------------------------------- +*/ +{ + hydraulics_t *hyd = &pr->hydraulics; + EN_Network *n = &pr->network; + Slink *link = &n->Link[i]; + + if (s == CLOSED) { + hyd->LinkFlows[i] = QZERO; + } + else if (link->Type == EN_PUMP) { + hyd->LinkFlows[i] = k * n->Pump[findpump(n,i)].Q0; + } + else { + hyd->LinkFlows[i] = PI * SQR(link->Diam)/4.0; + } +} + + +/*** Updated 9/7/00 ***/ +void setlinkflow(EN_Project *pr, int k, double dh) +/* +**-------------------------------------------------------------- +** Input: k = link index +** dh = headloss across link +** Output: none +** Purpose: sets flow in link based on current headloss +**-------------------------------------------------------------- +*/ +{ + int i,p; + double h0; + double x,y; + EN_Network *net = &pr->network; + hydraulics_t *hyd = &pr->hydraulics; + Slink *link = &net->Link[k]; + Scurve *curve; + + switch (link->Type) + { + case EN_CVPIPE: + case EN_PIPE: + + /* For Darcy-Weisbach formula: */ + /* use approx. inverse of formula. */ + if (hyd->Formflag == DW) { + x = -log(hyd->LinkSetting[k]/3.7/link->Diam); + y = sqrt(ABS(dh)/link->R/1.32547); + hyd->LinkFlows[k] = x*y; + } + + /* For Hazen-Williams or Manning formulas: */ + /* use inverse of formula. */ + else { + x = ABS(dh)/link->R; + y = 1.0/hyd->Hexp; + hyd->LinkFlows[k] = pow(x,y); + } + + /* Change sign of flow to match sign of headloss */ + if (dh < 0.0) { + hyd->LinkFlows[k] = -hyd->LinkFlows[k]; + } + + break; + + case EN_PUMP: + + /* Convert headloss to pump head gain */ + dh = -dh; + p = findpump(net,k); + + /* For custom pump curve, interpolate from curve */ + if (net->Pump[p].Ptype == CUSTOM) + { + dh = -dh * pr->Ucf[HEAD] / SQR(hyd->LinkSetting[k]); + i = net->Pump[p].Hcurve; + curve = &net->Curve[i]; + hyd->LinkFlows[k] = interp(curve->Npts,curve->Y,curve->X,dh) * hyd->LinkSetting[k] / pr->Ucf[FLOW]; + } + + /* Otherwise use inverse of power curve */ + else + { + h0 = -SQR(hyd->LinkSetting[k])*net->Pump[p].H0; + x = pow(hyd->LinkSetting[k],2.0 - net->Pump[p].N); + x = ABS(h0-dh)/(net->Pump[p].R*x), + y = 1.0/net->Pump[p].N; + hyd->LinkFlows[k] = pow(x,y); + } + break; + default: + break; + } +} + + +void setlinkstatus(EN_Project *pr, int index, char value, StatType *s, double *k) +/*---------------------------------------------------------------- +** Input: index = link index +** value = 0 (CLOSED) or 1 (OPEN) +** s = pointer to link status +** k = pointer to link setting +** Output: none +** Purpose: sets link status to OPEN or CLOSED +**---------------------------------------------------------------- +*/ +{ + EN_Network *net = &pr->network; + Slink *link = &net->Link[index]; + EN_LinkType t = link->Type; + + /* Status set to open */ + if (value == 1) { + /* Adjust link setting for pumps & valves */ + if (t == EN_PUMP) { + *k = 1.0; + } + if (t > EN_PUMP && t != EN_GPV) { + *k = MISSING; + } + /* Reset link flow if it was originally closed */ + *s = OPEN; + } + + /* Status set to closed */ + else if (value == 0) { + /* Adjust link setting for pumps & valves */ + if (t == EN_PUMP) { + *k = 0.0; + } + if (t > EN_PUMP && t != EN_GPV) { + *k = MISSING; + } + /* Reset link flow if it was originally open */ + *s = CLOSED; + } +} + + +void setlinksetting(EN_Project *pr, int index, double value, StatType *s, double *k) +/*---------------------------------------------------------------- +** Input: index = link index +** value = pump speed or valve setting +** s = pointer to link status +** k = pointer to link setting +** Output: none +** Purpose: sets pump speed or valve setting, adjusting link +** status and flow when necessary +**---------------------------------------------------------------- +*/ +{ + EN_Network *net = &pr->network; + Slink *link = &net->Link[index]; + EN_LinkType t = link->Type; + + /* For a pump, status is OPEN if speed > 0, CLOSED otherwise */ + if (t == EN_PUMP) + { + *k = value; + if (value > 0 && *s <= CLOSED) { + *s = OPEN; + } + if (value == 0 && *s > CLOSED) { + *s = CLOSED; + } + } + + /* For FCV, activate it */ + else if (t == EN_FCV) { + *k = value; + *s = ACTIVE; + } + + /* Open closed control valve with fixed status (setting = MISSING) */ + else { + if (*k == MISSING && *s <= CLOSED) { + *s = OPEN; + } + *k = value; + } +} + + +void resistance(EN_Project *pr, int k) +/* +**-------------------------------------------------------------------- +** Input: k = link index +** Output: none +** Purpose: computes link flow resistance +**-------------------------------------------------------------------- +*/ +{ + EN_Network *net = &pr->network; + hydraulics_t *hyd = &pr->hydraulics; + + double e,d,L; + + Slink *link = &net->Link[k]; + link->R = CSMALL; + + + switch (link->Type) { + /* Link is a pipe. Compute resistance based on headloss formula. */ + /* Friction factor for D-W formula gets included during solution */ + /* process in pipecoeff() function. */ + case EN_CVPIPE: + case EN_PIPE: + e = link->Kc; /* Roughness coeff. */ + d = link->Diam; /* Diameter */ + L = link->Len; /* Length */ + switch(hyd->Formflag) + { + case HW: + link->R = 4.727*L/pow(e,hyd->Hexp)/pow(d,4.871); + break; + case DW: + link->R = L/2.0/32.2/d/SQR(PI*SQR(d)/4.0); + break; + case CM: + link->R = SQR(4.0*e/(1.49*PI*d*d)) * pow((d/4.0),-1.333)*L; + } + break; + + /* Link is a pump. Use negligible resistance. */ + case EN_PUMP: + link->R = CBIG; //CSMALL; + break; + default: + break; + } +} + + +void demands(EN_Project *pr) +/* +**-------------------------------------------------------------------- +** Input: none +** Output: none +** Purpose: computes demands at nodes during current time period +**-------------------------------------------------------------------- +*/ +{ + int i,j,n; + long k,p; + double djunc, sum; + Pdemand demand; + + EN_Network *net = &pr->network; + hydraulics_t *hyd = &pr->hydraulics; + time_options_t *top = &pr->time_options; + + /* Determine total elapsed number of pattern periods */ + p = (top->Htime + top->Pstart) / top->Pstep; + + /* Update demand at each node according to its assigned pattern */ + hyd->Dsystem = 0.0; /* System-wide demand */ + for (i=1; i <= net->Njuncs; i++) { + sum = 0.0; + for (demand = net->Node[i].D; demand != NULL; demand = demand->next) { + /* + pattern period (k) = (elapsed periods) modulus + (periods per pattern) + */ + j = demand->Pat; + k = p % (long) net->Pattern[j].Length; + djunc = (demand->Base) * net->Pattern[j].F[k] * hyd->Dmult; + if (djunc > 0.0) { + hyd->Dsystem += djunc; + } + sum += djunc; + } + hyd->NodeDemand[i] = sum; + } + + /* Update head at fixed grade nodes with time patterns. */ + for (n=1; n <= net->Ntanks; n++) { + Stank *tank = &net->Tank[n]; + if (tank->A == 0.0) { + j = tank->Pat; + if (j > 0) { + k = p % (long) net->Pattern[j].Length; + i = tank->Node; + hyd->NodeHead[i] = net->Node[i].El * net->Pattern[j].F[k]; + } + } + } + + /* Update status of pumps with utilization patterns */ + for (n=1; n <= net->Npumps; n++) + { + Spump *pump = &net->Pump[n]; + j = pump->Upat; + if (j > 0) { + i = pump->Link; + k = p % (long) net->Pattern[j].Length; + setlinksetting(pr, i, net->Pattern[j].F[k], &hyd->LinkStatus[i], &hyd->LinkSetting[i]); + } + } +} /* End of demands */ + + +int controls(EN_Project *pr) +/* +**--------------------------------------------------------------------- +** Input: none +** Output: number of links whose setting changes +** Purpose: implements simple controls based on time or tank levels +**--------------------------------------------------------------------- +*/ +{ + int i, k, n, reset, setsum; + double h, vplus; + double v1, v2; + double k1, k2; + char s1, s2; + Slink *link; + + EN_Network *net = &pr->network; + time_options_t *top = &pr->time_options; + hydraulics_t *hyd = &pr->hydraulics; + + /* Examine each control statement */ + setsum = 0; + for (i=1; i <= net->Ncontrols; i++) + { + Scontrol *control = &net->Control[i]; + /* Make sure that link is defined */ + reset = 0; + if ( (k = control->Link) <= 0) { + continue; + } + link = &net->Link[k]; + /* Link is controlled by tank level */ + if ((n = control->Node) > 0 && n > net->Njuncs) + { + h = hyd->NodeHead[n]; + vplus = ABS(hyd->NodeDemand[n]); + v1 = tankvolume(pr,n - net->Njuncs,h); + v2 = tankvolume(pr,n - net->Njuncs, control->Grade); + if (control->Type == LOWLEVEL && v1 <= v2 + vplus) + reset = 1; + if (control->Type == HILEVEL && v1 >= v2 - vplus) + reset = 1; + } + + /* Link is time-controlled */ + if (control->Type == TIMER) + { + if (control->Time == top->Htime) reset = 1; + } + + /* Link is time-of-day controlled */ + if (control->Type == TIMEOFDAY) + { + if ((top->Htime + top->Tstart) % SECperDAY == control->Time) { + reset = 1; + } + } + + /* Update link status & pump speed or valve setting */ + if (reset == 1) + { + if (hyd->LinkStatus[k] <= CLOSED) { + s1 = CLOSED; + } + else { + s1 = OPEN; + } + s2 = control->Status; + k1 = hyd->LinkSetting[k]; + k2 = k1; + if (link->Type > EN_PIPE) { + k2 = control->Setting; + } + if (s1 != s2 || k1 != k2) { + hyd->LinkStatus[k] = s2; + hyd->LinkSetting[k] = k2; + if (pr->report.Statflag) { + writecontrolaction(pr,k,i); + } + setsum++; + } + } + } + return(setsum); +} /* End of controls */ + + +long timestep(EN_Project *pr) +/* +**---------------------------------------------------------------- +** Input: none +** Output: returns time step until next change in hydraulics +** Purpose: computes time step to advance hydraulic simulation +**---------------------------------------------------------------- +*/ +{ + EN_Network *net = &pr->network; + time_options_t *time = &pr->time_options; + + + long n,t,tstep; + + /* Normal time step is hydraulic time step */ + tstep = time->Hstep; + + /* Revise time step based on time until next demand period */ + n = ((time->Htime + time->Pstart) / time->Pstep) + 1; /* Next pattern period */ + t = n * time->Pstep - time->Htime; /* Time till next period */ + if (t > 0 && t < tstep) { + tstep = t; + } + + /* Revise time step based on time until next reporting period */ + t = time->Rtime - time->Htime; + if (t > 0 && t < tstep) { + tstep = t; + } + + /* Revise time step based on smallest time to fill or drain a tank */ + tanktimestep(pr,&tstep); + + /* Revise time step based on smallest time to activate a control */ + controltimestep(pr,&tstep); + + /* Evaluate rule-based controls (which will also update tank levels) */ + if (net->Nrules > 0) { + ruletimestep(pr,&tstep); + } + else { + tanklevels(pr,tstep); + } + return(tstep); +} + + +int tanktimestep(EN_Project *pr, long *tstep) +/* +**----------------------------------------------------------------- +** Input: *tstep = current time step +** Output: *tstep = modified current time step +** Purpose: revises time step based on shortest time to fill or +** drain a tank +**----------------------------------------------------------------- +*/ +{ + int i,n, tankIdx = 0; + double h,q,v; + long t; + + EN_Network *net = &pr->network; + hydraulics_t *hyd = &pr->hydraulics; + + /* (D[n] is net flow rate into (+) or out of (-) tank at node n) */ + for (i=1; i <= net->Ntanks; i++) + { + Stank *tank = &net->Tank[i]; + if (tank->A == 0.0) { + continue; /* Skip reservoirs */ + } + n = tank->Node; + h = hyd->NodeHead[n]; /* Current tank grade */ + q = hyd->NodeDemand[n]; /* Flow into tank */ + if (ABS(q) <= QZERO) { + continue; + } + if (q > 0.0 && h < tank->Hmax) { + v = tank->Vmax - tank->V; /* Volume to fill */ + } + else if (q < 0.0 && h > tank->Hmin) { + v = tank->Vmin - tank->V; /* Volume to drain (-) */ + } + else { + continue; + } + t = (long)ROUND(v/q); /* Time to fill/drain */ + if (t > 0 && t < *tstep) { + *tstep = t; + tankIdx = n; + } + } + return tankIdx; +} + + +void controltimestep(EN_Project *pr, long *tstep) +/* +**------------------------------------------------------------------ +** Input: *tstep = current time step +** Output: *tstep = modified current time step +** Purpose: revises time step based on shortest time to activate +** a simple control +**------------------------------------------------------------------ +*/ +{ + int i,j,k,n; + double h,q,v; + long t,t1,t2; + Slink *link; + Scontrol *control; + + EN_Network *net = &pr->network; + hydraulics_t *hyd = &pr->hydraulics; + + for (i=1; i <= net->Ncontrols; i++) + { + t = 0; + control = &net->Control[i]; + if ( (n = control->Node) > 0) /* Node control: */ + { + if ((j = n - net->Njuncs) <= 0) { + continue; /* Node is a tank */ + } + h = hyd->NodeHead[n]; /* Current tank grade */ + q = hyd->NodeDemand[n]; /* Flow into tank */ + if (ABS(q) <= QZERO) { + continue; + } + if + ( (h < control->Grade && + control->Type == HILEVEL && /* Tank below hi level */ + q > 0.0) /* & is filling */ + || (h > control->Grade && + control->Type == LOWLEVEL && /* Tank above low level */ + q < 0.0) /* & is emptying */ + ) + { /* Time to reach level */ + v = tankvolume(pr, j, control->Grade) - net->Tank[j].V; + t = (long)ROUND(v/q); + } + } + + if (control->Type == TIMER) /* Time control: */ + { + if (control->Time > pr->time_options.Htime) + t = control->Time - pr->time_options.Htime; + } + + if (control->Type == TIMEOFDAY) /* Time-of-day control: */ + { + t1 = (pr->time_options.Htime + pr->time_options.Tstart) % SECperDAY; + t2 = control->Time; + if (t2 >= t1) + t = t2 - t1; + else + t = SECperDAY - t1 + t2; + } + + if (t > 0 && t < *tstep) /* Revise time step */ + { + /* Check if rule actually changes link status or setting */ + k = control->Link; + link = &net->Link[k]; + if ( (link->Type > EN_PIPE && hyd->LinkSetting[k] != control->Setting) + || (hyd->LinkStatus[k] != control->Status) ) { + *tstep = t; + } + } + } +} /* End of timestep */ + + +void ruletimestep(EN_Project *pr, long *tstep) +/* +**-------------------------------------------------------------- +** Input: *tstep = current time step (sec) +** Output: *tstep = modified time step +** Purpose: updates next time step by checking if any rules +** will fire before then; also updates tank levels. +**-------------------------------------------------------------- +*/ +{ + long tnow, /* Start of time interval for rule evaluation */ + tmax, /* End of time interval for rule evaluation */ + dt, /* Normal time increment for rule evaluation */ + dt1; /* Actual time increment for rule evaluation */ + + EN_Network *net = &pr->network; + time_options_t *time = &pr->time_options; + + /* Find interval of time for rule evaluation */ + tnow = pr->time_options.Htime; + tmax = tnow + *tstep; + + /* If no rules, then time increment equals current time step */ + if (net->Nrules == 0) { + dt = *tstep; + dt1 = dt; + } + + /* Otherwise, time increment equals rule evaluation time step and */ + /* first actual increment equals time until next even multiple of */ + /* Rulestep occurs. */ + else + { + dt = pr->time_options.Rulestep; + dt1 = pr->time_options.Rulestep - (tnow % pr->time_options.Rulestep); + } + + /* Make sure time increment is no larger than current time step */ + dt = MIN(dt, *tstep); + dt1 = MIN(dt1, *tstep); + if (dt1 == 0) { + dt1 = dt; + } + + /* Step through time, updating tank levels, until either */ + /* a rule fires or we reach the end of evaluation period. */ + /* + ** Note: we are updating the global simulation time (Htime) + ** here because it is used by functions in RULES.C + ** to evaluate rules when checkrules() is called. + ** It is restored to its original value after the + ** rule evaluation process is completed (see below). + ** Also note that dt1 will equal dt after the first + ** time increment is taken. + */ + do { + pr->time_options.Htime += dt1; /* Update simulation clock */ + tanklevels(pr,dt1); /* Find new tank levels */ + if (checkrules(pr,dt1)) { + break; /* Stop if rules fire */ + } + dt = MIN(dt, tmax - time->Htime); /* Update time increment */ + dt1 = dt; /* Update actual increment */ + } while (dt > 0); /* Stop if no time left */ + + /* Compute an updated simulation time step (*tstep) */ + /* and return simulation time to its original value */ + *tstep = pr->time_options.Htime - tnow; + pr->time_options.Htime = tnow; +} + + +void addenergy(EN_Project *pr, long hstep) +/* +**------------------------------------------------------------- +** Input: hstep = time step (sec) +** Output: none +** Purpose: accumulates pump energy usage +**------------------------------------------------------------- +*/ +{ + int i,j,k; + long m,n; + double c0,c, /* Energy cost (cost/kwh) */ + f0, /* Energy cost factor */ + dt, /* Time interval (hr) */ + e, /* Pump efficiency (fraction) */ + q, /* Pump flow (cfs) */ + p, /* Pump energy (kw) */ + psum = 0.0; /* Total energy (kw) */ + + EN_Network *net = &pr->network; + hydraulics_t *hyd = &pr->hydraulics; + + /* Determine current time interval in hours */ + if (pr->time_options.Dur == 0) { + dt = 1.0; + } + else if (pr->time_options.Htime < pr->time_options.Dur) { + dt = (double) hstep / 3600.0; + } + else { + dt = 0.0; + } + + if (dt == 0.0) { + return; + } + n = (pr->time_options.Htime + pr->time_options.Pstart) / pr->time_options.Pstep; + + /* Compute default energy cost at current time */ + c0 = hyd->Ecost; + f0 = 1.0; + if (hyd->Epat > 0) + { + m = n % (long)net->Pattern[hyd->Epat].Length; + f0 = net->Pattern[hyd->Epat].F[m]; + } + + /* Examine each pump */ + for (j=1; j <= net->Npumps; j++) + { + Spump *pump = &net->Pump[j]; + /* Skip closed pumps */ + k = pump->Link; + if (hyd->LinkStatus[k] <= CLOSED) { + continue; + } + q = MAX(QZERO, ABS(hyd->LinkFlows[k])); + + /* Find pump-specific energy cost */ + if (pump->Ecost > 0.0) { + c = pump->Ecost; + } + else { + c = c0; + } + + if ( (i = pump->Epat) > 0) { + m = n % (long)net->Pattern[i].Length; + c *= net->Pattern[i].F[m]; + } + else { + c *= f0; + } + + /* Find pump energy & efficiency */ + getenergy(pr,k,&p,&e); + psum += p; + + /* Update pump's cumulative statistics */ + pump->Energy[0] += dt; /* Time on-line */ + pump->Energy[1] += e*dt; /* Effic.-hrs */ + pump->Energy[2] += p/q*dt; /* kw/cfs-hrs */ + pump->Energy[3] += p*dt; /* kw-hrs */ + pump->Energy[4] = MAX(pump->Energy[4],p); + pump->Energy[5] += c*p*dt; /* cost-hrs. */ + } + + /* Update maximum kw value */ + hyd->Emax = MAX(hyd->Emax,psum); +} /* End of pumpenergy */ + + +void getenergy(EN_Project *pr, int k, double *kw, double *eff) +/* +**---------------------------------------------------------------- +** Input: k = link index +** Output: *kw = kwatt energy used +** *eff = efficiency (pumps only) +** Purpose: computes flow energy associated with link k +**---------------------------------------------------------------- +*/ +{ + int i,j; + double dh, q, e; + double q4eff; //q4eff=flow at nominal speed to compute efficiency + Scurve *curve; + + EN_Network *net = &pr->network; + hydraulics_t *hyd = &pr->hydraulics; + Slink *link = &net->Link[k]; + +/*** Updated 6/24/02 ***/ + /* No energy if link is closed */ + if (hyd->LinkStatus[k] <= CLOSED) + { + *kw = 0.0; + *eff = 0.0; + return; + } +/*** End of update ***/ + + /* Determine flow and head difference */ + q = ABS(hyd->LinkFlows[k]); + dh = ABS(hyd->NodeHead[link->N1] - hyd->NodeHead[link->N2]); + + /* For pumps, find effic. at current flow */ + if (link->Type == EN_PUMP) + { + j = findpump(net,k); + e = hyd->Epump; + if ( (i = net->Pump[j].Ecurve) > 0) + { + q4eff = q / hyd->LinkSetting[k]; + curve = &net->Curve[i]; + e = interp(curve->Npts,curve->X, curve->Y, q4eff * pr->Ucf[FLOW]); + } + e = MIN(e, 100.0); + e = MAX(e, 1.0); + e /= 100.0; + } + else e = 1.0; + + /* Compute energy */ + *kw = dh * q * hyd->SpGrav / 8.814 / e * KWperHP; + *eff = e; +} + + +void tanklevels(EN_Project *pr, long tstep) +/* +**---------------------------------------------------------------- +** Input: tstep = current time step +** Output: none +** Purpose: computes new water levels in tanks after current +** time step +**---------------------------------------------------------------- +*/ +{ + int i,n; + double dv; + EN_Network *net = &pr->network; + hydraulics_t *hyd = &pr->hydraulics; + + for (i=1; i<=net->Ntanks; i++) + { + Stank *tank = &net->Tank[i]; + /* Skip reservoirs */ + if (tank->A == 0.0) { + continue; + } + + /* Update the tank's volume & water elevation */ + n = tank->Node; + dv = hyd->NodeDemand[n] * tstep; + tank->V += dv; + + /*** Updated 6/24/02 ***/ + /* Check if tank full/empty within next second */ + if (tank->V + hyd->NodeDemand[n] >= tank->Vmax) { + tank->V = tank->Vmax; + } + else if (tank->V - hyd->NodeDemand[n] <= tank->Vmin) { + tank->V = tank->Vmin; + } + hyd->NodeHead[n] = tankgrade(pr, i, tank->V); + } +} /* End of tanklevels */ + + +double tankvolume(EN_Project *pr, int i, double h) +/* +**-------------------------------------------------------------------- +** Input: i = tank index +** h = water elevation in tank +** Output: returns water volume in tank +** Purpose: finds water volume in tank i corresponding to elev. h. +**-------------------------------------------------------------------- +*/ +{ + int j; + EN_Network *net = &pr->network; + Stank *tank = &net->Tank[i]; + /* Use level*area if no volume curve */ + j = tank->Vcurve; + if (j == 0) { + return(tank->Vmin + (h - tank->Hmin) * tank->A); + } + + /* If curve exists, interpolate on h to find volume v */ + /* remembering that volume curve is in original units.*/ + else { + Scurve *curve = &net->Curve[j]; + return(interp(curve->Npts, curve->X, curve->Y, (h - net->Node[tank->Node].El) * pr->Ucf[HEAD]) / pr->Ucf[VOLUME]); + } + +} /* End of tankvolume */ + + +double tankgrade(EN_Project *pr, int i, double v) +/* +**------------------------------------------------------------------- +** Input: i = tank index +** v = volume in tank +** Output: returns water level in tank +** Purpose: finds water level in tank i corresponding to volume v. +**------------------------------------------------------------------- +*/ +{ + int j; + EN_Network *net = &pr->network; + Stank *tank = &net->Tank[i]; + /* Use area if no volume curve */ + j = tank->Vcurve; + if (j == 0) { + return(tank->Hmin + (v - tank->Vmin) / tank->A); + } + + /* If curve exists, interpolate on volume (originally the Y-variable */ + /* but used here as the X-variable) to find new level above bottom. */ + /* Remember that volume curve is stored in original units. */ + else { + Scurve *curve = &net->Curve[j]; + return(net->Node[tank->Node].El + interp(curve->Npts, curve->Y, curve->X, v * pr->Ucf[VOLUME]) / pr->Ucf[HEAD]); + } + +} /* End of tankgrade */ + + +int netsolve(EN_Project *pr, int *iter, double *relerr) +/* +**------------------------------------------------------------------- +** Input: none +** Output: *iter = # of iterations to reach solution +** *relerr = convergence error in solution +** returns error code +** Purpose: solves network nodal equations for heads and flows +** using Todini's Gradient algorithm +** +*** Updated 9/7/00 *** +*** Updated 2.00.11 *** +*** Updated 2.00.12 *** +** Notes: Status checks on CVs, pumps and pipes to tanks are made +** every hyd->CheckFreq iteration, up until MaxCheck iterations +** are reached. Status checks on control valves are made +** every iteration if DampLimit = 0 or only when the +** convergence error is at or below DampLimit. If DampLimit +** is > 0 then future computed flow changes are only 60% of +** their full value. A complete status check on all links +** is made when convergence is achieved. If convergence is +** not achieved in hyd->MaxIter trials and hyd->ExtraIter > 0 then +** another hyd->ExtraIter trials are made with no status changes +** made to any links and a warning message is generated. +** +** This procedure calls linsolve() which appears in SMATRIX.C. +**------------------------------------------------------------------- +*/ +{ + int i; /* Node index */ + int errcode = 0; /* Node causing solution error */ + int nextcheck; /* Next status check trial */ + int maxtrials; /* Max. trials for convergence */ + double newerr; /* New convergence error */ + int valveChange; /* Valve status change flag */ + int statChange; + + EN_Network *net = &pr->network; + hydraulics_t *hyd = &pr->hydraulics; + solver_t *sol = &hyd->solver; + report_options_t *rep = &pr->report; + + /* Initialize status checking & relaxation factor */ + nextcheck = hyd->CheckFreq; + hyd->RelaxFactor = 1.0; + + /* Repeat iterations until convergence or trial limit is exceeded. */ + /* (hyd->ExtraIter used to increase trials in case of status cycling.) */ + if (pr->report.Statflag == FULL) { + writerelerr(pr,0,0); + } + maxtrials = hyd->MaxIter; + if (hyd->ExtraIter > 0) { + maxtrials += hyd->ExtraIter; + } + *iter = 1; + while (*iter <= maxtrials) { + /* + ** Compute coefficient matrices A & F and solve A*H = F + ** where H = heads, A = Jacobian coeffs. derived from + ** head loss gradients, & F = flow correction terms. + ** Solution for H is returned in F from call to linsolve(). + */ + newcoeffs(pr); + errcode = linsolve(&hyd->solver,net->Njuncs); + + /* Take action depending on error code */ + if (errcode < 0) { + break; /* Memory allocation problem */ + } + if (errcode > 0) { /* Ill-conditioning problem */ + /* If control valve causing problem, fix its status & continue, */ + /* otherwise end the iterations with no solution. */ + if (badvalve(pr,sol->Order[errcode])) { + continue; + } + else break; + } + + /* Update current solution. */ + /* (Row[i] = row of solution matrix corresponding to node i). */ + for (i=1; i<=net->Njuncs; i++) { + hyd->NodeHead[i] = sol->F[sol->Row[i]]; /* Update heads */ + } + newerr = newflows(pr); /* Update flows */ + *relerr = newerr; + + /* Write convergence error to status report if called for */ + if (rep->Statflag == FULL) { + writerelerr(pr, *iter,*relerr); + } + + /* Apply solution damping & check for change in valve status */ + hyd->RelaxFactor = 1.0; + valveChange = FALSE; + if ( hyd->DampLimit > 0.0 ) { + if( *relerr <= hyd->DampLimit ) { + hyd->RelaxFactor = 0.6; + valveChange = valvestatus(pr); + } + } + else { + valveChange = valvestatus(pr); + } + + /* Check for convergence */ + if (*relerr <= hyd->Hacc) { + /* We have convergence. Quit if we are into extra iterations. */ + if (*iter > hyd->MaxIter) { + break; + } + + /* Quit if no status changes occur. */ + statChange = FALSE; + if (valveChange) { + statChange = TRUE; + } + if (linkstatus(pr)) { + statChange = TRUE; + } + if (pswitch(pr)) { + statChange = TRUE; + } + if (!statChange) { + break; + } + + /* We have a status change so continue the iterations */ + nextcheck = *iter + hyd->CheckFreq; + } + + /* No convergence yet. See if its time for a periodic status */ + /* check on pumps, CV's, and pipes connected to tanks. */ + else if (*iter <= hyd->MaxCheck && *iter == nextcheck) + { + linkstatus(pr); + nextcheck += hyd->CheckFreq; + } + (*iter)++; + } + + /* Iterations ended. Report any errors. */ + if (errcode < 0) errcode = 101; /* Memory allocation error */ + else if (errcode > 0) + { + writehyderr(pr, sol->Order[errcode]); /* Ill-conditioned eqns. error */ + errcode = 110; + } + + /* Add any emitter flows to junction demands */ + for (i=1; i<=net->Njuncs; i++) { + hyd->NodeDemand[i] += hyd->EmitterFlows[i]; + } + return(errcode); +} /* End of netsolve */ + + +int badvalve(EN_Project *pr, int n) +/* + **----------------------------------------------------------------- +** Input: n = node index +** Output: returns 1 if node n belongs to an active control valve, +** 0 otherwise +** Purpose: determines if a node belongs to an active control valve +** whose setting causes an inconsistent set of eqns. If so, +** the valve status is fixed open and a warning condition +** is generated. +**----------------------------------------------------------------- +*/ +{ + int i,k,n1,n2; + Slink *link; + EN_Network *net = &pr->network; + hydraulics_t *hyd = &pr->hydraulics; + report_options_t *rep = &pr->report; + time_options_t *time = &pr->time_options; + + for (i=1; i <= net->Nvalves; i++) { + k = net->Valve[i].Link; + link = &net->Link[k]; + n1 = link->N1; + n2 = link->N2; + if (n == n1 || n == n2) { + EN_LinkType t = link->Type; + if (t == EN_PRV || t == EN_PSV || t == EN_FCV) { + if (hyd->LinkStatus[k] == ACTIVE) { + if (rep->Statflag == FULL) { + sprintf(pr->Msg,FMT61,clocktime(rep->Atime,time->Htime),link->ID); + writeline(pr, pr->Msg); + } + if (link->Type == EN_FCV) { + hyd->LinkStatus[k] = XFCV; + } + else { + hyd->LinkStatus[k] = XPRESSURE; + } + return(1); + } + } + return(0); + } + } + return(0); +} + + +int valvestatus(EN_Project *pr) +/* +**----------------------------------------------------------------- +** Input: none +** Output: returns 1 if any pressure or flow control valve +** changes status, 0 otherwise +** Purpose: updates status for PRVs & PSVs whose status +** is not fixed to OPEN/CLOSED +**----------------------------------------------------------------- +*/ +{ + int change = FALSE, /* Status change flag */ + i,k, /* Valve & link indexes */ + n1,n2; /* Start & end nodes */ + StatType status; /* Valve status settings */ + double hset; /* Valve head setting */ + Slink *link; + + EN_Network *net = &pr->network; + hydraulics_t *hyd = &pr->hydraulics; + report_options_t *rep = &pr->report; + + for (i=1; i <= net->Nvalves; i++) /* Examine each valve */ + { + k = net->Valve[i].Link; /* Link index of valve */ + link = &net->Link[k]; + if (hyd->LinkSetting[k] == MISSING) { + continue; /* Valve status fixed */ + } + n1 = link->N1; /* Start & end nodes */ + n2 = link->N2; + status = hyd->LinkStatus[k]; /* Save current status */ + +// if (s != CLOSED /* No change if flow is */ +// && ABS(hyd->LinkFlows[k]) < hyd->Qtol) continue; /* negligible. */ + + switch (link->Type) /* Evaluate new status: */ + { + case EN_PRV: + hset = net->Node[n2].El + hyd->LinkSetting[k]; + hyd->LinkStatus[k] = prvstatus(pr,k,status,hset,hyd->NodeHead[n1],hyd->NodeHead[n2]); + break; + case EN_PSV: + hset = net->Node[n1].El + hyd->LinkSetting[k]; + hyd->LinkStatus[k] = psvstatus(pr,k,status,hset,hyd->NodeHead[n1],hyd->NodeHead[n2]); + break; + +//// FCV status checks moved back into the linkstatus() function //// +// case FCV: S[k] = fcvstatus(k,s,hyd->NodeHead[n1],hyd->NodeHead[n2]); +// break; + + default: + continue; + } + +/*** Updated 9/7/00 ***/ + /* Do not reset flow in valve if its status changes. */ + /* This strategy improves convergence. */ + + /* Check for status change */ + if (status != hyd->LinkStatus[k]) + { + if (rep->Statflag == FULL) { + writestatchange(pr, k,status,hyd->LinkStatus[k]); + } + change = TRUE; + } + } + return(change); +} /* End of valvestatus() */ + + +/*** Updated 9/7/00 ***/ +int linkstatus(EN_Project *pr) +/* +**-------------------------------------------------------------- +** Input: none +** Output: returns 1 if any link changes status, 0 otherwise +** Purpose: determines new status for pumps, CVs, FCVs & pipes +** to tanks. +**-------------------------------------------------------------- +*/ +{ + int change = FALSE, /* Status change flag */ + k, /* Link index */ + n1, /* Start node index */ + n2; /* End node index */ + double dh; /* Head difference */ + StatType status; /* Current status */ + + EN_Network *net = &pr->network; + hydraulics_t *hyd = &pr->hydraulics; + report_options_t *rep = &pr->report; + + /* Examine each Slink */ + for (k=1; k <= net->Nlinks; k++) + { + Slink *link = &net->Link[k]; + n1 = link->N1; + n2 = link->N2; + dh = hyd->NodeHead[n1] - hyd->NodeHead[n2]; + + /* Re-open temporarily closed links (status = XHEAD or TEMPCLOSED) */ + status = hyd->LinkStatus[k]; + if (status == XHEAD || status == TEMPCLOSED) { + hyd->LinkStatus[k] = OPEN; + } + + /* Check for status changes in CVs and pumps */ + if (link->Type == EN_CVPIPE) { + hyd->LinkStatus[k] = cvstatus(pr,hyd->LinkStatus[k],dh,hyd->LinkFlows[k]); + } + if (link->Type == EN_PUMP && hyd->LinkStatus[k] >= OPEN && hyd->LinkSetting[k] > 0.0) { + hyd->LinkStatus[k] = pumpstatus(pr,k,-dh); + } + + /* Check for status changes in non-fixed FCVs */ + if (link->Type == EN_FCV && hyd->LinkSetting[k] != MISSING) { // + hyd->LinkStatus[k] = fcvstatus(pr,k,status,hyd->NodeHead[n1],hyd->NodeHead[n2]); // + } + + /* Check for flow into (out of) full (empty) tanks */ + if (n1 > net->Njuncs || n2 > net->Njuncs) { + tankstatus(pr,k,n1,n2); + } + + /* Note change in link status; do not revise link flow */ + if (status != hyd->LinkStatus[k]) { + change = TRUE; + if (rep->Statflag == FULL) { + writestatchange(pr,k,status,hyd->LinkStatus[k]); + } + + //if (S[k] <= CLOSED) hyd->LinkFlows[k] = QZERO; + //else setlinkflow(k, dh); + } + } + return(change); +} /* End of linkstatus */ + + +StatType cvstatus(EN_Project *pr, StatType s, double dh, double q) +/* +**-------------------------------------------------- +** Input: s = current status +** dh = headloss +** q = flow +** Output: returns new link status +** Purpose: updates status of a check valve. +**-------------------------------------------------- +*/ +{ + hydraulics_t *hyd = &pr->hydraulics; + + /* Prevent reverse flow through CVs */ + if (ABS(dh) > hyd->Htol) + { + if (dh < -hyd->Htol) { + return(CLOSED); + } + else if (q < -hyd->Qtol) { + return(CLOSED); + } + else { + return(OPEN); + } + } + else + { + if (q < -hyd->Qtol) { + return(CLOSED); + } + else { + return(s); + } + } +} + + +StatType pumpstatus(EN_Project *pr, int k, double dh) +/* +**-------------------------------------------------- +** Input: k = link index +** dh = head gain +** Output: returns new pump status +** Purpose: updates status of an open pump. +**-------------------------------------------------- +*/ +{ + int p; + double hmax; + hydraulics_t *hyd = &pr->hydraulics; + EN_Network *net = &pr->network; + + /* Prevent reverse flow through pump */ + p = findpump(net,k); + if (net->Pump[p].Ptype == CONST_HP) { + hmax = BIG; + } + else { + hmax = SQR(hyd->LinkSetting[k]) * net->Pump[p].Hmax; + } + if (dh > hmax + hyd->Htol) { + return(XHEAD); + } + +/*** Flow higher than pump curve no longer results in a status change ***/ + /* Check if pump cannot deliver flow */ + //if (hyd->LinkFlows[k] > K[k]*Pump[p].Qmax + hyd->Qtol) return(XFLOW); + return(OPEN); +} + + +StatType prvstatus(EN_Project *pr, int k, StatType s, double hset, double h1, double h2) +/* +**----------------------------------------------------------- +** Input: k = link index +** s = current status +** hset = valve head setting +** h1 = head at upstream node +** h2 = head at downstream node +** Output: returns new valve status +** Purpose: updates status of a pressure reducing valve. +**----------------------------------------------------------- +*/ +{ + StatType status; /* New valve status */ + double hml; /* Minor headloss */ + hydraulics_t *hyd = &pr->hydraulics; + + double htol = hyd->Htol; + Slink *link = &pr->network.Link[k]; + + status = s; + if (hyd->LinkSetting[k] == MISSING) + return(status); /* Status fixed by user */ + hml = link->Km*SQR(hyd->LinkFlows[k]); /* Head loss when open */ + + /*** Status rules below have changed. ***/ + + switch (s) + { + case ACTIVE: + if (hyd->LinkFlows[k] < -hyd->Qtol) { + status = CLOSED; + } + else if (h1-hml < hset-htol) { + status = OPEN; + } + else { + status = ACTIVE; + } + break; + case OPEN: + if (hyd->LinkFlows[k] < -hyd->Qtol) { + status = CLOSED; + } + else if (h2 >= hset+htol) { + status = ACTIVE; + } + else { + status = OPEN; + } + break; + case CLOSED: + if ( h1 >= hset+htol && h2 < hset-htol) { + status = ACTIVE; + } + else if (h1 < hset-htol && h1 > h2+htol) { + status = OPEN; + } + else { + status = CLOSED; + } + break; + case XPRESSURE: + if (hyd->LinkFlows[k] < -hyd->Qtol) { + status = CLOSED; + } + break; + default: + break; + } + return(status); +} + + +StatType psvstatus(EN_Project *pr, int k, StatType s, double hset, double h1, double h2) +/* +**----------------------------------------------------------- +** Input: k = link index +** s = current status +** hset = valve head setting +** h1 = head at upstream node +** h2 = head at downstream node +** Output: returns new valve status +** Purpose: updates status of a pressure sustaining valve. +**----------------------------------------------------------- +*/ +{ + StatType status; /* New valve status */ + double hml; /* Minor headloss */ + hydraulics_t *hyd = &pr->hydraulics; + + double htol = hyd->Htol; + + Slink *link = &pr->network.Link[k]; + + status = s; + if (hyd->LinkSetting[k] == MISSING) { + return(status); /* Status fixed by user */ + } + hml = link->Km*SQR(hyd->LinkFlows[k]); /* Head loss when open */ + + /*** Status rules below have changed. ***/ + + switch (s) + { + case ACTIVE: + if (hyd->LinkFlows[k] < -hyd->Qtol) { + status = CLOSED; + } + else if (h2+hml > hset+htol) { + status = OPEN; + } + else { + status = ACTIVE; + } + break; + case OPEN: + if (hyd->LinkFlows[k] < -hyd->Qtol) { + status = CLOSED; + } + else if (h1 < hset-htol) { + status = ACTIVE; + } + else { + status = OPEN; + } + break; + case CLOSED: + if (h2 > hset+htol && h1 > h2+htol) { + status = OPEN; + } + else if (h1 >= hset+htol && h1 > h2+htol) { + status = ACTIVE; + } + else { + status = CLOSED; + } + break; + case XPRESSURE: + if (hyd->LinkFlows[k] < -hyd->Qtol) { + status = CLOSED; + } + break; + default: + break; + } + return(status); +} + + +StatType fcvstatus(EN_Project *pr, int k, StatType s, double h1, double h2) +/* +**----------------------------------------------------------- +** Input: k = link index +** s = current status +** h1 = head at upstream node +** h2 = head at downstream node +** Output: returns new valve status +** Purpose: updates status of a flow control valve. +** +** Valve status changes to XFCV if flow reversal. +** If current status is XFCV and current flow is +** above setting, then valve becomes active. +** If current status is XFCV, and current flow +** positive but still below valve setting, then +** status remains same. +**----------------------------------------------------------- +*/ +{ + StatType status; /* New valve status */ + hydraulics_t *hyd = &pr->hydraulics; + + status = s; + if (h1 - h2 < -hyd->Htol) { + status = XFCV; + } + else if ( hyd->LinkFlows[k] < -hyd->Qtol ) { + status = XFCV; + } + else if (s == XFCV && hyd->LinkFlows[k] >= hyd->LinkSetting[k]) { + status = ACTIVE; + } + return(status); +} + + +/*** Updated 9/7/00 ***/ +/*** Updated 11/19/01 ***/ +void tankstatus(EN_Project *pr, int k, int n1, int n2) +/* +**---------------------------------------------------------------- +** Input: k = link index +** n1 = start node of link +** n2 = end node of link +** Output: none +** Purpose: closes link flowing into full or out of empty tank +**---------------------------------------------------------------- +*/ +{ + int i,n; + double h,q; + Stank *tank; + + hydraulics_t *hyd = &pr->hydraulics; + EN_Network *net = &pr->network; + Slink *link = &net->Link[k]; + + /* Make node n1 be the tank */ + q = hyd->LinkFlows[k]; + i = n1 - net->Njuncs; + if (i <= 0) { + i = n2 - net->Njuncs; + if (i <= 0) { + return; + } + n = n1; + n1 = n2; + n2 = n; + q = -q; + } + h = hyd->NodeHead[n1] - hyd->NodeHead[n2]; + tank = &net->Tank[i]; + /* Skip reservoirs & closed links */ + if (tank->A == 0.0 || hyd->LinkStatus[k] <= CLOSED) { + return; + } + + /* If tank full, then prevent flow into it */ + if (hyd->NodeHead[n1] >= tank->Hmax - hyd->Htol) + { + + /* Case 1: Link is a pump discharging into tank */ + if ( link->Type == EN_PUMP ) { + if (link->N2 == n1) + hyd->LinkStatus[k] = TEMPCLOSED; + } + + /* Case 2: Downstream head > tank head */ + /* (i.e., an open outflow check valve would close) */ + else if (cvstatus(pr, OPEN, h, q) == CLOSED) { + hyd->LinkStatus[k] = TEMPCLOSED; + } + } + + /* If tank empty, then prevent flow out of it */ + if (hyd->NodeHead[n1] <= tank->Hmin + hyd->Htol) { + + /* Case 1: Link is a pump discharging from tank */ + if ( link->Type == EN_PUMP) { + if (link->N1 == n1) { + hyd->LinkStatus[k] = TEMPCLOSED; + } + } + + /* Case 2: Tank head > downstream head */ + /* (i.e., a closed outflow check valve would open) */ + else if (cvstatus(pr, CLOSED, h, q) == OPEN) { + hyd->LinkStatus[k] = TEMPCLOSED; + } + } +} /* End of tankstatus */ + + +int pswitch(EN_Project *pr) +/* +**-------------------------------------------------------------- +** Input: none +** Output: returns 1 if status of any link changes, 0 if not +** Purpose: adjusts settings of links controlled by junction +** pressures after a hydraulic solution is found +**-------------------------------------------------------------- +*/ +{ + int i, /* Control statement index */ + k, /* Link being controlled */ + n, /* Node controlling Slink */ + reset, /* Flag on control conditions */ + change, /* Flag for status or setting change */ + anychange = 0; /* Flag for 1 or more changes */ + char s; /* Current link status */ + + EN_Network *net = &pr->network; + hydraulics_t *hyd = &pr->hydraulics; + report_options_t *rep = &pr->report; + + /* Check each control statement */ + for (i=1; i <= net->Ncontrols; i++) + { + reset = 0; + if ( (k = net->Control[i].Link) <= 0) { + continue; + } + + /* Determine if control based on a junction, not a tank */ + if ( (n = net->Control[i].Node) > 0 && n <= net->Njuncs) { + /* Determine if control conditions are satisfied */ + if (net->Control[i].Type == LOWLEVEL + && hyd->NodeHead[n] <= net->Control[i].Grade + hyd->Htol ) { + reset = 1; + } + if (net->Control[i].Type == HILEVEL + && hyd->NodeHead[n] >= net->Control[i].Grade - hyd->Htol ) { + reset = 1; + } + } + + /* Determine if control forces a status or setting change */ + if (reset == 1) + { + Slink *link = &net->Link[k]; + change = 0; + s = hyd->LinkStatus[k]; + if (link->Type == EN_PIPE) { + if (s != net->Control[i].Status) { + change = 1; + } + } + if (link->Type == EN_PUMP) { + if (hyd->LinkSetting[k] != net->Control[i].Setting) { + change = 1; + } + } + if (link->Type >= EN_PRV) { + if (hyd->LinkSetting[k] != net->Control[i].Setting) { + change = 1; + } + else if (hyd->LinkSetting[k] == MISSING && s != net->Control[i].Status) { + change = 1; + } + } + + /* If a change occurs, update status & setting */ + if (change) { + hyd->LinkStatus[k] = net->Control[i].Status; + if (link->Type > EN_PIPE) { + hyd->LinkSetting[k] = net->Control[i].Setting; + } + if (rep->Statflag == FULL) { + writestatchange(pr, k,s,hyd->LinkStatus[k]); + } + + /* Re-set flow if status has changed */ +// if (S[k] != s) initlinkflow(k, S[k], K[k]); + anychange = 1; + } + } + } + return(anychange); +} /* End of pswitch */ + + +double newflows(EN_Project *pr) +/* +**---------------------------------------------------------------- +** Input: none +** Output: returns solution convergence error +** Purpose: updates link flows after new nodal heads computed +**---------------------------------------------------------------- +*/ +{ + double dh, /* Link head loss */ + dq; /* Link flow change */ + double dqsum, /* Network flow change */ + qsum; /* Network total flow */ + int k, n, n1, n2; + + EN_Network *net = &pr->network; + hydraulics_t *hyd = &pr->hydraulics; + solver_t *s = &hyd->solver; + + /* Initialize net inflows (i.e., demands) at tanks */ + for (n = net->Njuncs + 1; n <= net->Nnodes; n++) { + hyd->NodeDemand[n] = 0.0; + } + + /* Initialize sum of flows & corrections */ + qsum = 0.0; + dqsum = 0.0; + + /* Update flows in all links */ + for (k=1; k<=net->Nlinks; k++) + { + Slink *link = &net->Link[k]; + /* + ** Apply flow update formula: + ** dq = Y - P*(new head loss) + ** P = 1/(dh/dq) + ** Y = P*(head loss based on current flow) + ** where P & Y were computed in newcoeffs(). + */ + + n1 = link->N1; + n2 = link->N2; + dh = hyd->NodeHead[n1] - hyd->NodeHead[n2]; + dq = s->Y[k] - s->P[k] * dh; + + /* Adjust flow change by the relaxation factor */ + dq *= hyd->RelaxFactor; + + /* Prevent flow in constant HP pumps from going negative */ + if (link->Type == EN_PUMP) { + n = findpump(net,k); + if (net->Pump[n].Ptype == CONST_HP && dq > hyd->LinkFlows[k]) { + dq = hyd->LinkFlows[k]/2.0; + } + } + hyd->LinkFlows[k] -= dq; + + /* Update sum of absolute flows & flow corrections */ + qsum += ABS(hyd->LinkFlows[k]); + dqsum += ABS(dq); + + /* Update net flows to tanks */ + if ( hyd->LinkStatus[k] > CLOSED ) + { + if (n1 > net->Njuncs) { + hyd->NodeDemand[n1] -= hyd->LinkFlows[k]; + } + if (n2 > net->Njuncs) { + hyd->NodeDemand[n2] += hyd->LinkFlows[k]; + } + } + + } + + /* Update emitter flows */ + for (k=1; k<=net->Njuncs; k++) + { + if (net->Node[k].Ke == 0.0) { + continue; + } + dq = emitflowchange(pr,k); + hyd->EmitterFlows[k] -= dq; + qsum += ABS(hyd->EmitterFlows[k]); + dqsum += ABS(dq); + } + + /* Return ratio of total flow corrections to total flow */ + if (qsum > hyd->Hacc) { + return(dqsum/qsum); + } + else { + return(dqsum); + } +} /* End of newflows */ + + +void newcoeffs(EN_Project *pr) +/* +**-------------------------------------------------------------- +** Input: none +** Output: none +** Purpose: computes coefficients of linearized network eqns. +**-------------------------------------------------------------- +*/ +{ + hydraulics_t *hyd = &pr->hydraulics; + solver_t *s = &hyd->solver; + EN_Network *n = &pr->network; + + memset(s->Aii,0,(n->Nnodes+1)*sizeof(double)); /* Reset coeffs. to 0 */ + memset(s->Aij,0,(hyd->Ncoeffs+1)*sizeof(double)); + memset(s->F,0,(n->Nnodes+1)*sizeof(double)); + memset(hyd->X_tmp,0,(n->Nnodes+1)*sizeof(double)); + memset(s->P,0,(n->Nlinks+1)*sizeof(double)); + memset(s->Y,0,(n->Nlinks+1)*sizeof(double)); + linkcoeffs(pr); /* Compute link coeffs. */ + emittercoeffs(pr); /* Compute emitter coeffs.*/ + nodecoeffs(pr); /* Compute node coeffs. */ + valvecoeffs(pr); /* Compute valve coeffs. */ +} /* End of newcoeffs */ + + +void linkcoeffs(EN_Project *pr) +/* +**-------------------------------------------------------------- +** Input: none +** Output: none +** Purpose: computes solution matrix coefficients for links +**-------------------------------------------------------------- +*/ +{ + int k,n1,n2; + + EN_Network *net = &pr->network; + hydraulics_t *hyd = &pr->hydraulics; + solver_t *s = &hyd->solver; + EN_Network *n = &pr->network; + + /* Examine each link of network */ + for (k=1; k<=net->Nlinks; k++) + { + Slink *link = &net->Link[k]; + n1 = link->N1; /* Start node of Slink */ + n2 = link->N2; /* End node of link */ + + /* Compute P[k] = 1 / (dh/dQ) and Y[k] = h * P[k] */ + /* for each link k (where h = link head loss). */ + /* FCVs, PRVs, and PSVs with non-fixed status */ + /* are analyzed later. */ + + switch (link->Type) + { + case EN_CVPIPE: + case EN_PIPE: + pipecoeff(pr, k); + break; + case EN_PUMP: + pumpcoeff(pr, k); + break; + case EN_PBV: + pbvcoeff(pr, k); + break; + case EN_TCV: + tcvcoeff(pr, k); + break; + case EN_GPV: + gpvcoeff(pr, k); + break; + case EN_FCV: + case EN_PRV: + case EN_PSV: /* If valve status fixed then treat as pipe, otherwise ignore the valve for now. */ + if (hyd->LinkSetting[k] == MISSING) { + valvecoeff(pr, k); //pipecoeff(k); + } + else { + continue; + } + break; + default: + continue; + } + + /* Update net nodal inflows (X), solution matrix (A) and RHS array (F) */ + /* (Use covention that flow out of node is (-), flow into node is (+)) */ + hyd->X_tmp[n1] -= hyd->LinkFlows[k]; + hyd->X_tmp[n2] += hyd->LinkFlows[k]; + s->Aij[s->Ndx[k]] -= s->P[k]; /* Off-diagonal coeff. */ + if (n1 <= n->Njuncs) /* Node n1 is junction */ + { + s->Aii[s->Row[n1]] += s->P[k]; /* Diagonal coeff. */ + s->F[s->Row[n1]] += s->Y[k]; /* RHS coeff. */ + } + else { + s->F[s->Row[n2]] += (s->P[k]*hyd->NodeHead[n1]); /* Node n1 is a tank */ + } + if (n2 <= n->Njuncs) { /* Node n2 is junction */ + s->Aii[s->Row[n2]] += s->P[k]; /* Diagonal coeff. */ + s->F[s->Row[n2]] -= s->Y[k]; /* RHS coeff. */ + } + else { + s->F[s->Row[n1]] += (s->P[k] * hyd->NodeHead[n2]); /* Node n2 is a tank */ + } + } +} /* End of linkcoeffs */ + + +void nodecoeffs(EN_Project *pr) +/* +**---------------------------------------------------------------- +** Input: none +** Output: none +** Purpose: completes calculation of nodal flow imbalance (X) +** & flow correction (F) arrays +**---------------------------------------------------------------- +*/ +{ + int i; + hydraulics_t *hyd = &pr->hydraulics; + solver_t *s = &hyd->solver; + EN_Network *n = &pr->network; + + /* For junction nodes, subtract demand flow from net */ + /* flow imbalance & add imbalance to RHS array F. */ + for (i=1; i <= n->Njuncs; i++) + { + hyd->X_tmp[i] -= hyd->NodeDemand[i]; + s->F[s->Row[i]] += hyd->X_tmp[i]; + } +} /* End of nodecoeffs */ + + +void valvecoeffs(EN_Project *pr) +/* +**-------------------------------------------------------------- +** Input: none +** Output: none +** Purpose: computes matrix coeffs. for PRVs, PSVs & FCVs +** whose status is not fixed to OPEN/CLOSED +**-------------------------------------------------------------- +*/ +{ + int i,k,n1,n2; + + hydraulics_t *hyd = &pr->hydraulics; + EN_Network *n = &pr->network; + Slink *link; + Svalve *valve; + + for (i=1; i<=n->Nvalves; i++) /* Examine each valve */ + { + valve = &n->Valve[i]; + k = valve->Link; /* Link index of valve */ + link = &n->Link[k]; + if (hyd->LinkSetting[k] == MISSING) { + continue; /* Valve status fixed */ + } + n1 = link->N1; /* Start & end nodes */ + n2 = link->N2; + switch (link->Type) /* Call valve-specific */ + { /* function */ + case EN_PRV: + prvcoeff(pr, k,n1,n2); + break; + case EN_PSV: + psvcoeff(pr, k,n1,n2); + break; + case EN_FCV: + fcvcoeff(pr, k,n1,n2); + break; + default: continue; + } + } +} /* End of valvecoeffs */ + + +void emittercoeffs(EN_Project *pr) +/* +**-------------------------------------------------------------- +** Input: none +** Output: none +** Purpose: computes matrix coeffs. for emitters +** +** Note: Emitters consist of a fictitious pipe connected to +** a fictitious reservoir whose elevation equals that +** of the junction. The headloss through this pipe is +** Ke*(Flow)^hyd->Qexp, where Ke = emitter headloss coeff. +**-------------------------------------------------------------- +*/ +{ + int i; + double ke; + double p; + double q; + double y; + double z; + + hydraulics_t *hyd = &pr->hydraulics; + solver_t *s = &hyd->solver; + EN_Network *n = &pr->network; + + for (i=1; i <= n->Njuncs; i++) + { + Snode *node = &n->Node[i]; + if (node->Ke == 0.0) { + continue; + } + ke = MAX(CSMALL, node->Ke); + q = hyd->EmitterFlows[i]; + z = ke * pow(ABS(q),hyd->Qexp); + p = hyd->Qexp * z / ABS(q); + if (p < hyd->RQtol) { + p = 1.0 / hyd->RQtol; + } + else { + p = 1.0 / p; + } + y = SGN(q)*z*p; + s->Aii[s->Row[i]] += p; + s->F[s->Row[i]] += y + p * node->El; + hyd->X_tmp[i] -= q; + } +} + + +double emitflowchange(EN_Project *pr, int i) +/* +**-------------------------------------------------------------- +** Input: i = node index +** Output: returns change in flow at an emitter node +** Purpose: computes flow change at an emitter node +**-------------------------------------------------------------- +*/ +{ + double ke, p; + hydraulics_t *hyd = &pr->hydraulics; + EN_Network *n = &pr->network; + Snode *node = &n->Node[i]; + + ke = MAX(CSMALL, node->Ke); + p = hyd->Qexp * ke * pow(ABS(hyd->EmitterFlows[i]),(hyd->Qexp - 1.0)); + if (p < hyd->RQtol) { + p = 1/hyd->RQtol; + } + else { + p = 1.0/p; + } + return(hyd->EmitterFlows[i] / hyd->Qexp - p * (hyd->NodeHead[i] - node->El)); +} + + +void pipecoeff(EN_Project *pr, int k) +/* +**-------------------------------------------------------------- +** Input: k = link index +** Output: none +** Purpose: computes P & Y coefficients for pipe k +** +** P = inverse head loss gradient = 1/(dh/dQ) +** Y = flow correction term = h*P +**-------------------------------------------------------------- +*/ +{ + double hpipe, /* Normal head loss */ + hml, /* Minor head loss */ + ml, /* Minor loss coeff. */ + p, /* q*(dh/dq) */ + q, /* Abs. value of flow */ + r, /* Resistance coeff. */ + r1, /* Total resistance factor */ + f, /* D-W friction factor */ + dfdq; /* Derivative of fric. fact. */ + + hydraulics_t *hyd = &pr->hydraulics; + solver_t *s = &hyd->solver; + Slink *link = &pr->network.Link[k]; + + /* For closed pipe use headloss formula: h = CBIG*q */ + if (hyd->LinkStatus[k] <= CLOSED) + { + s->P[k] = 1.0/CBIG; + s->Y[k] = hyd->LinkFlows[k]; + return; + } + + /* Evaluate headloss coefficients */ + q = ABS(hyd->LinkFlows[k]); /* Absolute flow */ + ml = link->Km; /* Minor loss coeff. */ + r = link->R; /* Resistance coeff. */ + f = 1.0; /* D-W friction factor */ + if (hyd->Formflag == DW) f = DWcoeff(pr,k,&dfdq); + r1 = f*r+ml; + + /* Use large P coefficient for small flow resistance product */ + if (r1*q < hyd->RQtol) + { + s->P[k] = 1.0/hyd->RQtol; + s->Y[k] = hyd->LinkFlows[k]/hyd->Hexp; + return; + } + + /* Compute P and Y coefficients */ + if (hyd->Formflag == DW) /* D-W eqn. */ + { + hpipe = r1*SQR(q); /* Total head loss */ + p = 2.0*r1*q; /* |dh/dQ| */ + /* + dfdq*r*q*q;*/ /* Ignore df/dQ term */ + p = 1.0/p; + s->P[k] = p; + s->Y[k] = SGN(hyd->LinkFlows[k])*hpipe*p; + } + else /* H-W or C-M eqn. */ + { + hpipe = r*pow(q,hyd->Hexp); /* Friction head loss */ + p = hyd->Hexp*hpipe; /* Q*dh(friction)/dQ */ + if (ml > 0.0) + { + hml = ml*q*q; /* Minor head loss */ + p += 2.0*hml; /* Q*dh(Total)/dQ */ + } + else hml = 0.0; + p = hyd->LinkFlows[k]/p; /* 1 / (dh/dQ) */ + s->P[k] = ABS(p); + s->Y[k] = p*(hpipe + hml); + } +} /* End of pipecoeff */ + + +double DWcoeff(EN_Project *pr, int k, double *dfdq) +/* +**-------------------------------------------------------------- +** Input: k = link index +** Output: returns Darcy-Weisbach friction factor +** Purpose: computes Darcy-Weisbach friction factor +** +** Uses interpolating polynomials developed by +** E. Dunlop for transition flow from 2000 < Re < 4000. +** +** df/dq term is ignored as it slows convergence rate. +**-------------------------------------------------------------- +*/ +{ + double q, /* Abs. value of flow */ + f; /* Friction factor */ + double x1,x2,x3,x4, + y1,y2,y3, + fa,fb,r; + double s,w; + + hydraulics_t *hyd = &pr->hydraulics; + Slink *link = &pr->network.Link[k]; + + *dfdq = 0.0; + if (link->Type > EN_PIPE) + return(1.0); /* Only apply to pipes */ + q = ABS(hyd->LinkFlows[k]); + s = hyd->Viscos * link->Diam; + w = q/s; /* w = Re(Pi/4) */ + if (w >= A1) /* Re >= 4000; Colebrook Formula */ + { + y1 = A8/pow(w,0.9); + y2 = link->Kc/(3.7*link->Diam) + y1; + y3 = A9*log(y2); + f = 1.0/SQR(y3); + /* *dfdq = (2.0+AA*y1/(y2*y3))*f; */ /* df/dq */ + } + else if (w > A2) /* Re > 2000; Interpolation formula */ + { + y2 = link->Kc/(3.7*link->Diam) + AB; + y3 = A9*log(y2); + fa = 1.0/SQR(y3); + fb = (2.0+AC/(y2*y3))*fa; + r = w/A2; + x1 = 7.0*fa - fb; + x2 = 0.128 - 17.0*fa + 2.5*fb; + x3 = -0.128 + 13.0*fa - (fb+fb); + x4 = r*(0.032 - 3.0*fa + 0.5*fb); + f = x1 + r*(x2 + r*(x3+x4)); + /* *dfdq = (x1 + x1 + r*(3.0*x2 + r*(4.0*x3 + 5.0*x4))); */ + } + else if (w > A4) /* Laminar flow: Hagen-Poiseuille Formula */ + { + f = A3*s/q; + /* *dfdq = A3*s; */ + } + else + { + f = 8.0; + *dfdq = 0.0; + } + return(f); +} /* End of DWcoeff */ + + +/*** Updated 10/25/00 ***/ +/*** Updated 12/29/00 ***/ +void pumpcoeff(EN_Project *pr, int k) +/* +**-------------------------------------------------------------- +** Input: k = link index +** Output: none +** Purpose: computes P & Y coeffs. for pump in link k +**-------------------------------------------------------------- +*/ +{ + int p; /* Pump index */ + double h0, /* Shutoff head */ + q, /* Abs. value of flow */ + r, /* Flow resistance coeff. */ + n; /* Flow exponent coeff. */ + hydraulics_t *hyd; + solver_t *s; + double setting; + Spump *pump; + + hyd = &pr->hydraulics; + s = &hyd->solver; + + setting = hyd->LinkSetting[k]; + + /* Use high resistance pipe if pump closed or cannot deliver head */ + if (hyd->LinkStatus[k] <= CLOSED || setting == 0.0) { + s->P[k] = 1.0/CBIG; + s->Y[k] = hyd->LinkFlows[k]; + return; + } + + q = ABS(hyd->LinkFlows[k]); + q = MAX(q,TINY); + p = findpump(&pr->network,k); + + pump = &pr->network.Pump[p]; + /* Get pump curve coefficients for custom pump curve. */ + if (pump->Ptype == CUSTOM) + { + /* Find intercept (h0) & slope (r) of pump curve */ + /* line segment which contains speed-adjusted flow. */ + curvecoeff(pr,pump->Hcurve, q/setting, &h0, &r); + + /* Determine head loss coefficients. */ + pump->H0 = -h0; + pump->R = -r; + pump->N = 1.0; + } + + /* Adjust head loss coefficients for pump speed. */ + h0 = SQR(setting) * pump->H0; + n = pump->N; + r = pump->R * pow(setting,2.0-n); + if (n != 1.0) { + r = n * r * pow(q,n-1.0); + } + + /* Compute inverse headloss gradient (P) and flow correction factor (Y) */ + s->P[k] = 1.0/MAX(r,hyd->RQtol); + s->Y[k] = hyd->LinkFlows[k]/n + s->P[k]*h0; +} /* End of pumpcoeff */ + + +/*** Updated 10/25/00 ***/ +/*** Updated 12/29/00 ***/ +void curvecoeff(EN_Project *p, int i, double q, double *h0, double *r) +/* +**------------------------------------------------------------------- +** Input: i = curve index +** q = flow rate +** Output: *h0 = head at zero flow (y-intercept) +** *r = dHead/dFlow (slope) +** Purpose: computes intercept and slope of head v. flow curve +** at current flow. +**------------------------------------------------------------------- +*/ +{ + int k1, k2, npts; + double *x, *y; + Scurve *curve; + + /* Remember that curve is stored in untransformed units */ + q *= p->Ucf[FLOW]; + curve = &p->network.Curve[i]; + x = curve->X; /* x = flow */ + y = curve->Y; /* y = head */ + npts = curve->Npts; + + /* Find linear segment of curve that brackets flow q */ + k2 = 0; + while (k2 < npts && x[k2] < q) + k2++; + + if (k2 == 0) + k2++; + + else if (k2 == npts) + k2--; + + k1 = k2 - 1; + + /* Compute slope and intercept of this segment */ + *r = (y[k2]-y[k1]) / (x[k2]-x[k1]); + *h0 = y[k1] - (*r)*x[k1]; + + /* Convert units */ + *h0 = (*h0) / p->Ucf[HEAD]; + *r = (*r)*p->Ucf[FLOW]/p->Ucf[HEAD]; +} /* End of curvecoeff */ + + +void gpvcoeff(EN_Project *p, int k) +/* +**-------------------------------------------------------------- +** Input: k = link index +** Output: none +** Purpose: computes P & Y coeffs. for general purpose valve +**-------------------------------------------------------------- +*/ +{ + double h0, /* Headloss curve intercept */ + q, /* Abs. value of flow */ + r; /* Flow resistance coeff. */ + + hydraulics_t *hyd = &p->hydraulics; + solver_t *s = &hyd->solver; + +/*** Updated 9/7/00 ***/ + /* Treat as a pipe if valve closed */ + if (hyd->LinkStatus[k] == CLOSED) { + valvecoeff(p,k); //pipecoeff(k); + } + /* Otherwise utilize headloss curve */ + /* whose index is stored in K */ + else { + /* Find slope & intercept of headloss curve. */ + q = ABS(hyd->LinkFlows[k]); + q = MAX(q,TINY); + +/*** Updated 10/25/00 ***/ +/*** Updated 12/29/00 ***/ + curvecoeff(p,(int)ROUND(hyd->LinkSetting[k]),q,&h0,&r); + + /* Compute inverse headloss gradient (P) */ + /* and flow correction factor (Y). */ + s->P[k] = 1.0 / MAX(r,hyd->RQtol); + s->Y[k] = s->P[k]*(h0 + r*q) * SGN(hyd->LinkFlows[k]); + } +} + + +void pbvcoeff(EN_Project *p, int k) +/* +**-------------------------------------------------------------- +** Input: k = link index +** Output: none +** Purpose: computes P & Y coeffs. for pressure breaker valve +**-------------------------------------------------------------- +*/ +{ + + Slink *link = &p->network.Link[k]; + hydraulics_t *hyd = &p->hydraulics; + solver_t *s = &hyd->solver; + + /* If valve fixed OPEN or CLOSED then treat as a pipe */ + if (hyd->LinkSetting[k] == MISSING || hyd->LinkSetting[k] == 0.0) { + valvecoeff(p,k); //pipecoeff(k); + } + + /* If valve is active */ + else { + /* Treat as a pipe if minor loss > valve setting */ + if (link->Km * SQR(hyd->LinkFlows[k]) > hyd->LinkSetting[k]) { + valvecoeff(p,k); //pipecoeff(k); + } + /* Otherwise force headloss across valve to be equal to setting */ + else { + s->P[k] = CBIG; + s->Y[k] = hyd->LinkSetting[k] * CBIG; + } + } +} /* End of pbvcoeff */ + + +void tcvcoeff(EN_Project *p, int k) +/* +**-------------------------------------------------------------- +** Input: k = link index +** Output: none +** Purpose: computes P & Y coeffs. for throttle control valve +**-------------------------------------------------------------- +*/ +{ + double km; + Slink *link = &p->network.Link[k]; + hydraulics_t *hyd = &p->hydraulics; + + /* Save original loss coeff. for open valve */ + km = link->Km; + + /* If valve not fixed OPEN or CLOSED, compute its loss coeff. */ + if (hyd->LinkSetting[k] != MISSING) { + link->Km = 0.02517 * hyd->LinkSetting[k] / (SQR(link->Diam)*SQR(link->Diam)); + } + + /* Then apply usual pipe formulas */ + valvecoeff(p,k); //pipecoeff(k); + + /* Restore original loss coeff. */ + link->Km = km; +} /* End of tcvcoeff */ + + +void prvcoeff(EN_Project *p, int k, int n1, int n2) +/* +**-------------------------------------------------------------- +** Input: k = link index +** n1 = upstream node of valve +** n2 = downstream node of valve +** Output: none +** Purpose: computes solution matrix coeffs. for pressure +** reducing valves +**-------------------------------------------------------------- +*/ +{ + int i,j; /* Rows of solution matrix */ + double hset; /* Valve head setting */ + hydraulics_t *hyd = &p->hydraulics; + solver_t *s = &hyd->solver; + + i = s->Row[n1]; /* Matrix rows of nodes */ + j = s->Row[n2]; + hset = p->network.Node[n2].El + hyd->LinkSetting[k]; /* Valve setting */ + + if (hyd->LinkStatus[k] == ACTIVE) + { + /* + Set coeffs. to force head at downstream + node equal to valve setting & force flow (when updated in + newflows()) equal to flow imbalance at downstream node. + */ + s->P[k] = 0.0; + s->Y[k] = hyd->LinkFlows[k] + hyd->X_tmp[n2]; /* Force flow balance */ + s->F[j] += (hset * CBIG); /* Force head = hset */ + s->Aii[j] += CBIG; /* at downstream node */ + if (hyd->X_tmp[n2] < 0.0) { + s->F[i] += hyd->X_tmp[n2]; + } + return; + } + + /* + For OPEN, CLOSED, or XPRESSURE valve + compute matrix coeffs. using the valvecoeff() function. + */ + valvecoeff(p,k); /*pipecoeff(k);*/ + s->Aij[s->Ndx[k]] -= s->P[k]; + s->Aii[i] += s->P[k]; + s->Aii[j] += s->P[k]; + s->F[i] += (s->Y[k] - hyd->LinkFlows[k]); + s->F[j] -= (s->Y[k] - hyd->LinkFlows[k]); +} /* End of prvcoeff */ + + +void psvcoeff(EN_Project *p, int k, int n1, int n2) +/* +**-------------------------------------------------------------- +** Input: k = link index +** n1 = upstream node of valve +** n2 = downstream node of valve +** Output: none +** Purpose: computes solution matrix coeffs. for pressure +** sustaining valve +**-------------------------------------------------------------- +*/ +{ + int i,j; /* Rows of solution matrix */ + double hset; /* Valve head setting */ + + hydraulics_t *hyd = &p->hydraulics; + solver_t *s = &hyd->solver; + + i = s->Row[n1]; /* Matrix rows of nodes */ + j = s->Row[n2]; + hset = p->network.Node[n1].El + hyd->LinkSetting[k]; /* Valve setting */ + + if (hyd->LinkStatus[k] == ACTIVE) + { + /* + Set coeffs. to force head at upstream + node equal to valve setting & force flow (when updated in + newflows()) equal to flow imbalance at upstream node. + */ + s->P[k] = 0.0; + s->Y[k] = hyd->LinkFlows[k] - hyd->X_tmp[n1]; /* Force flow balance */ + s->F[i] += (hset*CBIG); /* Force head = hset */ + s->Aii[i] += CBIG; /* at upstream node */ + if (hyd->X_tmp[n1] > 0.0) { + s->F[j] += hyd->X_tmp[n1]; + } + return; + } + + /* + For OPEN, CLOSED, or XPRESSURE valve + compute matrix coeffs. using the valvecoeff() function. + */ + valvecoeff(p,k); /*pipecoeff(k);*/ + s->Aij[s->Ndx[k]] -= s->P[k]; + s->Aii[i] += s->P[k]; + s->Aii[j] += s->P[k]; + s->F[i] += (s->Y[k] - hyd->LinkFlows[k]); + s->F[j] -= (s->Y[k] - hyd->LinkFlows[k]); +} /* End of psvcoeff */ + + +void fcvcoeff(EN_Project *p, int k, int n1, int n2) +/* +**-------------------------------------------------------------- +** Input: k = link index +** n1 = upstream node of valve +** n2 = downstream node of valve +** Output: none +** Purpose: computes solution matrix coeffs. for flow control +** valve +**-------------------------------------------------------------- +*/ +{ + int i,j; /* Rows in solution matrix */ + double q; /* Valve flow setting */ + + hydraulics_t *hyd = &p->hydraulics; + solver_t *s = &hyd->solver; + + q = hyd->LinkSetting[k]; + i = hyd->solver.Row[n1]; + j = hyd->solver.Row[n2]; + + /* + If valve active, break network at valve and treat + flow setting as external demand at upstream node + and external supply at downstream node. + */ + if (hyd->LinkStatus[k] == ACTIVE) + { + hyd->X_tmp[n1] -= q; + s->F[i] -= q; + hyd->X_tmp[n2] += q; + s->F[j] += q; + /*P[k] = 0.0;*/ + s->P[k] = 1.0/CBIG; + s->Aij[s->Ndx[k]] -= s->P[k]; + s->Aii[i] += s->P[k]; + s->Aii[j] += s->P[k]; + s->Y[k] = hyd->LinkFlows[k] - q; + } + /* + Otherwise treat valve as an open pipe + */ + else + { + valvecoeff(p,k); //pipecoeff(k); + s->Aij[s->Ndx[k]] -= s->P[k]; + s->Aii[i] += s->P[k]; + s->Aii[j] += s->P[k]; + s->F[i] += (s->Y[k] - hyd->LinkFlows[k]); + s->F[j] -= (s->Y[k] - hyd->LinkFlows[k]); + } +} /* End of fcvcoeff */ + + +/*** New function added. ***/ +void valvecoeff(EN_Project *pr, int k) +/* +**-------------------------------------------------------------- +** Input: k = link index +** Output: none +** Purpose: computes solution matrix coeffs. for a completely +** open, closed, or throttled control valve. +**-------------------------------------------------------------- +*/ +{ + double p; + + EN_Network *n = &pr->network; + hydraulics_t *hyd = &pr->hydraulics; + solver_t *s = &hyd->solver; + + Slink *link = &n->Link[k]; + + double flow = hyd->LinkFlows[k]; + + // Valve is closed. Use a very small matrix coeff. + if (hyd->LinkStatus[k] <= CLOSED) + { + s->P[k] = 1.0/CBIG; + s->Y[k] = flow; + return; + } + + // Account for any minor headloss through the valve + if (link->Km > 0.0) + { + p = 2.0 * link->Km * fabs(flow); + if ( p < hyd->RQtol ) { + p = hyd->RQtol; + } + s->P[k] = 1.0 / p; + s->Y[k] = flow / 2.0; + } + else + { + s->P[k] = 1.0 / hyd->RQtol; + s->Y[k] = flow; + } +} + +/**************** END OF HYDRAUL.C ***************/ + diff --git a/src/inpfile.c b/src/inpfile.c old mode 100755 new mode 100644 index 77ee434..1a2b17d --- a/src/inpfile.c +++ b/src/inpfile.c @@ -1,662 +1,709 @@ -/* -********************************************************************* - -INPFILE.C -- Save Input Function for EPANET Program - -VERSION: 2.00 -DATE: 5/8/00 - 3/1/01 - 11/19/01 - 6/24/02 - 8/15/07 (2.00.11) - 2/14/08 (2.00.12) -AUTHOR: L. Rossman - US EPA - NRMRL - -This module contains the function saveinpfile() which saves the -data describing a piping network to a file in EPANET's text format. - -******************************************************************** -*/ - -#include -#include -#ifndef __APPLE__ -#include -#else -#include -#endif -#include -#include "hash.h" -#include "text.h" -#include "types.h" -#include "funcs.h" -#define EXTERN extern -#include "vars.h" - -/* Defined in enumstxt.h in EPANET.C */ -extern char *LinkTxt[]; -extern char *FormTxt[]; -extern char *StatTxt[]; -extern char *FlowUnitsTxt[]; -extern char *PressUnitsTxt[]; -extern char *ControlTxt[]; -extern char *SourceTxt[]; -extern char *MixTxt[]; -extern char *TstatTxt[]; -extern char *RptFlagTxt[]; -extern char *SectTxt[]; - - -void saveauxdata(FILE *f) //(2.00.12 - LR) -/* ------------------------------------------------------------- - Writes auxilary data from original input file to new file. ------------------------------------------------------------- -*/ -{ - int sect,newsect; - char *tok; - char line[MAXLINE+1]; - char s[MAXLINE+1]; - - sect = -1; - rewind(InFile); - while (fgets(line,MAXLINE,InFile) != NULL) - { - /* Check if line begins with a new section heading */ - strcpy(s,line); - tok = strtok(s,SEPSTR); - if (tok != NULL && *tok == '[') - { - newsect = findmatch(tok,SectTxt); - if (newsect >= 0) - { - sect = newsect; - if (sect == _END) break; - switch(sect) - { - //case _RULES: - case _COORDS: if (Coordflag == FALSE) - { - fprintf(f, "%s", line); - } - break; - case _VERTICES: - case _LABELS: - case _BACKDROP: - case _TAGS: fprintf(f, "%s", line); //(2.00.12 - LR) - } - continue; - } - else continue; - } - - /* Write lines appearing in the section to file */ - switch(sect) - { - //case _RULES: - case _COORDS: if (Coordflag == FALSE) - { - fprintf(f, "%s", line); - } - break; - case _VERTICES: - case _LABELS: - case _BACKDROP: - case _TAGS: fprintf(f, "%s", line); //(2.00.12 - LR) - } - } -} - - -//// This function was heavily modified. //// //(2.00.12 - LR) - -int saveinpfile(char *fname) -/* -------------------------------------------------- - Writes network data to text file. -------------------------------------------------- -*/ -{ - int i,j,n; - int errcode; - double d,kc,ke,km,ucf; - char s[MAXLINE+1], s1[MAXLINE+1], s2[MAXLINE+1]; - Pdemand demand; - Psource source; - FILE *f; - -/* Open the new text file */ - - if ((f = fopen(fname,"wt")) == NULL) return(308); - -/* Write [TITLE] section */ - - fprintf(f,"[TITLE]"); - for (i=0; i<3; i++) - { - if (strlen(Title[i]) > 0) fprintf(f,"\n%s",Title[i]); - } - -/* Write [JUNCTIONS] section */ -/* (Leave demands for [DEMANDS] section) */ - - fprintf(f,"\n\n[JUNCTIONS]"); - for (i=1; i<=Njuncs; i++) - fprintf(f,"\n %-31s %12.4f", Node[i].ID, Node[i].El*Ucf[ELEV]); - -/* Write [RESERVOIRS] section */ - - fprintf(f,"\n\n[RESERVOIRS]"); - for (i=1; i<=Ntanks; i++) - { - if (Tank[i].A == 0.0) - { - n = Tank[i].Node; - sprintf(s," %-31s %12.4f",Node[n].ID, Node[n].El*Ucf[ELEV]); - if ((j = Tank[i].Pat) > 0) - sprintf(s1," %-31s",Pattern[j].ID); - else - strcpy(s1,""); - fprintf(f, "\n%s %s", s,s1); - } - } - -/* Write [TANKS] section */ - - fprintf(f,"\n\n[TANKS]"); - for (i=1; i<=Ntanks; i++) - { - if (Tank[i].A > 0.0) - { - n = Tank[i].Node; - sprintf(s," %-31s %12.4f %12.4f %12.4f %12.4f %12.4f %12.4f", - Node[n].ID, - Node[n].El*Ucf[ELEV], - (Tank[i].H0 - Node[n].El)*Ucf[ELEV], - (Tank[i].Hmin - Node[n].El)*Ucf[ELEV], - (Tank[i].Hmax - Node[n].El)*Ucf[ELEV], - sqrt(4.0*Tank[i].A/PI)*Ucf[ELEV], - Tank[i].Vmin*SQR(Ucf[ELEV])*Ucf[ELEV]); - if ((j = Tank[i].Vcurve) > 0) - sprintf(s1,"%-31s",Curve[j].ID); - else - strcpy(s1,""); - fprintf(f, "\n%s %s", s,s1); - } - } - -/* Write [PIPES] section */ - - fprintf(f,"\n\n[PIPES]"); - for (i=1; i<=Nlinks; i++) - { - if (Link[i].Type <= PIPE) - { - d = Link[i].Diam; - kc = Link[i].Kc; - if (Formflag == DW) kc = kc*Ucf[ELEV]*1000.0; - km = Link[i].Km*SQR(d)*SQR(d)/0.02517; - sprintf(s," %-31s %-31s %-31s %12.4f %12.4f", - Link[i].ID, - Node[Link[i].N1].ID, - Node[Link[i].N2].ID, - Link[i].Len*Ucf[LENGTH], - d*Ucf[DIAM]); - if (Formflag == DW) sprintf(s1, "%12.4f %12.4f", kc, km); - else sprintf(s1, "%12.4f %12.4f", kc, km); - if (Link[i].Type == CV) sprintf(s2,"CV"); - else if (Link[i].Stat == CLOSED) sprintf(s2,"CLOSED"); - else strcpy(s2,""); - fprintf(f,"\n%s %s %s",s,s1,s2); - } - } - -/* Write [PUMPS] section */ - - fprintf(f, "\n\n[PUMPS]"); - for (i=1; i<=Npumps; i++) - { - n = Pump[i].Link; - sprintf(s," %-31s %-31s %-31s", - Link[n].ID, - Node[Link[n].N1].ID, - Node[Link[n].N2].ID); - - /* Pump has constant power */ - if (Pump[i].Ptype == CONST_HP) - sprintf(s1, " POWER %.4f", Link[n].Km); - - /* Pump has a head curve */ - else if ((j = Pump[i].Hcurve) > 0) - sprintf(s1, " HEAD %s", Curve[j].ID); - - /* Old format used for pump curve */ - else - { - fprintf(f, "\n%s %12.4f %12.4f %12.4f 0.0 %12.4f",s, - -Pump[i].H0*Ucf[HEAD], - (-Pump[i].H0 - Pump[i].R*pow(Pump[i].Q0,Pump[i].N))*Ucf[HEAD], - Pump[i].Q0*Ucf[FLOW], - Pump[i].Qmax*Ucf[FLOW]); - continue; - } - strcat(s,s1); - - if ((j = Pump[i].Upat) > 0) - sprintf(s1," PATTERN %s",Pattern[j].ID); - else strcpy(s1,""); - strcat(s,s1); - - if (Link[n].Kc != 1.0) - sprintf(s1, " SPEED %.4f", Link[n].Kc); - else strcpy(s1,""); - strcat(s,s1); - - fprintf(f,"\n%s",s); - } - -/* Write [VALVES] section */ - - fprintf(f, "\n\n[VALVES]"); - for (i=1; i<=Nvalves; i++) - { - n = Valve[i].Link; - d = Link[n].Diam; - kc = Link[n].Kc; - if (kc == MISSING) kc = 0.0; - switch (Link[n].Type) - { - case FCV: kc *= Ucf[FLOW]; break; - case PRV: - case PSV: - case PBV: kc *= Ucf[PRESSURE]; break; - } - km = Link[n].Km*SQR(d)*SQR(d)/0.02517; - - sprintf(s," %-31s %-31s %-31s %12.4f %5s", - Link[n].ID, - Node[Link[n].N1].ID, - Node[Link[n].N2].ID, - d*Ucf[DIAM], - LinkTxt[Link[n].Type]); - - if (Link[n].Type == GPV && (j = ROUND(Link[n].Kc)) > 0) - sprintf(s1,"%-31s %12.4f", Curve[j].ID, km); - else sprintf(s1,"%12.4f %12.4f",kc,km); - - fprintf(f, "\n%s %s", s,s1); - } - -/* Write [DEMANDS] section */ - - fprintf(f, "\n\n[DEMANDS]"); - ucf = Ucf[DEMAND]; - for (i=1; i<=Njuncs; i++) - { - for (demand = Node[i].D; demand != NULL; demand = demand->next) - { - sprintf(s," %-31s %14.6f",Node[i].ID,ucf*demand->Base); - if ((j = demand->Pat) > 0) sprintf(s1," %s",Pattern[j].ID); - else strcpy(s1,""); - fprintf(f,"\n%s %s",s,s1); - } - } - -/* Write [EMITTERS] section */ - - fprintf(f, "\n\n[EMITTERS]"); - for (i=1; i<=Njuncs; i++) - { - if (Node[i].Ke == 0.0) continue; - ke = Ucf[FLOW]/pow(Ucf[PRESSURE]*Node[i].Ke,(1.0/Qexp)); - fprintf(f,"\n %-31s %14.6f",Node[i].ID,ke); - } - -/* Write [STATUS] section */ - - fprintf(f, "\n\n[STATUS]"); - for (i=1; i<=Nlinks; i++) - { - if (Link[i].Type <= PUMP) - { - if (Link[i].Stat == CLOSED) - fprintf(f, "\n %-31s %s",Link[i].ID,StatTxt[CLOSED]); - - /* Write pump speed here for pumps with old-style pump curve input */ - else if (Link[i].Type == PUMP) - { - n = PUMPINDEX(i); - if ( - Pump[n].Hcurve == 0 && - Pump[n].Ptype != CONST_HP && - Link[i].Kc != 1.0 - ) - fprintf(f, "\n %-31s %-.4f",Link[i].ID, Link[i].Kc); - } - } - - /* Write fixed-status PRVs & PSVs (setting = MISSING) */ - else if (Link[i].Kc == MISSING) - { - if (Link[i].Stat == OPEN) - fprintf(f, "\n %-31s %s",Link[i].ID,StatTxt[OPEN]); - if (Link[i].Stat == CLOSED) - fprintf(f, "\n%-31s %s",Link[i].ID,StatTxt[CLOSED]); - } - } - -/* Write [PATTERNS] section */ -/* (Use 6 pattern factors per line) */ - - fprintf(f, "\n\n[PATTERNS]"); - for (i=1; i<=Npats; i++) - { - for (j=0; j Njuncs) kc *= Ucf[HEAD]; - else kc *= Ucf[PRESSURE]; - fprintf(f, "\n%s IF NODE %s %s %.4f", s, - Node[n].ID, ControlTxt[Control[i].Type], kc); - break; - - /* Print timer control */ - case TIMER: - fprintf(f, "\n%s AT %s %.4f HOURS", - s, ControlTxt[TIMER], Control[i].Time/3600.); - break; - - /* Print time-of-day control */ - case TIMEOFDAY: - fprintf(f, "\n%s AT %s %s", - s, ControlTxt[TIMEOFDAY], clocktime(Atime, Control[i].Time)); - break; - } - } - -/* Write [RULES] section */ - - fprintf(f, "\n\n[RULES]"); - for (i=1; i<=Nrules; i++) - { - fprintf(f, "\nRULE %s",Rule[i].label); - errcode = writeRuleinInp(f, i); - fprintf(f, "\n"); - } - -/* Write [QUALITY] section */ -/* (Skip nodes with default quality of 0) */ - - fprintf(f, "\n\n[QUALITY]"); - for (i=1; i<=Nnodes; i++) - { - if (Node[i].C0 == 0.0) continue; - fprintf(f, "\n %-31s %14.6f",Node[i].ID,Node[i].C0*Ucf[QUALITY]); - } - -/* Write [SOURCES] section */ - - fprintf(f, "\n\n[SOURCES]"); - for (i=1; i<=Nnodes; i++) - { - source = Node[i].S; - if (source == NULL) continue; - sprintf(s," %-31s %-8s %14.6f", - Node[i].ID, - SourceTxt[source->Type], - source->C0); - if ((j = source->Pat) > 0) - sprintf(s1,"%s",Pattern[j].ID); - else strcpy(s1,""); - fprintf(f,"\n%s %s",s,s1); - } - -/* Write [MIXING] section */ - - fprintf(f, "\n\n[MIXING]"); - for (i=1; i<=Ntanks; i++) - { - if (Tank[i].A == 0.0) continue; - fprintf(f, "\n %-31s %-8s %12.4f", - Node[Tank[i].Node].ID, - MixTxt[Tank[i].MixModel], - (Tank[i].V1max/Tank[i].Vmax)); - } - -/* Write [REACTIONS] section */ - - fprintf(f, "\n\n[REACTIONS]"); - fprintf(f, "\n ORDER BULK %-.2f", BulkOrder); - fprintf(f, "\n ORDER WALL %-.0f", WallOrder); - fprintf(f, "\n ORDER TANK %-.2f", TankOrder); - fprintf(f, "\n GLOBAL BULK %-.6f", Kbulk*SECperDAY); - fprintf(f, "\n GLOBAL WALL %-.6f", Kwall*SECperDAY); - if (Climit > 0.0) - fprintf(f, "\n LIMITING POTENTIAL %-.6f", Climit); - if (Rfactor != MISSING && Rfactor != 0.0) - fprintf(f, "\n ROUGHNESS CORRELATION %-.6f",Rfactor); - for (i=1; i<=Nlinks; i++) - { - if (Link[i].Type > PIPE) continue; - if (Link[i].Kb != Kbulk) - fprintf(f, "\n BULK %-31s %-.6f",Link[i].ID,Link[i].Kb*SECperDAY); - if (Link[i].Kw != Kwall) - fprintf(f, "\n WALL %-31s %-.6f",Link[i].ID,Link[i].Kw*SECperDAY); - } - for (i=1; i<=Ntanks; i++) - { - if (Tank[i].A == 0.0) continue; - if (Tank[i].Kb != Kbulk) - fprintf(f, "\n TANK %-31s %-.6f",Node[Tank[i].Node].ID, - Tank[i].Kb*SECperDAY); - } - -/* Write [ENERGY] section */ - - fprintf(f, "\n\n[ENERGY]"); - if (Ecost != 0.0) - fprintf(f, "\n GLOBAL PRICE %-.4f", Ecost); - if (Epat != 0) - fprintf(f, "\n GLOBAL PATTERN %s", Pattern[Epat].ID); - fprintf(f, "\n GLOBAL EFFIC %-.4f", Epump); - fprintf(f, "\n DEMAND CHARGE %-.4f", Dcost); - for (i=1; i<=Npumps; i++) - { - if (Pump[i].Ecost > 0.0) - fprintf(f, "\n PUMP %-31s PRICE %-.4f", - Link[Pump[i].Link].ID,Pump[i].Ecost); - if (Pump[i].Epat > 0.0) - fprintf(f, "\n PUMP %-31s PATTERN %s", - Link[Pump[i].Link].ID,Pattern[Pump[i].Epat].ID); - if (Pump[i].Ecurve > 0.0) - fprintf(f, "\n PUMP %-31s EFFIC %s", - Link[Pump[i].Link].ID,Curve[Pump[i].Ecurve].ID); - } - -/* Write [TIMES] section */ - - fprintf(f, "\n\n[TIMES]"); - fprintf(f, "\n DURATION %s",clocktime(Atime,Dur)); - fprintf(f, "\n HYDRAULIC TIMESTEP %s",clocktime(Atime,Hstep)); - fprintf(f, "\n QUALITY TIMESTEP %s",clocktime(Atime,Qstep)); - fprintf(f, "\n REPORT TIMESTEP %s",clocktime(Atime,Rstep)); - fprintf(f, "\n REPORT START %s",clocktime(Atime,Rstart)); - fprintf(f, "\n PATTERN TIMESTEP %s",clocktime(Atime,Pstep)); - fprintf(f, "\n PATTERN START %s",clocktime(Atime,Pstart)); - fprintf(f, "\n RULE TIMESTEP %s",clocktime(Atime,Rulestep)); - fprintf(f, "\n START CLOCKTIME %s",clocktime(Atime,Tstart)); - fprintf(f, "\n STATISTIC %s",TstatTxt[Tstatflag]); - -/* Write [OPTIONS] section */ - - fprintf(f, "\n\n[OPTIONS]"); - fprintf(f, "\n UNITS %s", FlowUnitsTxt[Flowflag]); - fprintf(f, "\n PRESSURE %s", PressUnitsTxt[Pressflag]); - fprintf(f, "\n HEADLOSS %s", FormTxt[Formflag]); - if (DefPat >= 1 && DefPat <= Npats) - fprintf(f, "\n PATTERN %s", Pattern[DefPat].ID); - if (Hydflag == USE) - fprintf(f, "\n HYDRAULICS USE %s", HydFname); - if (Hydflag == SAVE) - fprintf(f, "\n HYDRAULICS SAVE %s", HydFname); - if (ExtraIter == -1) - fprintf(f, "\n UNBALANCED STOP"); - if (ExtraIter >= 0) - fprintf(f, "\n UNBALANCED CONTINUE %d", ExtraIter); - if (Qualflag == CHEM) - fprintf(f, "\n QUALITY %s %s", ChemName, ChemUnits); - if (Qualflag == TRACE) - fprintf(f, "\n QUALITY TRACE %-31s", Node[TraceNode].ID); - if (Qualflag == AGE) - fprintf(f, "\n QUALITY AGE"); - if (Qualflag == NONE) - fprintf(f, "\n QUALITY NONE"); - fprintf(f, "\n DEMAND MULTIPLIER %-.4f", Dmult); - fprintf(f, "\n EMITTER EXPONENT %-.4f", 1.0/Qexp); - fprintf(f, "\n VISCOSITY %-.6f", Viscos/VISCOS); - fprintf(f, "\n DIFFUSIVITY %-.6f", Diffus/DIFFUS); - fprintf(f, "\n SPECIFIC GRAVITY %-.6f", SpGrav); - fprintf(f, "\n TRIALS %-d", MaxIter); - fprintf(f, "\n ACCURACY %-.8f", Hacc); - fprintf(f, "\n TOLERANCE %-.8f", Ctol*Ucf[QUALITY]); - fprintf(f, "\n CHECKFREQ %-d", CheckFreq); - fprintf(f, "\n MAXCHECK %-d", MaxCheck); - fprintf(f, "\n DAMPLIMIT %-.8f", DampLimit); - -/* Write [REPORT] section */ - - fprintf(f, "\n\n[REPORT]"); - fprintf(f, "\n PAGESIZE %d", PageSize); - fprintf(f, "\n STATUS %s", RptFlagTxt[Statflag]); - fprintf(f, "\n SUMMARY %s", RptFlagTxt[Summaryflag]); - fprintf(f, "\n ENERGY %s", RptFlagTxt[Energyflag]); - fprintf(f, "\n MESSAGES %s", RptFlagTxt[Messageflag]); - if (strlen(Rpt2Fname) > 0) - fprintf(f, "\n FILE %s", Rpt2Fname); - switch (Nodeflag) - { - case 0: - fprintf(f, "\n NODES NONE"); - break; - case 1: - fprintf(f, "\n NODES ALL"); - break; - default: - j = 0; - for (i=1; i<=Nnodes; i++) - { - if (Node[i].Rpt == 1) - { - if (j % 5 == 0) fprintf(f, "\n NODES "); - fprintf(f, "%s ", Node[i].ID); - j++; - } - } - } - switch (Linkflag) - { - case 0: - fprintf(f, "\n LINKS NONE"); - break; - case 1: - fprintf(f, "\n LINKS ALL"); - break; - default: - j = 0; - for (i=1; i<=Nlinks; i++) - { - if (Link[i].Rpt == 1) - { - if (j % 5 == 0) fprintf(f, "\n LINKS "); - fprintf(f, "%s ", Link[i].ID); - j++; - } - } - } - for (i=0; i -BIG) - fprintf(f, "\n %-20sABOVE %.6f", Field[i].Name, Field[i].RptLim[HI]); - } - else fprintf(f, "\n %-20sNO", Field[i].Name); - } - fprintf(f, "\n\n"); - -/* Write [COORDINATES] section */ - - if (Coordflag == TRUE) - { - fprintf(f, "\n\n[COORDINATES]"); - for (i=1; i<=Nnodes; i++) - { - if (Coord[i].HaveCoords == TRUE) - { - fprintf(f,"\n %-31s %14.6f %14.6f",Node[i].ID,Coord[i].X,Coord[i].Y); - } - } - fprintf(f, "\n\n"); - } - -/* Save auxilary data to new input file */ - - saveauxdata(f); - -/* Close the new input file */ - - fprintf(f, "\n[END]"); - fclose(f); - return(0); -} - +/* +********************************************************************* + +INPFILE.C -- Save Input Function for EPANET Program + +VERSION: 2.00 +DATE: 5/8/00 + 3/1/01 + 11/19/01 + 6/24/02 + 8/15/07 (2.00.11) + 2/14/08 (2.00.12) +AUTHOR: L. Rossman + US EPA - NRMRL + +This module contains the function saveinpfile() which saves the +data describing a piping network to a file in EPANET's text format. + +******************************************************************** +*/ + +#include +#include +#ifndef __APPLE__ +#include +#else +#include +#endif +#include "hash.h" +#include "text.h" +#include "types.h" +#include "epanet2.h" +#include "funcs.h" +#include +#define EXTERN extern +#include "vars.h" + +/* Defined in enumstxt.h in EPANET.C */ +extern char *LinkTxt[]; +extern char *FormTxt[]; +extern char *StatTxt[]; +extern char *FlowUnitsTxt[]; +extern char *PressUnitsTxt[]; +extern char *ControlTxt[]; +extern char *SourceTxt[]; +extern char *MixTxt[]; +extern char *TstatTxt[]; +extern char *RptFlagTxt[]; +extern char *SectTxt[]; + +void saveauxdata(parser_data_t *parser, FILE *f) +/* +------------------------------------------------------------ + Writes auxilary data from original input file to new file. +------------------------------------------------------------ +*/ +{ + int sect, newsect; + char *tok; + char line[MAXLINE + 1]; + char s[MAXLINE + 1]; + FILE *InFile = parser->InFile; + char Coordflag = parser->Coordflag; + + sect = -1; + if (InFile == NULL) { + return; + } + rewind(InFile); + while (fgets(line, MAXLINE, InFile) != NULL) { + /* Check if line begins with a new section heading */ + strcpy(s, line); + tok = strtok(s, SEPSTR); + if (tok != NULL && *tok == '[') { + newsect = findmatch(tok, SectTxt); + if (newsect >= 0) { + sect = newsect; + if (sect == _END) + break; + switch (sect) { + // case _RULES: + case _COORDS: + if (Coordflag == FALSE) { + fprintf(f, "%s", line); + } + break; + case _VERTICES: + case _LABELS: + case _BACKDROP: + case _TAGS: + fprintf(f, "%s", line); + } + continue; + } else + continue; + } + + /* Write lines appearing in the section to file */ + switch (sect) { + // case _RULES: + case _COORDS: + if (Coordflag == FALSE) { + fprintf(f, "%s", line); + } + break; + case _VERTICES: + case _LABELS: + case _BACKDROP: + case _TAGS: + fprintf(f, "%s", line); + } + } +} + +//// This function was heavily modified. //// + +int saveinpfile(EN_Project *pr, char *fname) +/* +------------------------------------------------- + Writes network data to text file. +------------------------------------------------- +*/ +{ + + EN_Network *net = &pr->network; + parser_data_t *par = &pr->parser; + report_options_t *rep = &pr->report; + out_file_t *out = &pr->out_files; + hydraulics_t *hyd = &pr->hydraulics; + quality_t *qu = &pr->quality; + time_options_t *time = &pr->time_options; + + int i, j, n; + int errcode; + double d, kc, ke, km, ucf; + char s[MAXLINE + 1], s1[MAXLINE + 1], s2[MAXLINE + 1]; + Pdemand demand; + Psource source; + FILE *f; + Slink *link; + Stank *tank; + Snode *node; + Spump *pump; + Scontrol *control; + Scurve *curve; + Scoord *coord; + + /* Open the new text file */ + + if ((f = fopen(fname, "wt")) == NULL) { + return (308); + } + + /* Write [TITLE] section */ + + fprintf(f, s_TITLE); + for (i = 0; i < 3; i++) { + if (strlen(pr->Title[i]) > 0) { + fprintf(f, "\n%s", pr->Title[i]); + } + } + + /* Write [JUNCTIONS] section */ + /* (Leave demands for [DEMANDS] section) */ + + fprintf(f, "\n\n"); + fprintf(f, s_JUNCTIONS); + for (i = 1; i <= net->Njuncs; i++) { + node = &net->Node[i]; + fprintf(f, "\n %-31s %12.4f ;%s", node->ID, node->El * pr->Ucf[ELEV], node->Comment); + } + /* Write [RESERVOIRS] section */ + + fprintf(f, "\n\n"); + fprintf(f, s_RESERVOIRS); + for (i = 1; i <= net->Ntanks; i++) { + tank = &net->Tank[i]; + if (tank->A == 0.0) { + node = &net->Node[tank->Node]; + sprintf(s, " %-31s %12.4f", node->ID, node->El * pr->Ucf[ELEV]); + if ((j = tank->Pat) > 0) { + sprintf(s1, " %-31s", net->Pattern[j].ID); + } + else { + strcpy(s1, ""); + } + fprintf(f, "\n%s %s ;%s", s, s1, node->Comment); + } + } + + /* Write [TANKS] section */ + + fprintf(f, "\n\n"); + fprintf(f, s_TANKS); + for (i = 1; i <= net->Ntanks; i++) { + tank = &net->Tank[i]; + if (tank->A > 0.0) { + node = &net->Node[tank->Node]; + sprintf(s, " %-31s %12.4f %12.4f %12.4f %12.4f %12.4f %12.4f", node->ID, + node->El * pr->Ucf[ELEV], (tank->H0 - node->El) * pr->Ucf[ELEV], + (tank->Hmin - node->El) * pr->Ucf[ELEV], + (tank->Hmax - node->El) * pr->Ucf[ELEV], + sqrt(4.0 * tank->A / PI) * pr->Ucf[ELEV], + tank->Vmin * SQR(pr->Ucf[ELEV]) * pr->Ucf[ELEV]); + if ((j = tank->Vcurve) > 0) + sprintf(s1, "%-31s", net->Curve[j].ID); + else + strcpy(s1, ""); + fprintf(f, "\n%s %s ;%s", s, s1, node->Comment); + } + } + + /* Write [PIPES] section */ + + fprintf(f, "\n\n"); + fprintf(f, s_PIPES); + for (i = 1; i <= net->Nlinks; i++) { + link = &net->Link[i]; + if (link->Type <= EN_PIPE) { + d = link->Diam; + kc = link->Kc; + if (hyd->Formflag == DW) + kc = kc * pr->Ucf[ELEV] * 1000.0; + km = link->Km * SQR(d) * SQR(d) / 0.02517; + sprintf(s, " %-31s %-31s %-31s %12.4f %12.4f", link->ID, + net->Node[link->N1].ID, net->Node[link->N2].ID, + link->Len * pr->Ucf[LENGTH], d * pr->Ucf[DIAM]); + if (hyd->Formflag == DW) + sprintf(s1, "%12.4f %12.4f", kc, km); + else + sprintf(s1, "%12.4f %12.4f", kc, km); + if (link->Type == EN_CVPIPE) + sprintf(s2, "CV"); + else if (link->Stat == CLOSED) + sprintf(s2, "CLOSED"); + else + strcpy(s2, ""); + fprintf(f, "\n%s %s %s ;%s", s, s1, s2, link->Comment); + } + } + + /* Write [PUMPS] section */ + + fprintf(f, "\n\n"); + fprintf(f, s_PUMPS); + for (i = 1; i <= net->Npumps; i++) { + n = net->Pump[i].Link; + link = &net->Link[n]; + pump = &net->Pump[i]; + sprintf(s, " %-31s %-31s %-31s", link->ID, net->Node[link->N1].ID, net->Node[link->N2].ID); + + /* Pump has constant power */ + if (pump->Ptype == CONST_HP) + sprintf(s1, " POWER %.4f", link->Km); + + /* Pump has a head curve */ + else if ((j = pump->Hcurve) > 0) + sprintf(s1, " HEAD %s", net->Curve[j].ID); + + /* Old format used for pump curve */ + else { + fprintf(f, "\n%s %12.4f %12.4f %12.4f 0.0 %12.4f", s, + -pump->H0 * pr->Ucf[HEAD], + (-pump->H0 - pump->R * pow(pump->Q0, pump->N)) * + pr->Ucf[HEAD], + pump->Q0 * pr->Ucf[FLOW], pump->Qmax * pr->Ucf[FLOW]); + continue; + } + strcat(s, s1); + + if ((j = pump->Upat) > 0) + sprintf(s1, " PATTERN %s", net->Pattern[j].ID); + else + strcpy(s1, ""); + strcat(s, s1); + + if (link->Kc != 1.0) + sprintf(s1, " SPEED %.4f", link->Kc); + else + strcpy(s1, ""); + strcat(s, s1); + + fprintf(f, "\n%s ;%s", s, link->Comment); + } + + /* Write [VALVES] section */ + + fprintf(f, "\n\n"); + fprintf(f, s_VALVES); + for (i = 1; i <= net->Nvalves; i++) { + n = net->Valve[i].Link; + link = &net->Link[n]; + d = link->Diam; + kc = link->Kc; + if (kc == MISSING) + kc = 0.0; + switch (link->Type) { + case EN_FCV: + kc *= pr->Ucf[FLOW]; + break; + case EN_PRV: + case EN_PSV: + case EN_PBV: + kc *= pr->Ucf[PRESSURE]; + break; + default: + break; + // not a valve... pipe/pump/cv + } + km = link->Km * SQR(d) * SQR(d) / 0.02517; + + sprintf(s, " %-31s %-31s %-31s %12.4f %5s", link->ID, net->Node[link->N1].ID, + net->Node[link->N2].ID, d * pr->Ucf[DIAM], LinkTxt[link->Type]); + + if (link->Type == EN_GPV && (j = ROUND(link->Kc)) > 0) + sprintf(s1, "%-31s %12.4f", net->Curve[j].ID, km); + else + sprintf(s1, "%12.4f %12.4f", kc, km); + + fprintf(f, "\n%s %s ;%s", s, s1, link->Comment); + } + + /* Write [DEMANDS] section */ + + fprintf(f, "\n\n"); + fprintf(f, s_DEMANDS); + ucf = pr->Ucf[DEMAND]; + for (i = 1; i <= net->Njuncs; i++) { + node = &net->Node[i]; + for (demand = node->D; demand != NULL; demand = demand->next) { + sprintf(s, " %-31s %14.6f", node->ID, ucf * demand->Base); + if ((j = demand->Pat) > 0) + sprintf(s1, " %s", net->Pattern[j].ID); + else + strcpy(s1, ""); + fprintf(f, "\n%s %s", s, s1); + } + } + + /* Write [EMITTERS] section */ + + fprintf(f, "\n\n"); + fprintf(f, s_EMITTERS); + for (i = 1; i <= net->Njuncs; i++) { + node = &net->Node[i]; + if (node->Ke == 0.0) + continue; + ke = pr->Ucf[FLOW] / pow(pr->Ucf[PRESSURE] * node->Ke, (1.0 / hyd->Qexp)); + fprintf(f, "\n %-31s %14.6f", node->ID, ke); + } + + /* Write [STATUS] section */ + + fprintf(f, "\n\n"); + fprintf(f, s_STATUS); + for (i = 1; i <= net->Nlinks; i++) { + link = &net->Link[i]; + if (link->Type <= EN_PUMP) { + if (link->Stat == CLOSED) + fprintf(f, "\n %-31s %s", link->ID, StatTxt[CLOSED]); + + /* Write pump speed here for pumps with old-style pump curve input */ + else if (link->Type == EN_PUMP) { + n = findpump(net, i); + pump = &net->Pump[n]; + if (pump->Hcurve == 0 && pump->Ptype != CONST_HP && + link->Kc != 1.0) + fprintf(f, "\n %-31s %-.4f", link->ID, link->Kc); + } + } + + /* Write fixed-status PRVs & PSVs (setting = MISSING) */ + else if (link->Kc == MISSING) { + if (link->Stat == OPEN) + fprintf(f, "\n %-31s %s", link->ID, StatTxt[OPEN]); + if (link->Stat == CLOSED) + fprintf(f, "\n%-31s %s", link->ID, StatTxt[CLOSED]); + } + } + + /* Write [PATTERNS] section */ + /* (Use 6 pattern factors per line) */ + + fprintf(f, "\n\n"); + fprintf(f, s_PATTERNS); + for (i = 1; i <= net->Npats; i++) { + for (j = 0; j < net->Pattern[i].Length; j++) { + if (j % 6 == 0) + fprintf(f, "\n %-31s", net->Pattern[i].ID); + fprintf(f, " %12.4f", net->Pattern[i].F[j]); + } + } + + /* Write [CURVES] section */ + + fprintf(f, "\n\n"); + fprintf(f, s_CURVES); + for (i = 1; i <= net->Ncurves; i++) { + for (j = 0; j < net->Curve[i].Npts; j++) { + curve = &net->Curve[i]; + fprintf(f, "\n %-31s %12.4f %12.4f", curve->ID, curve->X[j], curve->Y[j]); + } + } + + /* Write [CONTROLS] section */ + + fprintf(f, "\n\n"); + fprintf(f, s_CONTROLS); + for (i = 1; i <= net->Ncontrols; i++) { + control = &net->Control[i]; + /* Check that controlled link exists */ + if ((j = control->Link) <= 0) + continue; + link = &net->Link[j]; + + /* Get text of control's link status/setting */ + if (control->Setting == MISSING) + sprintf(s, " LINK %s %s ", link->ID, StatTxt[control->Status]); + else { + kc = control->Setting; + switch (link->Type) { + case EN_PRV: + case EN_PSV: + case EN_PBV: + kc *= pr->Ucf[PRESSURE]; + break; + case EN_FCV: + kc *= pr->Ucf[FLOW]; + break; + default: + break; + } + sprintf(s, " LINK %s %.4f", link->ID, kc); + } + + switch (control->Type) { + /* Print level control */ + case LOWLEVEL: + case HILEVEL: + n = control->Node; + node = &net->Node[n]; + kc = control->Grade - node->El; + if (n > net->Njuncs) + kc *= pr->Ucf[HEAD]; + else + kc *= pr->Ucf[PRESSURE]; + fprintf(f, "\n%s IF NODE %s %s %.4f", s, node->ID, + ControlTxt[control->Type], kc); + break; + + /* Print timer control */ + case TIMER: + fprintf(f, "\n%s AT %s %.4f HOURS", s, ControlTxt[TIMER], + control->Time / 3600.); + break; + + /* Print time-of-day control */ + case TIMEOFDAY: + fprintf(f, "\n%s AT %s %s", s, ControlTxt[TIMEOFDAY], + clocktime(rep->Atime, control->Time)); + break; + } + } + + /* Write [RULES] section */ + + fprintf(f, "\n\n"); + fprintf(f, s_RULES); + for (i = 1; i <= net->Nrules; i++) { + fprintf(f, "\nRULE %s", pr->rules.Rule[i].label); + errcode = writeRuleinInp(pr, f, i); + fprintf(f, "\n"); + } + + /* Write [QUALITY] section */ + /* (Skip nodes with default quality of 0) */ + + fprintf(f, "\n\n"); + fprintf(f, s_QUALITY); + for (i = 1; i <= net->Nnodes; i++) { + node = &net->Node[i]; + if (node->C0 == 0.0) + continue; + fprintf(f, "\n %-31s %14.6f", node->ID, node->C0 * pr->Ucf[QUALITY]); + } + + /* Write [SOURCES] section */ + + fprintf(f, "\n\n"); + fprintf(f, s_SOURCES); + for (i = 1; i <= net->Nnodes; i++) { + node = &net->Node[i]; + source = node->S; + if (source == NULL) + continue; + sprintf(s, " %-31s %-8s %14.6f", node->ID, SourceTxt[source->Type], source->C0); + if ((j = source->Pat) > 0) + sprintf(s1, "%s", net->Pattern[j].ID); + else + strcpy(s1, ""); + fprintf(f, "\n%s %s", s, s1); + } + + /* Write [MIXING] section */ + + fprintf(f, "\n\n"); + fprintf(f, s_MIXING); + for (i = 1; i <= net->Ntanks; i++) { + Stank *tank = &net->Tank[i]; + if (tank->A == 0.0) + continue; + fprintf(f, "\n %-31s %-8s %12.4f", net->Node[tank->Node].ID, + MixTxt[tank->MixModel], (tank->V1max / tank->Vmax)); + } + + /* Write [REACTIONS] section */ + + fprintf(f, "\n\n"); + fprintf(f, s_REACTIONS); + fprintf(f, "\n ORDER BULK %-.2f", qu->BulkOrder); + fprintf(f, "\n ORDER WALL %-.0f", qu->WallOrder); + fprintf(f, "\n ORDER TANK %-.2f", qu->TankOrder); + fprintf(f, "\n GLOBAL BULK %-.6f", qu->Kbulk * SECperDAY); + fprintf(f, "\n GLOBAL WALL %-.6f", qu->Kwall * SECperDAY); + if (qu->Climit > 0.0) + fprintf(f, "\n LIMITING POTENTIAL %-.6f", qu->Climit); + if (qu->Rfactor != MISSING && qu->Rfactor != 0.0) + fprintf(f, "\n ROUGHNESS CORRELATION %-.6f", qu->Rfactor); + for (i = 1; i <= net->Nlinks; i++) { + link = &net->Link[i]; + if (link->Type > EN_PIPE) + continue; + if (link->Kb != qu->Kbulk) + fprintf(f, "\n BULK %-31s %-.6f", link->ID, link->Kb * SECperDAY); + if (link->Kw != qu->Kwall) + fprintf(f, "\n WALL %-31s %-.6f", link->ID, link->Kw * SECperDAY); + } + for (i = 1; i <= net->Ntanks; i++) { + tank = &net->Tank[i]; + if (tank->A == 0.0) + continue; + if (tank->Kb != qu->Kbulk) + fprintf(f, "\n TANK %-31s %-.6f", net->Node[tank->Node].ID, + tank->Kb * SECperDAY); + } + + /* Write [ENERGY] section */ + + fprintf(f, "\n\n"); + fprintf(f, s_ENERGY); + if (hyd->Ecost != 0.0) + fprintf(f, "\n GLOBAL PRICE %-.4f", hyd->Ecost); + if (hyd->Epat != 0) + fprintf(f, "\n GLOBAL PATTERN %s", net->Pattern[hyd->Epat].ID); + fprintf(f, "\n GLOBAL EFFIC %-.4f", hyd->Epump); + fprintf(f, "\n DEMAND CHARGE %-.4f", hyd->Dcost); + for (i = 1; i <= net->Npumps; i++) { + pump = &net->Pump[i]; + if (pump->Ecost > 0.0) + fprintf(f, "\n PUMP %-31s PRICE %-.4f", net->Link[pump->Link].ID, + pump->Ecost); + if (pump->Epat > 0.0) + fprintf(f, "\n PUMP %-31s PATTERN %s", net->Link[pump->Link].ID, + net->Pattern[pump->Epat].ID); + if (pump->Ecurve > 0.0) + fprintf(f, "\n PUMP %-31s EFFIC %s", net->Link[pump->Link].ID, + net->Curve[pump->Ecurve].ID); + } + + /* Write [TIMES] section */ + + fprintf(f, "\n\n"); + fprintf(f, s_TIMES); + fprintf(f, "\n DURATION %s", clocktime(rep->Atime, time->Dur)); + fprintf(f, "\n HYDRAULIC TIMESTEP %s", clocktime(rep->Atime, time->Hstep)); + fprintf(f, "\n QUALITY TIMESTEP %s", clocktime(rep->Atime, qu->Qstep)); + fprintf(f, "\n REPORT TIMESTEP %s", clocktime(rep->Atime, time->Rstep)); + fprintf(f, "\n REPORT START %s", clocktime(rep->Atime, time->Rstart)); + fprintf(f, "\n PATTERN TIMESTEP %s", clocktime(rep->Atime, time->Pstep)); + fprintf(f, "\n PATTERN START %s", clocktime(rep->Atime, time->Pstart)); + fprintf(f, "\n RULE TIMESTEP %s", clocktime(rep->Atime, time->Rulestep)); + fprintf(f, "\n START CLOCKTIME %s", clocktime(rep->Atime, time->Tstart)); + fprintf(f, "\n STATISTIC %s", TstatTxt[rep->Tstatflag]); + + /* Write [OPTIONS] section */ + + fprintf(f, "\n\n"); + fprintf(f, s_OPTIONS); + fprintf(f, "\n UNITS %s", FlowUnitsTxt[par->Flowflag]); + fprintf(f, "\n PRESSURE %s", PressUnitsTxt[par->Pressflag]); + fprintf(f, "\n HEADLOSS %s", FormTxt[hyd->Formflag]); + if (hyd->DefPat >= 1 && hyd->DefPat <= net->Npats) + fprintf(f, "\n PATTERN %s", net->Pattern[hyd->DefPat].ID); + if (out->Hydflag == USE) + fprintf(f, "\n HYDRAULICS USE %s", out->HydFname); + if (out->Hydflag == SAVE) + fprintf(f, "\n HYDRAULICS SAVE %s", out->HydFname); + if (hyd->ExtraIter == -1) + fprintf(f, "\n UNBALANCED STOP"); + if (hyd->ExtraIter >= 0) + fprintf(f, "\n UNBALANCED CONTINUE %d", hyd->ExtraIter); + if (qu->Qualflag == CHEM) + fprintf(f, "\n QUALITY %s %s", qu->ChemName, qu->ChemUnits); + if (qu->Qualflag == TRACE) + fprintf(f, "\n QUALITY TRACE %-31s", net->Node[qu->TraceNode].ID); + if (qu->Qualflag == AGE) + fprintf(f, "\n QUALITY AGE"); + if (qu->Qualflag == NONE) + fprintf(f, "\n QUALITY NONE"); + fprintf(f, "\n DEMAND MULTIPLIER %-.4f", hyd->Dmult); + fprintf(f, "\n EMITTER EXPONENT %-.4f", 1.0 / hyd->Qexp); + fprintf(f, "\n VISCOSITY %-.6f", hyd->Viscos / VISCOS); + fprintf(f, "\n DIFFUSIVITY %-.6f", qu->Diffus / DIFFUS); + fprintf(f, "\n SPECIFIC GRAVITY %-.6f", hyd->SpGrav); + fprintf(f, "\n TRIALS %-d", hyd->MaxIter); + fprintf(f, "\n ACCURACY %-.8f", hyd->Hacc); + fprintf(f, "\n TOLERANCE %-.8f", qu->Ctol * pr->Ucf[QUALITY]); + fprintf(f, "\n CHECKFREQ %-d", hyd->CheckFreq); + fprintf(f, "\n MAXCHECK %-d", hyd->MaxCheck); + fprintf(f, "\n DAMPLIMIT %-.8f", hyd->DampLimit); + + /* Write [REPORT] section */ + + fprintf(f, "\n\n"); + fprintf(f, s_REPORT); + fprintf(f, "\n PAGESIZE %d", rep->PageSize); + fprintf(f, "\n STATUS %s", RptFlagTxt[rep->Statflag]); + fprintf(f, "\n SUMMARY %s", RptFlagTxt[rep->Summaryflag]); + fprintf(f, "\n ENERGY %s", RptFlagTxt[rep->Energyflag]); + fprintf(f, "\n MESSAGES %s", RptFlagTxt[rep->Messageflag]); + if (strlen(rep->Rpt2Fname) > 0) + fprintf(f, "\n FILE %s", rep->Rpt2Fname); + switch (rep->Nodeflag) { + case 0: + fprintf(f, "\n NODES NONE"); + break; + case 1: + fprintf(f, "\n NODES ALL"); + break; + default: + j = 0; + for (i = 1; i <= net->Nnodes; i++) { + node = &net->Node[i]; + if (node->Rpt == 1) { + if (j % 5 == 0) + fprintf(f, "\n NODES "); + fprintf(f, "%s ", node->ID); + j++; + } + } + } + switch (rep->Linkflag) { + case 0: + fprintf(f, "\n LINKS NONE"); + break; + case 1: + fprintf(f, "\n LINKS ALL"); + break; + default: + j = 0; + for (i = 1; i <= net->Nlinks; i++) { + Slink *link = &net->Link[i]; + if (link->Rpt == 1) { + if (j % 5 == 0) + fprintf(f, "\n LINKS "); + fprintf(f, "%s ", link->ID); + j++; + } + } + } + for (i = 0; i < FRICTION; i++) { + SField *field = &rep->Field[i]; + if (field->Enabled == TRUE) { + fprintf(f, "\n %-20sPRECISION %d", field->Name, field->Precision); + if (field->RptLim[LOW] < BIG) + fprintf(f, "\n %-20sBELOW %.6f", field->Name, field->RptLim[LOW]); + if (field->RptLim[HI] > -BIG) + fprintf(f, "\n %-20sABOVE %.6f", field->Name, field->RptLim[HI]); + } else + fprintf(f, "\n %-20sNO",field->Name); + } + fprintf(f, "\n\n"); + + /* Write [COORDINATES] section */ + + if (par->Coordflag == TRUE) { + fprintf(f, "\n\n"); + fprintf(f, s_COORDS); + for (i = 1; i <= net->Nnodes; i++) { + node = &net->Node[i]; + coord = &net->Coord[i]; + if (coord->HaveCoords == TRUE) { + fprintf(f, "\n %-31s %14.6f %14.6f", node->ID, coord->X, coord->Y); + } + } + fprintf(f, "\n\n"); + } + + /* Save auxilary data to new input file */ + + saveauxdata(par,f); + + /* Close the new input file */ + + fprintf(f, "\n"); + fprintf(f, s_END); + fclose(f); + return (0); +} diff --git a/src/input1.c b/src/input1.c old mode 100755 new mode 100644 index 15e1ae8..6da3bae --- a/src/input1.c +++ b/src/input1.c @@ -1,641 +1,720 @@ -/* -********************************************************************* - -INPUT1.C -- Input Functions for EPANET Program - -VERSION: 2.00 -DATE: 5/30/00 - 9/7/00 - 11/19/01 - 6/24/02 - 2/14/08 (2.00.12) -AUTHOR: L. Rossman - US EPA - NRMRL - - This module initializes, retrieves, and adjusts the input - data for a network simulation. - - The entry point for this module is: - getdata() -- called from ENopen() in EPANET.C. - -********************************************************************* -*/ - -#include -#include -#include -#ifndef __APPLE__ -#include -#endif -#include -#include "hash.h" -#include "text.h" -#include "types.h" -#include "funcs.h" -#define EXTERN extern -#include "vars.h" - -/* - --------------------- Module Global Variables ---------------------- -*/ - -#define MAXITER 200 /* Default max. # hydraulic iterations */ //(2.00.12 - LR) -#define HACC 0.001 /* Default hydraulics convergence ratio */ -#define HTOL 0.0005 /* Default hydraulic head tolerance (ft) */ - -/*** Updated 11/19/01 ***/ -#define QTOL 0.0001 /* Default flow rate tolerance (cfs) */ - -#define AGETOL 0.01 /* Default water age tolerance (hrs) */ -#define CHEMTOL 0.01 /* Default concentration tolerance */ -#define PAGESIZE 0 /* Default uses no page breaks */ -#define SPGRAV 1.0 /* Default specific gravity */ -#define EPUMP 75 /* Default pump efficiency */ -#define DEFPATID "1" /* Default demand pattern ID */ - -/* - These next three parameters are used in the hydraulics solver: -*/ - -#define RQTOL 1E-7 /* Default low flow resistance tolerance */ -#define CHECKFREQ 2 /* Default status check frequency */ -#define MAXCHECK 10 /* Default # iterations for status checks */ -#define DAMPLIMIT 0 /* Default damping threshold */ //(2.00.12 - LR) - -extern char *Fldname[]; /* Defined in enumstxt.h in EPANET.C */ -extern char *RptFlowUnitsTxt[]; - - -int getdata() -/* -**---------------------------------------------------------------- -** Input: none -** Output: returns error code -** Purpose: reads in network data from disk file -**---------------------------------------------------------------- -*/ -{ - int errcode = 0; - setdefaults(); /* Assign default data values */ - initreport(); /* Initialize reporting options */ - rewind(InFile); /* Rewind input file */ - ERRCODE(readdata()); /* Read in network data */ - if (!errcode) adjustdata(); /* Adjust data for default values */ - if (!errcode) initunits(); /* Initialize units on input data */ - ERRCODE(inittanks()); /* Initialize tank volumes */ - if (!errcode) convertunits(); /* Convert units on input data */ - return(errcode); -} /* End of getdata */ - - -void setdefaults() -/* -**---------------------------------------------------------------- -** Input: none -** Output: none -** Purpose: assigns default values to global variables -**---------------------------------------------------------------- -*/ -{ - strncpy(Title[0],"",MAXMSG); - strncpy(Title[1],"",MAXMSG); - strncpy(Title[2],"",MAXMSG); - strncpy(TmpDir,"",MAXFNAME); //(2.00.12 - LR) - strncpy(TmpFname,"",MAXFNAME); //(2.00.12 - LR) - strncpy(HydFname,"",MAXFNAME); - strncpy(MapFname,"",MAXFNAME); - strncpy(ChemName,t_CHEMICAL,MAXID); - strncpy(ChemUnits,u_MGperL,MAXID); - strncpy(DefPatID,DEFPATID,MAXID); - Hydflag = SCRATCH; /* No external hydraulics file */ - Qualflag = NONE; /* No quality simulation */ - Formflag = HW; /* Use Hazen-Williams formula */ - Unitsflag = US; /* US unit system */ - Flowflag = GPM; /* Flow units are gpm */ - Pressflag = PSI; /* Pressure units are psi */ - Tstatflag = SERIES; /* Generate time series output */ - Warnflag = FALSE; /* Warning flag is off */ - Htol = HTOL; /* Default head tolerance */ - Qtol = QTOL; /* Default flow tolerance */ - Hacc = HACC; /* Default hydraulic accuracy */ - Ctol = MISSING; /* No pre-set quality tolerance */ - MaxIter = MAXITER; /* Default max. hydraulic trials */ - ExtraIter = -1; /* Stop if network unbalanced */ - Dur = 0; /* 0 sec duration (steady state) */ - Tstart = 0; /* Starting time of day */ - Pstart = 0; /* Starting pattern period */ - Hstep = 3600; /* 1 hr hydraulic time step */ - Qstep = 0; /* No pre-set quality time step */ - Pstep = 3600; /* 1 hr time pattern period */ - Rstep = 3600; /* 1 hr reporting period */ - Rulestep = 0; /* No pre-set rule time step */ - Rstart = 0; /* Start reporting at time 0 */ - TraceNode = 0; /* No source tracing */ - BulkOrder = 1.0; /* 1st-order bulk reaction rate */ - WallOrder = 1.0; /* 1st-order wall reaction rate */ - TankOrder = 1.0; /* 1st-order tank reaction rate */ - Kbulk = 0.0; /* No global bulk reaction */ - Kwall = 0.0; /* No global wall reaction */ - Climit = 0.0; /* No limiting potential quality */ - Diffus = MISSING; /* Temporary diffusivity */ - Rfactor = 0.0; /* No roughness-reaction factor */ - Viscos = MISSING; /* Temporary viscosity */ - SpGrav = SPGRAV; /* Default specific gravity */ - DefPat = 0; /* Default demand pattern index */ - Epat = 0; /* No energy price pattern */ - Ecost = 0.0; /* Zero unit energy cost */ - Dcost = 0.0; /* Zero energy demand charge */ - Epump = EPUMP; /* Default pump efficiency */ - Emax = 0.0; /* Zero peak energy usage */ - Qexp = 2.0; /* Flow exponent for emitters */ - Dmult = 1.0; /* Demand multiplier */ - RQtol = RQTOL; /* Default hydraulics parameters */ - CheckFreq = CHECKFREQ; - MaxCheck = MAXCHECK; - DampLimit = DAMPLIMIT; //(2.00.12 - LR) -} /* End of setdefaults */ - - -void initreport() -/* -**---------------------------------------------------------------------- -** Input: none -** Output: none -** Purpose: initializes reporting options -**---------------------------------------------------------------------- -*/ -{ - int i; - strncpy(Rpt2Fname,"",MAXFNAME); - PageSize = PAGESIZE; /* Default page size for report */ - Summaryflag = TRUE; /* Write summary report */ - Messageflag = TRUE; /* Report error/warning messages */ - Statflag = FALSE; /* No hydraulic status reports */ - Energyflag = FALSE; /* No energy usage report */ - Nodeflag = 0; /* No reporting on nodes */ - Linkflag = 0; /* No reporting on links */ - for (i=0; i Pstep) Hstep = Pstep; - if (Hstep > Rstep) Hstep = Rstep; - -/* Report start time cannot be greater than simulation duration */ - if (Rstart > Dur) Rstart = 0; - -/* No water quality analysis for steady state run */ - if (Dur == 0) Qualflag = NONE; - -/* If no quality timestep, then make it 1/10 of hydraulic timestep */ - if (Qstep == 0) Qstep = Hstep/10; - -/* If no rule time step then make it 1/10 of hydraulic time step; */ -/* Rule time step cannot be greater than hydraulic time step */ - if (Rulestep == 0) Rulestep = Hstep/10; - Rulestep = MIN(Rulestep, Hstep); - -/* Quality timestep cannot exceed hydraulic timestep */ - Qstep = MIN(Qstep, Hstep); - -/* If no quality tolerance, then use default values */ - if (Ctol == MISSING) - { - if (Qualflag == AGE) Ctol = AGETOL; - else Ctol = CHEMTOL; - } - -/* Determine unit system based on flow units */ - switch (Flowflag) - { - case LPS: /* Liters/sec */ - case LPM: /* Liters/min */ - case MLD: /* megaliters/day */ - case CMH: /* cubic meters/hr */ - case CMD: /* cubic meters/day */ - Unitsflag = SI; - break; - default: - Unitsflag = US; - } - -/* Revise pressure units depending on flow units */ - if (Unitsflag != SI) Pressflag = PSI; - else if (Pressflag == PSI) Pressflag = METERS; - -/* Store value of viscosity & diffusivity */ - ucf = 1.0; - if (Unitsflag == SI) ucf = SQR(MperFT); - - if (Viscos == MISSING) /* No value supplied */ - Viscos = VISCOS; - else if (Viscos > 1.e-3) /* Multiplier supplied */ - Viscos = Viscos*VISCOS; - else /* Actual value supplied */ - Viscos = Viscos/ucf; - - if (Diffus == MISSING) - Diffus = DIFFUS; - else if (Diffus > 1.e-4) - Diffus = Diffus*DIFFUS; - else - Diffus = Diffus/ucf; - -/* - Set exponent in head loss equation and adjust flow-resistance tolerance. -*/ - if (Formflag == HW) Hexp = 1.852; - else Hexp = 2.0; - -/*** Updated 9/7/00 ***/ -/*** No adjustment made to flow-resistance tolerance ***/ - /*RQtol = RQtol/Hexp;*/ - -/* See if default reaction coeffs. apply */ - for (i=1; i<=Nlinks; i++) - { - if (Link[i].Type > PIPE) continue; - if (Link[i].Kb == MISSING) Link[i].Kb = Kbulk; /* Bulk coeff. */ - if (Link[i].Kw == MISSING) /* Wall coeff. */ - { - /* Rfactor is the pipe roughness correlation factor */ - if (Rfactor == 0.0) Link[i].Kw = Kwall; - else if ((Link[i].Kc > 0.0) && (Link[i].Diam > 0.0)) - { - if (Formflag == HW) Link[i].Kw = Rfactor/Link[i].Kc; - if (Formflag == DW) Link[i].Kw = Rfactor/ABS(log(Link[i].Kc/Link[i].Diam)); - if (Formflag == CM) Link[i].Kw = Rfactor*Link[i].Kc; - } - else Link[i].Kw = 0.0; - } - } - for (i=1; i<=Ntanks; i++) - if (Tank[i].Kb == MISSING) Tank[i].Kb = Kbulk; - -/* Use default pattern if none assigned to a demand */ - for (i=1; i<=Nnodes; i++) - { - for (demand = Node[i].D; demand != NULL; demand = demand->next) - if (demand->Pat == 0) demand->Pat = DefPat; - } - -/* Remove QUALITY as a reporting variable if no WQ analysis */ - if (Qualflag == NONE) Field[QUALITY].Enabled = FALSE; - -} /* End of adjustdata */ - - -int inittanks() -/* -**--------------------------------------------------------------- -** Input: none -** Output: returns error code -** Purpose: initializes volumes in non-cylindrical tanks -**--------------------------------------------------------------- -*/ -{ - int i,j,n = 0; - double a; - int errcode = 0, - levelerr; - - for (j=1; j<=Ntanks; j++) - { - - /* Skip reservoirs */ - if (Tank[j].A == 0.0) continue; - - /* Check for valid lower/upper tank levels */ - levelerr = 0; - if (Tank[j].H0 > Tank[j].Hmax || - Tank[j].Hmin > Tank[j].Hmax || - Tank[j].H0 < Tank[j].Hmin - ) levelerr = 1; - - /* Check that tank heights are within volume curve */ - i = Tank[j].Vcurve; - if (i > 0) - { - n = Curve[i].Npts - 1; - if (Tank[j].Hmin < Curve[i].X[0] || - Tank[j].Hmax > Curve[i].X[n] - ) levelerr = 1; - } - - /* Report error in levels if found */ - if (levelerr) - { - sprintf(Msg,ERR225,Node[Tank[j].Node].ID); - writeline(Msg); - errcode = 200; - } - - /* Else if tank has a volume curve, */ - else if (i > 0) - { - /* Find min., max., and initial volumes from curve */ - Tank[j].Vmin = interp(Curve[i].Npts,Curve[i].X, - Curve[i].Y,Tank[j].Hmin); - Tank[j].Vmax = interp(Curve[i].Npts,Curve[i].X, - Curve[i].Y,Tank[j].Hmax); - Tank[j].V0 = interp(Curve[i].Npts,Curve[i].X, - Curve[i].Y,Tank[j].H0); - - /* Find a "nominal" diameter for tank */ - a = (Curve[i].Y[n] - Curve[i].Y[0])/ - (Curve[i].X[n] - Curve[i].X[0]); - Tank[j].A = sqrt(4.0*a/PI); - } - } - return(errcode); -} /* End of inittanks */ - - -void initunits() -/* -**-------------------------------------------------------------- -** Input: none -** Output: none -** Purpose: determines unit conversion factors -**-------------------------------------------------------------- -*/ -{ - double dcf, /* distance conversion factor */ - ccf, /* concentration conversion factor */ - qcf, /* flow conversion factor */ - hcf, /* head conversion factor */ - pcf, /* pressure conversion factor */ - wcf; /* energy conversion factor */ - - if (Unitsflag == SI) /* SI units */ - { - strcpy(Field[DEMAND].Units,RptFlowUnitsTxt[Flowflag]); - strcpy(Field[ELEV].Units,u_METERS); - strcpy(Field[HEAD].Units,u_METERS); - if (Pressflag == METERS) strcpy(Field[PRESSURE].Units,u_METERS); - else strcpy(Field[PRESSURE].Units,u_KPA); - strcpy(Field[LENGTH].Units,u_METERS); - strcpy(Field[DIAM].Units,u_MMETERS); - strcpy(Field[FLOW].Units,RptFlowUnitsTxt[Flowflag]); - strcpy(Field[VELOCITY].Units,u_MperSEC); - strcpy(Field[HEADLOSS].Units,u_per1000M); - strcpy(Field[FRICTION].Units,""); - strcpy(Field[POWER].Units,u_KW); - dcf = 1000.0*MperFT; - qcf = LPSperCFS; - if (Flowflag == LPM) qcf = LPMperCFS; - if (Flowflag == MLD) qcf = MLDperCFS; - if (Flowflag == CMH) qcf = CMHperCFS; - if (Flowflag == CMD) qcf = CMDperCFS; - hcf = MperFT; - if (Pressflag == METERS) pcf = MperFT*SpGrav; - else pcf = KPAperPSI*PSIperFT*SpGrav; - wcf = KWperHP; - } - else /* US units */ - { - strcpy(Field[DEMAND].Units,RptFlowUnitsTxt[Flowflag]); - strcpy(Field[ELEV].Units,u_FEET); - strcpy(Field[HEAD].Units,u_FEET); - strcpy(Field[PRESSURE].Units,u_PSI); - strcpy(Field[LENGTH].Units,u_FEET); - strcpy(Field[DIAM].Units,u_INCHES); - strcpy(Field[FLOW].Units,RptFlowUnitsTxt[Flowflag]); - strcpy(Field[VELOCITY].Units,u_FTperSEC); - strcpy(Field[HEADLOSS].Units,u_per1000FT); - strcpy(Field[FRICTION].Units,""); - strcpy(Field[POWER].Units,u_HP); - dcf = 12.0; - qcf = 1.0; - if (Flowflag == GPM) qcf = GPMperCFS; - if (Flowflag == MGD) qcf = MGDperCFS; - if (Flowflag == IMGD)qcf = IMGDperCFS; - if (Flowflag == AFD) qcf = AFDperCFS; - hcf = 1.0; - pcf = PSIperFT*SpGrav; - wcf = 1.0; - } - strcpy(Field[QUALITY].Units,""); - ccf = 1.0; - if (Qualflag == CHEM) - { - ccf = 1.0/LperFT3; - strncpy(Field[QUALITY].Units,ChemUnits,MAXID); - strncpy(Field[REACTRATE].Units,ChemUnits,MAXID); - strcat(Field[REACTRATE].Units,t_PERDAY); - } - else if (Qualflag == AGE) strcpy(Field[QUALITY].Units,u_HOURS); - else if (Qualflag == TRACE) strcpy(Field[QUALITY].Units,u_PERCENT); - Ucf[DEMAND] = qcf; - Ucf[ELEV] = hcf; - Ucf[HEAD] = hcf; - Ucf[PRESSURE] = pcf; - Ucf[QUALITY] = ccf; - Ucf[LENGTH] = hcf; - Ucf[DIAM] = dcf; - Ucf[FLOW] = qcf; - Ucf[VELOCITY] = hcf; - Ucf[HEADLOSS] = hcf; - Ucf[LINKQUAL] = ccf; - Ucf[REACTRATE] = ccf; - Ucf[FRICTION] = 1.0; - Ucf[POWER] = wcf; - Ucf[VOLUME] = hcf*hcf*hcf; - if (Hstep < 1800) /* Report time in mins. */ - { /* if hydraulic time step */ - Ucf[TIME] = 1.0/60.0; /* is less than 1/2 hour. */ - strcpy(Field[TIME].Units,u_MINUTES); - } - else - { - Ucf[TIME] = 1.0/3600.0; - strcpy(Field[TIME].Units,u_HOURS); - } - -} /* End of initunits */ - - -void convertunits() -/* -**-------------------------------------------------------------- -** Input: none -** Output: none -** Purpose: converts units of input data -**-------------------------------------------------------------- -*/ -{ - int i,j,k; - double ucf; /* Unit conversion factor */ - Pdemand demand; /* Pointer to demand record */ - -/* Convert nodal elevations & initial WQ */ -/* (WQ source units are converted in QUALITY.C */ - for (i=1; i<=Nnodes; i++) - { - Node[i].El /= Ucf[ELEV]; - Node[i].C0 /= Ucf[QUALITY]; - } - -/* Convert demands */ - for (i=1; i<=Njuncs; i++) - { - for (demand = Node[i].D; demand != NULL; demand = demand->next) - demand->Base /= Ucf[DEMAND]; - } - -/* Convert emitter discharge coeffs. to head loss coeff. */ - ucf = pow(Ucf[FLOW],Qexp)/Ucf[PRESSURE]; - for (i=1; i<=Njuncs; i++) - if (Node[i].Ke > 0.0) Node[i].Ke = ucf/pow(Node[i].Ke,Qexp); - -/* Initialize tank variables (convert tank levels to elevations) */ - for (j=1; j<=Ntanks; j++) - { - i = Tank[j].Node; - Tank[j].H0 = Node[i].El + Tank[j].H0/Ucf[ELEV]; - Tank[j].Hmin = Node[i].El + Tank[j].Hmin/Ucf[ELEV]; - Tank[j].Hmax = Node[i].El + Tank[j].Hmax/Ucf[ELEV]; - Tank[j].A = PI*SQR(Tank[j].A/Ucf[ELEV])/4.0; - Tank[j].V0 /= Ucf[VOLUME]; - Tank[j].Vmin /= Ucf[VOLUME]; - Tank[j].Vmax /= Ucf[VOLUME]; - Tank[j].Kb /= SECperDAY; - Tank[j].V = Tank[j].V0; - Tank[j].C = Node[i].C0; - Tank[j].V1max *= Tank[j].Vmax; - } - -/* Convert WQ option concentration units */ - Climit /= Ucf[QUALITY]; - Ctol /= Ucf[QUALITY]; - -/* Convert global reaction coeffs. */ - Kbulk /= SECperDAY; - Kwall /= SECperDAY; - -/* Convert units of link parameters */ - for (k=1; k<=Nlinks; k++) - { - if (Link[k].Type <= PIPE) - { - /* Convert pipe parameter units: */ - /* - for Darcy-Weisbach formula, convert roughness */ - /* from millifeet (or mm) to ft (or m) */ - /* - for US units, convert diameter from inches to ft */ - if (Formflag == DW) Link[k].Kc /= (1000.0*Ucf[ELEV]); - Link[k].Diam /= Ucf[DIAM]; - Link[k].Len /= Ucf[LENGTH]; - - /* Convert minor loss coeff. from V^2/2g basis to Q^2 basis */ - Link[k].Km = 0.02517*Link[k].Km/SQR(Link[k].Diam)/SQR(Link[k].Diam); - - /* Convert units on reaction coeffs. */ - Link[k].Kb /= SECperDAY; - Link[k].Kw /= SECperDAY; - } - - else if (Link[k].Type == PUMP ) - { - /* Convert units for pump curve parameters */ - i = PUMPINDEX(k); - if (Pump[i].Ptype == CONST_HP) - { - /* For constant hp pump, convert kw to hp */ - if (Unitsflag == SI) Pump[i].R /= Ucf[POWER]; - } - else - { - /* For power curve pumps, convert */ - /* shutoff head and flow coefficient */ - if (Pump[i].Ptype == POWER_FUNC) - { - Pump[i].H0 /= Ucf[HEAD]; - Pump[i].R *= (pow(Ucf[FLOW],Pump[i].N)/Ucf[HEAD]); - } - /* Convert flow range & max. head units */ - Pump[i].Q0 /= Ucf[FLOW]; - Pump[i].Qmax /= Ucf[FLOW]; - Pump[i].Hmax /= Ucf[HEAD]; - } - } - - else - { - /* For flow control valves, convert flow setting */ - /* while for other valves convert pressure setting */ - Link[k].Diam /= Ucf[DIAM]; - Link[k].Km = 0.02517*Link[k].Km/SQR(Link[k].Diam)/SQR(Link[k].Diam); - if (Link[k].Kc != MISSING) switch (Link[k].Type) - { - case FCV: Link[k].Kc /= Ucf[FLOW]; break; - case PRV: - case PSV: - case PBV: Link[k].Kc /= Ucf[PRESSURE]; break; - } - } - - /* Compute flow resistances */ - resistance(k); - } - -/* Convert units on control settings */ - for (i=1; i<=Ncontrols; i++) - { - if ( (k = Control[i].Link) == 0) continue; - if ( (j = Control[i].Node) > 0) - { - /* j = index of controlling node, and if */ - /* j > Njuncs, then control is based on tank level */ - /* otherwise control is based on nodal pressure */ - if (j > Njuncs) - Control[i].Grade = Node[j].El + Control[i].Grade/Ucf[ELEV]; - else Control[i].Grade = Node[j].El + Control[i].Grade/Ucf[PRESSURE]; - } - - /* Convert units on valve settings */ - if (Control[i].Setting != MISSING) switch (Link[k].Type) - { - case PRV: - case PSV: - case PBV: - Control[i].Setting /= Ucf[PRESSURE]; - break; - case FCV: - Control[i].Setting /= Ucf[FLOW]; - } - } -} /* End of convertunits */ - -/************************ END OF INPUT1.C ************************/ - +/* +********************************************************************* + +INPUT1.C -- Input Functions for EPANET Program + +VERSION: 2.00 +DATE: 5/30/00 + 9/7/00 + 11/19/01 + 6/24/02 + 2/14/08 (2.00.12) +AUTHOR: L. Rossman + US EPA - NRMRL + + This module initializes, retrieves, and adjusts the input + data for a network simulation. + + The entry point for this module is: + getdata() -- called from ENopen() in EPANET.C. + +********************************************************************* +*/ + +#include +#include +#include +#ifndef __APPLE__ +#include +#endif +#include "hash.h" +#include "text.h" +#include "types.h" +#include "epanet2.h" +#include "funcs.h" +#include +#define EXTERN extern +#include "vars.h" + +/* + --------------------- Module Global Variables ---------------------- +*/ + +#define MAXITER \ + 200 /* Default max. # hydraulic iterations */ +#define HACC 0.001 /* Default hydraulics convergence ratio */ +#define HTOL 0.0005 /* Default hydraulic head tolerance (ft) */ + +/*** Updated 11/19/01 ***/ +#define QTOL 0.0001 /* Default flow rate tolerance (cfs) */ + +#define AGETOL 0.01 /* Default water age tolerance (hrs) */ +#define CHEMTOL 0.01 /* Default concentration tolerance */ +#define PAGESIZE 0 /* Default uses no page breaks */ +#define SPGRAV 1.0 /* Default specific gravity */ +#define EPUMP 75 /* Default pump efficiency */ +#define DEFPATID "1" /* Default demand pattern ID */ + +/* + These next three parameters are used in the hydraulics solver: +*/ + +#define RQTOL 1E-7 /* Default low flow resistance tolerance */ +#define CHECKFREQ 2 /* Default status check frequency */ +#define MAXCHECK 10 /* Default # iterations for status checks */ +#define DAMPLIMIT \ + 0 /* Default damping threshold */ + +extern char *Fldname[]; /* Defined in enumstxt.h in EPANET.C */ +extern char *RptFlowUnitsTxt[]; + +int getdata(EN_Project *pr) +/* +**---------------------------------------------------------------- +** Input: none +** Output: returns error code +** Purpose: reads in network data from disk file +**---------------------------------------------------------------- +*/ +{ + int errcode = 0; + setdefaults(pr); /* Assign default data values */ + initreport(&pr->report); /* Initialize reporting options */ + rewind(pr->parser.InFile); /* Rewind input file */ + ERRCODE(readdata(pr)); /* Read in network data */ + if (!errcode) + adjustdata(pr); /* Adjust data for default values */ + if (!errcode) + initunits(pr); /* Initialize units on input data */ + ERRCODE(inittanks(pr)); /* Initialize tank volumes */ + if (!errcode) + convertunits(pr); /* Convert units on input data */ + return (errcode); +} /* End of getdata */ + +void setdefaults(EN_Project *pr) +/* +**---------------------------------------------------------------- +** Input: none +** Output: none +** Purpose: assigns default values to global variables +**---------------------------------------------------------------- +*/ +{ + parser_data_t *par = &pr->parser; + report_options_t *rep = &pr->report; + hydraulics_t *hyd = &pr->hydraulics; + quality_t *qu = &pr->quality; + time_options_t *time = &pr->time_options; + out_file_t *out = &pr->out_files; + + strncpy(pr->Title[0], "", MAXMSG); + strncpy(pr->Title[1], "", MAXMSG); + strncpy(pr->Title[2], "", MAXMSG); + strncpy(out->TmpDir, "", MAXFNAME); + strncpy(out->TmpFname, "", MAXFNAME); + strncpy(out->HydFname, "", MAXFNAME); + strncpy(pr->MapFname, "", MAXFNAME); + strncpy(qu->ChemName, t_CHEMICAL, MAXID); + strncpy(qu->ChemUnits, u_MGperL, MAXID); + strncpy(par->DefPatID, DEFPATID, MAXID); + out->Hydflag = SCRATCH; /* No external hydraulics file */ + qu->Qualflag = NONE; /* No quality simulation */ + hyd->Formflag = HW; /* Use Hazen-Williams formula */ + par->Unitsflag = US; /* US unit system */ + par->Flowflag = GPM; /* Flow units are gpm */ + par->Pressflag = PSI; /* Pressure units are psi */ + rep->Tstatflag = SERIES; /* Generate time series output */ + pr->Warnflag = FALSE; /* Warning flag is off */ + hyd->Htol = HTOL; /* Default head tolerance */ + hyd->Qtol = QTOL; /* Default flow tolerance */ + hyd->Hacc = HACC; /* Default hydraulic accuracy */ + qu->Ctol = MISSING; /* No pre-set quality tolerance */ + hyd->MaxIter = MAXITER; /* Default max. hydraulic trials */ + hyd->ExtraIter = -1; /* Stop if network unbalanced */ + time->Dur = 0; /* 0 sec duration (steady state) */ + time->Tstart = 0; /* Starting time of day */ + time->Pstart = 0; /* Starting pattern period */ + time->Hstep = 3600; /* 1 hr hydraulic time step */ + qu->Qstep = 0; /* No pre-set quality time step */ + time->Pstep = 3600; /* 1 hr time pattern period */ + time->Rstep = 3600; /* 1 hr reporting period */ + time->Rulestep = 0; /* No pre-set rule time step */ + time->Rstart = 0; /* Start reporting at time 0 */ + qu->TraceNode = 0; /* No source tracing */ + qu->BulkOrder = 1.0; /* 1st-order bulk reaction rate */ + qu->WallOrder = 1.0; /* 1st-order wall reaction rate */ + qu->TankOrder = 1.0; /* 1st-order tank reaction rate */ + qu->Kbulk = 0.0; /* No global bulk reaction */ + qu->Kwall = 0.0; /* No global wall reaction */ + qu->Climit = 0.0; /* No limiting potential quality */ + qu->Diffus = MISSING; /* Temporary diffusivity */ + qu->Rfactor = 0.0; /* No roughness-reaction factor */ + hyd->Viscos = MISSING; /* Temporary viscosity */ + hyd->SpGrav = SPGRAV; /* Default specific gravity */ + hyd->DefPat = 0; /* Default demand pattern index */ + hyd->Epat = 0; /* No energy price pattern */ + hyd->Ecost = 0.0; /* Zero unit energy cost */ + hyd->Dcost = 0.0; /* Zero energy demand charge */ + hyd->Epump = EPUMP; /* Default pump efficiency */ + hyd->Emax = 0.0; /* Zero peak energy usage */ + hyd->Qexp = 2.0; /* Flow exponent for emitters */ + hyd->Dmult = 1.0; /* Demand multiplier */ + hyd->RQtol = RQTOL; /* Default hydraulics parameters */ + hyd->CheckFreq = CHECKFREQ; + hyd->MaxCheck = MAXCHECK; + hyd->DampLimit = DAMPLIMIT; +} /* End of setdefaults */ + +void initreport(report_options_t *r) +/* +**---------------------------------------------------------------------- +** Input: none +** Output: none +** Purpose: initializes reporting options +**---------------------------------------------------------------------- +*/ +{ + int i; + strncpy(r->Rpt2Fname, "", MAXFNAME); + r->PageSize = PAGESIZE; /* Default page size for report */ + r->Summaryflag = TRUE; /* Write summary report */ + r->Messageflag = TRUE; /* Report error/warning messages */ + r->Statflag = FALSE; /* No hydraulic status reports */ + r->Energyflag = FALSE; /* No energy usage report */ + r->Nodeflag = 0; /* No reporting on nodes */ + r->Linkflag = 0; /* No reporting on links */ + for (i = 0; i < MAXVAR; i++) /* For each reporting variable: */ + { + strncpy(r->Field[i].Name, Fldname[i], MAXID); + r->Field[i].Enabled = FALSE; /* Not included in report */ + r->Field[i].Precision = 2; /* 2 decimal precision */ + + /*** Updated 6/24/02 ***/ + r->Field[i].RptLim[LOW] = SQR(BIG); /* No reporting limits */ + r->Field[i].RptLim[HI] = -SQR(BIG); + } + r->Field[FRICTION].Precision = 3; + for (i = DEMAND; i <= QUALITY; i++) { + r->Field[i].Enabled = TRUE; + } + for (i = FLOW; i <= HEADLOSS; i++) { + r->Field[i].Enabled = TRUE; + } +} + +void adjustdata(EN_Project *pr) +/* +**---------------------------------------------------------------------- +** Input: none +** Output: none +** Purpose: adjusts data after input file has been processed +**---------------------------------------------------------------------- +*/ +{ + EN_Network *net = &pr->network; + hydraulics_t *hyd = &pr->hydraulics; + quality_t *qu = &pr->quality; + time_options_t *time = &pr->time_options; + parser_data_t *par = &pr->parser; + report_options_t *rep = &pr->report; + + int i; + double ucf; /* Unit conversion factor */ + Pdemand demand; /* Pointer to demand record */ + + /* Use 1 hr pattern & report time step if none specified */ + if (time->Pstep <= 0) + time->Pstep = 3600; + if (time->Rstep == 0) + time->Rstep = time->Pstep; + + /* Hydraulic time step cannot be greater than pattern or report time step */ + if (time->Hstep <= 0) + time->Hstep = 3600; + if (time->Hstep > time->Pstep) + time->Hstep = time->Pstep; + if (time->Hstep > time->Rstep) + time->Hstep = time->Rstep; + + /* Report start time cannot be greater than simulation duration */ + if (time->Rstart > time->Dur) + time->Rstart = 0; + + /* No water quality analysis for steady state run */ + if (time->Dur == 0) + qu->Qualflag = NONE; + + /* If no quality timestep, then make it 1/10 of hydraulic timestep */ + if (qu->Qstep == 0) + qu->Qstep = time->Hstep / 10; + + /* If no rule time step then make it 1/10 of hydraulic time step; */ + /* Rule time step cannot be greater than hydraulic time step */ + if (time->Rulestep == 0) + time->Rulestep = time->Hstep / 10; + time->Rulestep = MIN(time->Rulestep, time->Hstep); + + /* Quality timestep cannot exceed hydraulic timestep */ + qu->Qstep = MIN(qu->Qstep, time->Hstep); + + /* If no quality tolerance, then use default values */ + if (qu->Ctol == MISSING) { + if (qu->Qualflag == AGE) + qu->Ctol = AGETOL; + else + qu->Ctol = CHEMTOL; + } + + /* Determine unit system based on flow units */ + switch (par->Flowflag) { + case LPS: /* Liters/sec */ + case LPM: /* Liters/min */ + case MLD: /* megaliters/day */ + case CMH: /* cubic meters/hr */ + case CMD: /* cubic meters/day */ + par->Unitsflag = SI; + break; + default: + par->Unitsflag = US; + } + + /* Revise pressure units depending on flow units */ + if (par->Unitsflag != SI) + par->Pressflag = PSI; + else if (par->Pressflag == PSI) + par->Pressflag = METERS; + + /* Store value of viscosity & diffusivity */ + ucf = 1.0; + if (par->Unitsflag == SI) + ucf = SQR(MperFT); + + if (hyd->Viscos == MISSING) /* No value supplied */ + hyd->Viscos = VISCOS; + else if (hyd->Viscos > 1.e-3) /* Multiplier supplied */ + hyd->Viscos = hyd->Viscos * VISCOS; + else /* Actual value supplied */ + hyd->Viscos = hyd->Viscos / ucf; + + if (qu->Diffus == MISSING) + qu->Diffus = DIFFUS; + else if (qu->Diffus > 1.e-4) + qu->Diffus = qu->Diffus * DIFFUS; + else + qu->Diffus = qu->Diffus / ucf; + + /* + Set exponent in head loss equation and adjust flow-resistance tolerance. + */ + if (hyd->Formflag == HW) + hyd->Hexp = 1.852; + else + hyd->Hexp = 2.0; + + /*** Updated 9/7/00 ***/ + /*** No adjustment made to flow-resistance tolerance ***/ + /*hyd->RQtol = hyd->RQtol/Hexp;*/ + + /* See if default reaction coeffs. apply */ + for (i = 1; i <= net->Nlinks; i++) { + Slink *link = &net->Link[i]; + if (link->Type > EN_PIPE) + continue; + if (link->Kb == MISSING) + link->Kb = qu->Kbulk; /* Bulk coeff. */ + if (link->Kw == MISSING) /* Wall coeff. */ + { + /* Rfactor is the pipe roughness correlation factor */ + if (qu->Rfactor == 0.0) + link->Kw = qu->Kwall; + else if ((link->Kc > 0.0) && (link->Diam > 0.0)) { + if (hyd->Formflag == HW) + link->Kw = qu->Rfactor / link->Kc; + if (hyd->Formflag == DW) + link->Kw = qu->Rfactor / ABS(log(link->Kc / link->Diam)); + if (hyd->Formflag == CM) + link->Kw = qu->Rfactor * link->Kc; + } else + link->Kw = 0.0; + } + } + for (i = 1; i <= net->Ntanks; i++) { + Stank *tank = &net->Tank[i]; + if (tank->Kb == MISSING) { + tank->Kb = qu->Kbulk; + } + } + /* Use default pattern if none assigned to a demand */ + for (i = 1; i <= net->Nnodes; i++) { + Snode *node = &net->Node[i]; + for (demand = node->D; demand != NULL; demand = demand->next) { + if (demand->Pat == 0) { + demand->Pat = hyd->DefPat; + } + } + } + + /* Remove QUALITY as a reporting variable if no WQ analysis */ + if (qu->Qualflag == NONE) + rep->Field[QUALITY].Enabled = FALSE; + +} /* End of adjustdata */ + +int inittanks(EN_Project *pr) +/* +**--------------------------------------------------------------- +** Input: none +** Output: returns error code +** Purpose: initializes volumes in non-cylindrical tanks +**--------------------------------------------------------------- +*/ +{ + EN_Network *net = &pr->network; + + int i, j, n = 0; + double a; + int errcode = 0, levelerr; + + for (j = 1; j <= net->Ntanks; j++) { + Stank *tank = &net->Tank[j]; + /* Skip reservoirs */ + if (tank->A == 0.0) + continue; + + /* Check for valid lower/upper tank levels */ + levelerr = 0; + if (tank->H0 > tank->Hmax || tank->Hmin > tank->Hmax || + tank->H0 < tank->Hmin) { + levelerr = 1; + } + + /* Check that tank heights are within volume curve */ + i = tank->Vcurve; + if (i > 0) { + Scurve *curve = &net->Curve[i]; + n = curve->Npts - 1; + if (tank->Hmin < curve->X[0] || tank->Hmax > curve->X[n]) + levelerr = 1; + } + + /* Report error in levels if found */ + if (levelerr) { + char errMsg[MAXMSG+1]; + EN_geterror(225, errMsg, MAXMSG); + sprintf(pr->Msg, "%s node: %s", errMsg, net->Node[tank->Node].ID); + writeline(pr, pr->Msg); + errcode = 200; + } + + /* Else if tank has a volume curve, */ + else if (i > 0) { + Scurve *curve = &net->Curve[i]; + /* Find min., max., and initial volumes from curve */ + tank->Vmin = interp(curve->Npts, curve->X, curve->Y, tank->Hmin); + tank->Vmax = interp(curve->Npts, curve->X, curve->Y, tank->Hmax); + tank->V0 = interp(curve->Npts, curve->X, curve->Y, tank->H0); + + /* Find a "nominal" diameter for tank */ + a = (curve->Y[n] - curve->Y[0]) / (curve->X[n] - curve->X[0]); + tank->A = sqrt(4.0 * a / PI); + } + } + return (errcode); +} /* End of inittanks */ + +void initunits(EN_Project *pr) +/* +**-------------------------------------------------------------- +** Input: none +** Output: none +** Purpose: determines unit conversion factors +**-------------------------------------------------------------- +*/ +{ + parser_data_t *par = &pr->parser; + report_options_t *rep = &pr->report; + hydraulics_t *hyd = &pr->hydraulics; + quality_t *qu = &pr->quality; + time_options_t *time = &pr->time_options; + + double dcf, /* distance conversion factor */ + ccf, /* concentration conversion factor */ + qcf, /* flow conversion factor */ + hcf, /* head conversion factor */ + pcf, /* pressure conversion factor */ + wcf; /* energy conversion factor */ + + if (par->Unitsflag == SI) /* SI units */ + { + strcpy(rep->Field[DEMAND].Units, RptFlowUnitsTxt[par->Flowflag]); + strcpy(rep->Field[ELEV].Units, u_METERS); + strcpy(rep->Field[HEAD].Units, u_METERS); + if (par->Pressflag == METERS) + strcpy(rep->Field[PRESSURE].Units, u_METERS); + else + strcpy(rep->Field[PRESSURE].Units, u_KPA); + strcpy(rep->Field[LENGTH].Units, u_METERS); + strcpy(rep->Field[DIAM].Units, u_MMETERS); + strcpy(rep->Field[FLOW].Units, RptFlowUnitsTxt[par->Flowflag]); + strcpy(rep->Field[VELOCITY].Units, u_MperSEC); + strcpy(rep->Field[HEADLOSS].Units, u_per1000M); + strcpy(rep->Field[FRICTION].Units, ""); + strcpy(rep->Field[POWER].Units, u_KW); + dcf = 1000.0 * MperFT; + qcf = LPSperCFS; + if (par->Flowflag == LPM) + qcf = LPMperCFS; + if (par->Flowflag == MLD) + qcf = MLDperCFS; + if (par->Flowflag == CMH) + qcf = CMHperCFS; + if (par->Flowflag == CMD) + qcf = CMDperCFS; + hcf = MperFT; + if (par->Pressflag == METERS) + pcf = MperFT * hyd->SpGrav; + else + pcf = KPAperPSI * PSIperFT * hyd->SpGrav; + wcf = KWperHP; + } else /* US units */ + { + strcpy(rep->Field[DEMAND].Units, RptFlowUnitsTxt[par->Flowflag]); + strcpy(rep->Field[ELEV].Units, u_FEET); + strcpy(rep->Field[HEAD].Units, u_FEET); + strcpy(rep->Field[PRESSURE].Units, u_PSI); + strcpy(rep->Field[LENGTH].Units, u_FEET); + strcpy(rep->Field[DIAM].Units, u_INCHES); + strcpy(rep->Field[FLOW].Units, RptFlowUnitsTxt[par->Flowflag]); + strcpy(rep->Field[VELOCITY].Units, u_FTperSEC); + strcpy(rep->Field[HEADLOSS].Units, u_per1000FT); + strcpy(rep->Field[FRICTION].Units, ""); + strcpy(rep->Field[POWER].Units, u_HP); + dcf = 12.0; + qcf = 1.0; + if (par->Flowflag == GPM) + qcf = GPMperCFS; + if (par->Flowflag == MGD) + qcf = MGDperCFS; + if (par->Flowflag == IMGD) + qcf = IMGDperCFS; + if (par->Flowflag == AFD) + qcf = AFDperCFS; + hcf = 1.0; + pcf = PSIperFT * hyd->SpGrav; + wcf = 1.0; + } + strcpy(rep->Field[QUALITY].Units, ""); + ccf = 1.0; + if (qu->Qualflag == CHEM) { + ccf = 1.0 / LperFT3; + strncpy(rep->Field[QUALITY].Units, qu->ChemUnits, MAXID); + strncpy(rep->Field[REACTRATE].Units, qu->ChemUnits, MAXID); + strcat(rep->Field[REACTRATE].Units, t_PERDAY); + } else if (qu->Qualflag == AGE) + strcpy(rep->Field[QUALITY].Units, u_HOURS); + else if (qu->Qualflag == TRACE) + strcpy(rep->Field[QUALITY].Units, u_PERCENT); + pr->Ucf[DEMAND] = qcf; + pr->Ucf[ELEV] = hcf; + pr->Ucf[HEAD] = hcf; + pr->Ucf[PRESSURE] = pcf; + pr->Ucf[QUALITY] = ccf; + pr->Ucf[LENGTH] = hcf; + pr->Ucf[DIAM] = dcf; + pr->Ucf[FLOW] = qcf; + pr->Ucf[VELOCITY] = hcf; + pr->Ucf[HEADLOSS] = hcf; + pr->Ucf[LINKQUAL] = ccf; + pr->Ucf[REACTRATE] = ccf; + pr->Ucf[FRICTION] = 1.0; + pr->Ucf[POWER] = wcf; + pr->Ucf[VOLUME] = hcf * hcf * hcf; + if (time->Hstep < 1800) /* Report time in mins. */ + { /* if hydraulic time step */ + pr->Ucf[TIME] = 1.0 / 60.0; /* is less than 1/2 hour. */ + strcpy(rep->Field[TIME].Units, u_MINUTES); + } else { + pr->Ucf[TIME] = 1.0 / 3600.0; + strcpy(rep->Field[TIME].Units, u_HOURS); + } + +} /* End of initunits */ + +void convertunits(EN_Project *pr) +/* +**-------------------------------------------------------------- +** Input: none +** Output: none +** Purpose: converts units of input data +**-------------------------------------------------------------- +*/ +{ + EN_Network *net = &pr->network; + hydraulics_t *hyd = &pr->hydraulics; + quality_t *qu = &pr->quality; + parser_data_t *par = &pr->parser; + + int i, j, k; + double ucf; /* Unit conversion factor */ + Pdemand demand; /* Pointer to demand record */ + Snode *node; + Stank *tank; + Slink *link; + Spump *pump; + Scontrol *control; + + /* Convert nodal elevations & initial WQ */ + /* (WQ source units are converted in QUALITY.C */ + for (i = 1; i <= net->Nnodes; i++) { + node = &net->Node[i]; + node->El /= pr->Ucf[ELEV]; + node->C0 /= pr->Ucf[QUALITY]; + } + + /* Convert demands */ + for (i = 1; i <= net->Njuncs; i++) { + node = &net->Node[i]; + for (demand = node->D; demand != NULL; demand = demand->next) { + demand->Base /= pr->Ucf[DEMAND]; + } + } + + /* Convert emitter discharge coeffs. to head loss coeff. */ + ucf = pow(pr->Ucf[FLOW], hyd->Qexp) / pr->Ucf[PRESSURE]; + for (i = 1; i <= net->Njuncs; i++) { + node = &net->Node[i]; + if (node->Ke > 0.0) { + node->Ke = ucf / pow(node->Ke, hyd->Qexp); + } + } + /* Initialize tank variables (convert tank levels to elevations) */ + for (j = 1; j <= net->Ntanks; j++) { + tank = &net->Tank[j]; + i = tank->Node; + node = &net->Node[i]; + tank->H0 = node->El + tank->H0 / pr->Ucf[ELEV]; + tank->Hmin = node->El + tank->Hmin / pr->Ucf[ELEV]; + tank->Hmax = node->El + tank->Hmax / pr->Ucf[ELEV]; + tank->A = PI * SQR(tank->A / pr->Ucf[ELEV]) / 4.0; + tank->V0 /= pr->Ucf[VOLUME]; + tank->Vmin /= pr->Ucf[VOLUME]; + tank->Vmax /= pr->Ucf[VOLUME]; + tank->Kb /= SECperDAY; + tank->V = tank->V0; + tank->C = node->C0; + tank->V1max *= tank->Vmax; + } + + /* Convert WQ option concentration units */ + qu->Climit /= pr->Ucf[QUALITY]; + qu->Ctol /= pr->Ucf[QUALITY]; + + /* Convert global reaction coeffs. */ + qu->Kbulk /= SECperDAY; + qu->Kwall /= SECperDAY; + + /* Convert units of link parameters */ + for (k = 1; k <= net->Nlinks; k++) { + link = &net->Link[k]; + if (link->Type <= EN_PIPE) { + /* Convert pipe parameter units: */ + /* - for Darcy-Weisbach formula, convert roughness */ + /* from millifeet (or mm) to ft (or m) */ + /* - for US units, convert diameter from inches to ft */ + if (hyd->Formflag == DW) + link->Kc /= (1000.0 * pr->Ucf[ELEV]); + link->Diam /= pr->Ucf[DIAM]; + link->Len /= pr->Ucf[LENGTH]; + + /* Convert minor loss coeff. from V^2/2g basis to Q^2 basis */ + link->Km = 0.02517 * link->Km / SQR(link->Diam) / SQR(link->Diam); + + /* Convert units on reaction coeffs. */ + link->Kb /= SECperDAY; + link->Kw /= SECperDAY; + } + + else if (link->Type == EN_PUMP) { + /* Convert units for pump curve parameters */ + i = findpump(net, k); + pump = &net->Pump[i]; + if (pump->Ptype == CONST_HP) { + /* For constant hp pump, convert kw to hp */ + if (par->Unitsflag == SI) + pump->R /= pr->Ucf[POWER]; + } else { + /* For power curve pumps, convert */ + /* shutoff head and flow coefficient */ + if (pump->Ptype == POWER_FUNC) { + pump->H0 /= pr->Ucf[HEAD]; + pump->R *= (pow(pr->Ucf[FLOW], pump->N) / pr->Ucf[HEAD]); + } + /* Convert flow range & max. head units */ + pump->Q0 /= pr->Ucf[FLOW]; + pump->Qmax /= pr->Ucf[FLOW]; + pump->Hmax /= pr->Ucf[HEAD]; + } + } + + else { + /* For flow control valves, convert flow setting */ + /* while for other valves convert pressure setting */ + link->Diam /= pr->Ucf[DIAM]; + link->Km = 0.02517 * link->Km / SQR(link->Diam) / SQR(link->Diam); + if (link->Kc != MISSING) + switch (link->Type) { + case EN_FCV: + link->Kc /= pr->Ucf[FLOW]; + break; + case EN_PRV: + case EN_PSV: + case EN_PBV: + link->Kc /= pr->Ucf[PRESSURE]; + break; + default: + break; + } + + } + + /* Compute flow resistances */ + resistance(pr, k); + } + + /* Convert units on control settings */ + for (i = 1; i <= net->Ncontrols; i++) { + control = &net->Control[i]; + if ((k = control->Link) == 0) { + continue; + } + link = &net->Link[k]; + if ((j = control->Node) > 0) { + node = &net->Node[j]; + if (j > net->Njuncs) { + /* j > Njuncs, then control is based on tank level */ + control->Grade = node->El + control->Grade / pr->Ucf[ELEV]; + } else { + /* otherwise control is based on nodal pressure */ + control->Grade = node->El + control->Grade / pr->Ucf[PRESSURE]; + } + } + + /* Convert units on valve settings */ + if (control->Setting != MISSING) { + switch (link->Type) { + case EN_PRV: + case EN_PSV: + case EN_PBV: + control->Setting /= pr->Ucf[PRESSURE]; + break; + case EN_FCV: + control->Setting /= pr->Ucf[FLOW]; + default: + break; + } + } + } +} /* End of convertunits */ + +/************************ END OF INPUT1.C ************************/ diff --git a/src/input2.c b/src/input2.c old mode 100755 new mode 100644 index aa03cdc..2e54645 --- a/src/input2.c +++ b/src/input2.c @@ -1,981 +1,1047 @@ -/* -********************************************************************** - -INPUT2.C -- Input data file interpreter for EPANET - -VERSION: 2.00 -DATE: 5/30/00 - 9/7/00 - 10/25/00 -AUTHOR: L. Rossman - US EPA - NRMRL - -This module reads and interprets the input data from file InFile. - -The entry points for this module are: - netsize() -- called from ENopen() in EPANET.C - readdata() -- called from getdata() in INPUT1.C - -The following utility functions are all called from INPUT3.C - addnodeID() - addlinkID() - findID() - getfloat() - -********************************************************************** -*/ - -#include -#include -#include -#ifndef __APPLE__ -#include -#endif -#include -#include "hash.h" -#include "text.h" -#include "types.h" -#include "funcs.h" -#define EXTERN extern -#include "vars.h" - -#define MAXERRS 10 /* Max. input errors reported */ - -int Ntokens, /* Number of tokens in input line */ - Ntitle; /* Number of title lines */ -char *Tok[MAXTOKS]; /* Array of token strings */ - - /* Used in INPUT3.C: */ -STmplist *PrevPat; /* Pointer to pattern list element */ -STmplist *PrevCurve; /* Pointer to curve list element */ - - /* Defined in enumstxt.h in EPANET.C */ -extern char *SectTxt[]; /* Input section keywords */ -extern char *RptSectTxt[]; - - -int netsize() -/* -**-------------------------------------------------------------- -** Input: none -** Output: returns error code -** Purpose: determines number of system components -**-------------------------------------------------------------- -*/ -{ - char line[MAXLINE+1]; /* Line from input data file */ - char *tok; /* First token of line */ - int sect,newsect; /* Input data sections */ - int errcode = 0; /* Error code */ - -/* Initialize network component counts */ - MaxJuncs = 0; - MaxTanks = 0; - MaxPipes = 0; - MaxPumps = 0; - MaxValves = 0; - MaxControls = 0; - MaxRules = 0; - MaxCurves = 0; - sect = -1; - -/* Add a default pattern 0 */ - MaxPats = -1; - addpattern(""); - -/* Make pass through data file counting number of each component */ - while (fgets(line,MAXLINE,InFile) != NULL) - { - /* Skip blank lines & those beginning with a comment */ - tok = strtok(line,SEPSTR); - if (tok == NULL) continue; - if (*tok == ';') continue; - - /* Check if line begins with a new section heading */ - if (*tok == '[') - { - newsect = findmatch(tok,SectTxt); - if (newsect >= 0) - { - sect = newsect; - if (sect == _END) break; - continue; - } - else continue; - } - - /* Add to count of current component */ - switch(sect) - { - case _JUNCTIONS: MaxJuncs++; break; - case _RESERVOIRS: - case _TANKS: MaxTanks++; break; - case _PIPES: MaxPipes++; break; - case _PUMPS: MaxPumps++; break; - case _VALVES: MaxValves++; break; - case _CONTROLS: MaxControls++; break; - case _RULES: addrule(tok); break; /* See RULES.C */ - case _PATTERNS: errcode = addpattern(tok); - break; - case _CURVES: errcode = addcurve(tok); - break; - } - if (errcode) break; - } - - MaxNodes = MaxJuncs + MaxTanks; - MaxLinks = MaxPipes + MaxPumps + MaxValves; - if (MaxPats < 1) MaxPats = 1; - if (!errcode) - { - if (MaxJuncs < 1) errcode = 223; /* Not enough nodes */ - else if (MaxTanks == 0) errcode = 224; /* No tanks */ - } - return(errcode); -} /* End of netsize */ - - -int readdata() -/* -**-------------------------------------------------------------- -** Input: none -** Output: returns error code -** Purpose: reads contents of input data file -**-------------------------------------------------------------- -*/ -{ - char line[MAXLINE+1], /* Line from input data file */ - wline[MAXLINE+1]; /* Working copy of input line */ - int sect,newsect, /* Data sections */ - errcode = 0, /* Error code */ - inperr,errsum; /* Error code & total error count */ - -/* Allocate input buffer */ - X = (double *) calloc(MAXTOKS, sizeof(double)); - ERRCODE(MEMCHECK(X)); - - if (!errcode) - { - - /* Initialize number of network components */ - Ntitle = 0; - Nnodes = 0; - Njuncs = 0; - Ntanks = 0; - Nlinks = 0; - Npipes = 0; - Npumps = 0; - Nvalves = 0; - Ncontrols = 0; - Nrules = 0; - Ncurves = MaxCurves; - Npats = MaxPats; - PrevPat = NULL; - PrevCurve = NULL; - - sect = -1; - errsum = 0; - - /* Read each line from input file. */ - while (fgets(line,MAXLINE,InFile) != NULL) - { - - /* Make copy of line and scan for tokens */ - strcpy(wline,line); - Ntokens = gettokens(wline); - - /* Skip blank lines and comments */ - if (Ntokens == 0) continue; - if (*Tok[0] == ';') continue; - - /* Check if max. length exceeded */ - if (strlen(line) >= MAXLINE) - { - sprintf(Msg,ERR214); - writeline(Msg); - writeline(line); - errsum++; - } - - /* Check if at start of a new input section */ - if (*Tok[0] == '[') - { - newsect = findmatch(Tok[0],SectTxt); - if (newsect >= 0) - { - sect = newsect; - if (sect == _END) break; - continue; - } - else - { - inperrmsg(201,sect,line); - errsum++; - break; - } - } - - /* Otherwise process next line of input in current section */ - else - { - if (sect >=0) //for cases were no section is present on the top of the input file - { - inperr = newline(sect,line); - if (inperr > 0) - { - inperrmsg(inperr,sect,line); - errsum++; - } - } - else - { - errcode = 200; - break; - } - } - - /* Stop if reach end of file or max. error count */ - if (errsum == MAXERRS) break; - } /* End of while */ - - /* Check for errors */ - if (errsum > 0) errcode = 200; - } - -/* Check for unlinked nodes */ - if (!errcode) errcode = unlinked(); - -/* Get pattern & curve data from temp. lists */ - if (!errcode) errcode = getpatterns(); - if (!errcode) errcode = getcurves(); - if (!errcode) errcode = getpumpparams(); - -/* Free input buffer */ - free(X); - return(errcode); - -} /* End of readdata */ - - -int newline(int sect, char *line) -/* -**-------------------------------------------------------------- -** Input: sect = current section of input file -** *line = line read from input file -** Output: returns error code or 0 if no error found -** Purpose: processes a new line of data from input file -**-------------------------------------------------------------- -*/ -{ - int n; - switch (sect) - { - case _TITLE: if (Ntitle < 3) - { - n = (int)strlen(line); - if (line[n-1] == 10) line[n-1] = ' '; - strncpy(Title[Ntitle],line,MAXMSG); - Ntitle++; - } - return(0); - case _JUNCTIONS: return(juncdata()); - case _RESERVOIRS: - case _TANKS: return(tankdata()); - case _PIPES: return(pipedata()); - case _PUMPS: return(pumpdata()); - case _VALVES: return(valvedata()); - case _PATTERNS: return(patterndata()); - case _CURVES: return(curvedata()); - case _DEMANDS: return(demanddata()); - case _CONTROLS: return(controldata()); - case _RULES: return(ruledata()); /* See RULES.C */ - case _SOURCES: return(sourcedata()); - case _EMITTERS: return(emitterdata()); - case _QUALITY: return(qualdata()); - case _STATUS: return(statusdata()); - case _ROUGHNESS: return(0); - case _ENERGY: return(energydata()); - case _REACTIONS: return(reactdata()); - case _MIXING: return(mixingdata()); - case _REPORT: return(reportdata()); - case _TIMES: return(timedata()); - case _OPTIONS: return(optiondata()); - - /* Data in these sections are not used for any computations */ - case _COORDS: if (Coordflag == TRUE) - { - return(coordata()); - } - else return(0); - case _LABELS: return(0); - case _TAGS: return(0); - case _VERTICES: return(0); - case _BACKDROP: return(0); - } - return(201); -} /* end of newline */ - - -int getpumpparams(void) -/* -**------------------------------------------------------------- -** Input: none -** Output: returns error code -** Purpose: computes & checks pump curve parameters -**-------------------------------------------------------------- -*/ -{ - int i, j = 0, k, m, n = 0; - double a,b,c, - h0 = 0.0, h1 = 0.0, h2 = 0.0, q1 = 0.0, q2 = 0.0; - - for (i=1; i<=Npumps; i++) - { - k = Pump[i].Link; - if (Pump[i].Ptype == CONST_HP) /* Constant Hp pump */ - { - Pump[i].H0 = 0.0; - Pump[i].R = -8.814*Link[k].Km; - Pump[i].N = -1.0; - Pump[i].Hmax = BIG; /* No head limit */ - Pump[i].Qmax = BIG; /* No flow limit */ - Pump[i].Q0 = 1.0; /* Init. flow = 1 cfs */ - continue; - } - - /* Set parameters for pump curves */ - else if (Pump[i].Ptype == NOCURVE) /* Pump curve specified */ - { - j = Pump[i].Hcurve; /* Get index of head curve */ - if (j == 0) - { /* Error: No head curve */ - sprintf(Msg,ERR226,Link[k].ID); - writeline(Msg); - return(200); - } - n = Curve[j].Npts; - if (n == 1) /* Only a single h-q point */ - { /* supplied so use generic */ - Pump[i].Ptype = POWER_FUNC; /* power function curve. */ - q1 = Curve[j].X[0]; - h1 = Curve[j].Y[0]; - h0 = 1.33334*h1; - q2 = 2.0*q1; - h2 = 0.0; - } - else if (n == 3 - && Curve[j].X[0] == 0.0) /* 3 h-q points supplied with */ - { /* shutoff head so use fitted */ - Pump[i].Ptype = POWER_FUNC; /* power function curve. */ - h0 = Curve[j].Y[0]; - q1 = Curve[j].X[1]; - h1 = Curve[j].Y[1]; - q2 = Curve[j].X[2]; - h2 = Curve[j].Y[2]; - } - else Pump[i].Ptype = CUSTOM; /* Else use custom pump curve.*/ - - /* Compute shape factors & limits of power function pump curves */ - if (Pump[i].Ptype == POWER_FUNC) - { - if (!powercurve(h0,h1,h2,q1,q2,&a,&b,&c)) - { /* Error: Invalid curve */ - sprintf(Msg,ERR227,Link[k].ID); - writeline(Msg); - return(200); - } - else - { - Pump[i].H0 = -a; - Pump[i].R = -b; - Pump[i].N = c; - Pump[i].Q0 = q1; - Pump[i].Qmax = pow((-a/b),(1.0/c)); - Pump[i].Hmax = h0; - } - } - } - - /* Assign limits to custom pump curves */ - if (Pump[i].Ptype == CUSTOM) - { - for (m=1; m= Curve[j].Y[m-1]) - { /* Error: Invalid curve */ - sprintf(Msg,ERR227,Link[k].ID); - writeline(Msg); - return(200); - } - } - Pump[i].Qmax = Curve[j].X[n-1]; - Pump[i].Q0 = (Curve[j].X[0] + Pump[i].Qmax)/2.0; - Pump[i].Hmax = Curve[j].Y[0]; - } - } /* Next pump */ - return(0); -} - - -int addnodeID(int n, char *id) -/* -**------------------------------------------------------------- -** Input: n = node index -** id = ID label -** Output: returns 0 if ID already in use, 1 if not -** Purpose: adds a node ID to the Node Hash Table -**-------------------------------------------------------------- -*/ -{ - if (findnode(id)) return(0); /* see EPANET.C */ - strncpy(Node[n].ID, id, MAXID); - ENHashTableInsert(NodeHashTable, Node[n].ID, n); /* see HASH.C */ - return(1); -} - - -int addlinkID(int n, char *id) -/* -**------------------------------------------------------------- -** Input: n = link index -** id = ID label -** Output: returns 0 if ID already in use, 1 if not -** Purpose: adds a link ID to the Link Hash Table -**-------------------------------------------------------------- -*/ -{ - if (findlink(id)) return(0); /* see EPANET.C */ - strncpy(Link[n].ID, id, MAXID); - ENHashTableInsert(LinkHashTable, Link[n].ID, n); /* see HASH.C */ - return(1); -} - - -int addpattern(char *id) -/* -**------------------------------------------------------------- -** Input: id = pattern ID label -** Output: returns error code -** Purpose: adds a new pattern to the database -**-------------------------------------------------------------- -*/ -{ - STmplist *p; - -/* Check if ID is same as last one processed */ - if (Patlist != NULL && strcmp(id,Patlist->ID) == 0) return(0); - -/* Check that pattern was not already created */ - if (findID(id,Patlist) == NULL) - { - - /* Update pattern count & create new list element */ - (MaxPats)++; - p = (STmplist *) malloc(sizeof(STmplist)); - if (p == NULL) return(101); - - /* Initialize list element properties */ - else - { - p->i = MaxPats; - strncpy(p->ID,id,MAXID); - p->x = NULL; - p->y = NULL; - p->next = Patlist; - Patlist = p; - } - } - return(0); -} - - -int addcurve(char *id) -/* -**------------------------------------------------------------- -** Input: id = curve ID label -** Output: returns error code -** Purpose: adds a new curve to the database -**-------------------------------------------------------------- -*/ -{ - STmplist *c; - -/* Check if ID is same as last one processed */ - if (Curvelist != NULL && strcmp(id,Curvelist->ID) == 0) return(0); - -/* Check that curve was not already created */ - if (findID(id,Curvelist) == NULL) - { - - /* Update curve count & create new list element */ - (MaxCurves)++; - c = (STmplist *) malloc(sizeof(STmplist)); - if (c == NULL) return(101); - - /* Initialize list element properties */ - else - { - c->i = MaxCurves; - strncpy(c->ID,id,MAXID); - c->x = NULL; - c->y = NULL; - c->next = Curvelist; - Curvelist = c; - } - } - return(0); -} - - -STmplist *findID(char *id, STmplist *list) -/* -**------------------------------------------------------------- -** Input: id = ID label -** list = pointer to head of a temporary list -** Output: returns list item with requested ID label -** Purpose: searches for item in temporary list -**------------------------------------------------------------- -*/ -{ - STmplist *item; - for (item = list; item != NULL; item = item->next) - { - if (strcmp(item->ID,id) == 0) - { - return(item); - } - } - return(NULL); -} - - -int unlinked() -/* -**-------------------------------------------------------------- -** Input: none -** Output: returns error code if any unlinked junctions found -** Purpose: checks for unlinked junctions in network -** -** NOTE: unlinked tanks have no effect on computations. -**-------------------------------------------------------------- -*/ -{ - char *marked; - int i,err, errcode; - errcode = 0; - err = 0; - marked = (char *) calloc(Nnodes+1,sizeof(char)); - ERRCODE(MEMCHECK(marked)); - if (!errcode) - { - memset(marked,0,(Nnodes+1)*sizeof(char)); - for (i=1; i<=Nlinks; i++) /* Mark end nodes of each link */ - { - marked[Link[i].N1]++; - marked[Link[i].N2]++; - } - for (i=1; i<=Njuncs; i++) /* Check each junction */ - { - if (marked[i] == 0) /* If not marked then error */ - { - err++; - sprintf(Msg,ERR233,Node[i].ID); - writeline(Msg); - } - if (err >= MAXERRS) break; - } - if (err > 0) errcode = 200; - } - free(marked); - return(errcode); -} /* End of unlinked */ - - -int getpatterns(void) -/* -**----------------------------------------------------------- -** Input: none -** Output: returns error code -** Purpose: retrieves pattern data from temporary linked list -**------------------------------------------------------------- -*/ -{ - int i,j; - SFloatlist *f; - STmplist *pat; - -/* Start at head of list */ - pat = Patlist; - -/* Traverse list of patterns */ - while (pat != NULL) - { - - /* Get index of current pattern in Pattern array */ - i = pat->i; - - /* Check if this is the default pattern */ - if (strcmp(pat->ID, DefPatID) == 0) DefPat = i; - if (i >= 0 && i <= MaxPats) - { - /* Save pattern ID */ - strcpy(Pattern[i].ID, pat->ID); - - /* Give pattern a length of at least 1 */ - if (Pattern[i].Length == 0) Pattern[i].Length = 1; - Pattern[i].F = (double *) calloc(Pattern[i].Length, sizeof(double)); - if (Pattern[i].F == NULL) return(101); - - /* Start at head of pattern multiplier list */ - /* (which holds multipliers in reverse order)*/ - f = pat->x; - j = Pattern[i].Length - 1; - - /* Use at least one multiplier equal to 1.0 */ - if (f == NULL) Pattern[i].F[0] = 1.0; - - /* Traverse list, storing multipliers in Pattern array */ - else while (f != NULL && j >= 0) - { - Pattern[i].F[j] = f->value; - f = f->next; - j--; - } - } - pat = pat->next; - } - return(0); -} - - -int getcurves(void) -/* -**----------------------------------------------------------- -** Input: none -** Output: returns error code -** Purpose: retrieves curve data from temporary linked list -**----------------------------------------------------------- -*/ -{ - int i,j; - double x; - SFloatlist *fx, *fy; - STmplist *c; - -/* Start at head of curve list */ - c = Curvelist; - -/* Traverse list of curves */ - while (c != NULL) - { - i = c->i; - if (i >= 1 && i <= MaxCurves) - { - - /* Save curve ID */ - strcpy(Curve[i].ID, c->ID); - - /* Check that curve has data points */ - if (Curve[i].Npts <= 0) - { - sprintf(Msg,ERR230,c->ID); - writeline(Msg); - return(200); - } - - /* Allocate memory for curve data */ - Curve[i].X = (double *) calloc(Curve[i].Npts, sizeof(double)); - Curve[i].Y = (double *) calloc(Curve[i].Npts, sizeof(double)); - if (Curve[i].X == NULL || Curve[i].Y == NULL) return(101); - - /* Traverse list of x,y data */ - x = BIG; - fx = c->x; - fy = c->y; - j = Curve[i].Npts - 1; - while (fx != NULL && fy != NULL && j >= 0) - { - - /* Check that x data is in ascending order */ - if (fx->value >= x) - { - sprintf(Msg,ERR230,c->ID); - writeline(Msg); - return(200); - } - x = fx->value; - - /* Save x,y data in Curve structure */ - Curve[i].X[j] = fx->value; - fx = fx->next; - Curve[i].Y[j] = fy->value; - fy = fy->next; - j--; - } - } - c = c->next; - } - return(0); -} - - -int findmatch(char *line, char *keyword[]) -/* -**-------------------------------------------------------------- -** Input: *line = line from input file -** *keyword[] = list of NULL terminated keywords -** Output: returns index of matching keyword or -** -1 if no match found -** Purpose: determines which keyword appears on input line -**-------------------------------------------------------------- -*/ -{ - int i = 0; - while (keyword[i] != NULL) - { - if (match(line,keyword[i])) return(i); - i++; - } - return(-1); -} /* end of findmatch */ - - - -int match(char *str, char *substr) -/* -**-------------------------------------------------------------- -** Input: *str = string being searched -** *substr = substring being searched for -** Output: returns 1 if substr found in str, 0 if not -** Purpose: sees if substr matches any part of str -** -** (Not case sensitive) -**-------------------------------------------------------------- -*/ -{ - int i,j; - -/*** Updated 9/7/00 ***/ -/* Fail if substring is empty */ - if (!substr[0]) return(0); - -/* Skip leading blanks of str. */ - for (i=0; str[i]; i++) - if (str[i] != ' ') break; - -/* Check if substr matches remainder of str. */ - for (i=i,j=0; substr[j]; i++,j++) - if (!str[i] || UCHAR(str[i]) != UCHAR(substr[j])) - return(0); - return(1); -} /* end of match */ - - -/*** Updated 10/25/00 ***/ -/* The gettokens function has been totally re-written. */ - -int gettokens(char *s) -/* -**-------------------------------------------------------------- -** Input: *s = string to be tokenized -** Output: returns number of tokens in s -** Purpose: scans string for tokens, saving pointers to them -** in module global variable Tok[] -** -** Tokens can be separated by the characters listed in SEPSTR -** (spaces, tabs, newline, carriage return) which is defined -** in TYPES.H. Text between quotes is treated as a single token. -**-------------------------------------------------------------- -*/ -{ - int len, m, n; - char *c; - -/* Begin with no tokens */ - for (n=0; n 0 && n < MAXTOKS) - { - m = (int)strcspn(s,SEPSTR); /* Find token length */ - len -= m+1; /* Update length of s */ - if (m == 0) s++; /* No token found */ - else - { - if (*s == '"') /* Token begins with quote */ - { - s++; /* Start token after quote */ - m = (int)strcspn(s,"\"\n\r"); /* Find end quote (or EOL) */ - } - s[m] = '\0'; /* Null-terminate the token */ - Tok[n] = s; /* Save pointer to token */ - n++; /* Update token count */ - s += m+1; /* Begin next token */ - } - } - return(n); -} /* End of gettokens */ - - -double hour(char *time, char *units) -/* -**--------------------------------------------------------- -** Input: *time = string containing a time value -** *units = string containing time units -** Output: returns numerical value of time in hours, -** or -1 if an error occurs -** Purpose: converts time from units to hours -**--------------------------------------------------------- -*/ -{ - int n; - double y[3]; - char *s; - -/* Separate clock time into hrs, min, sec. */ - for (n=0; n<3; n++) y[n] = 0.0; - n = 0; - s = strtok(time,":"); - while (s != NULL && n <= 3) - { - if (!getfloat(s,&y[n])) return(-1.0); - s = strtok(NULL,":"); - n++; - } - -/* If decimal time with units attached then convert to hours. */ - if (n == 1) - { - /*if (units[0] == '\0') return(y[0]);*/ - if (strlen(units) == 0) return(y[0]); - if (match(units,w_SECONDS)) return(y[0]/3600.0); - if (match(units,w_MINUTES)) return(y[0]/60.0); - if (match(units,w_HOURS)) return(y[0]); - if (match(units,w_DAYS)) return(y[0]*24.0); - } - -/* Convert hh:mm:ss format to decimal hours */ - if (n > 1) y[0] = y[0] + y[1]/60.0 + y[2]/3600.0; - -/* If am/pm attached then adjust hour accordingly */ -/* (12 am is midnight, 12 pm is noon) */ - if (units[0] == '\0') return(y[0]); - if (match(units,w_AM)) - { - if (y[0] >= 13.0) return(-1.0); - if (y[0] >= 12.0) return(y[0]-12.0); - else return(y[0]); - } - if (match(units,w_PM)) - { - if (y[0] >= 13.0) return(-1.0); - if (y[0] >= 12.0) return(y[0]); - else return(y[0]+12.0); - } - return(-1.0); -} /* end of hour */ - - -int getfloat(char *s, double *y) -/* -**----------------------------------------------------------- -** Input: *s = character string -** Output: *y = floating point number -** returns 1 if conversion successful, 0 if not -** Purpose: converts string to floating point number -**----------------------------------------------------------- -*/ -{ - char *endptr; - *y = (double) strtod(s,&endptr); - if (*endptr > 0) return(0); - return(1); -} - - -int setreport(char *s) -/* -**----------------------------------------------------------- -** Input: *s = report format command -** Output: none -** Returns: error code -** Purpose: processes a report formatting command -** issued by the ENsetreport function -**----------------------------------------------------------- -*/ -{ - Ntokens = gettokens(s); - return(reportdata()); -} - - -void inperrmsg(int err, int sect, char *line) -/* -**------------------------------------------------------------- -** Input: err = error code -** sect = input data section -** *line = line from input file -** Output: none -** Purpose: displays input error message -**------------------------------------------------------------- -*/ -{ - char fmt[MAXMSG+1]; - char id[MAXMSG+1]; - -/* Retrieve ID label of object with input error */ -/* (No ID used for CONTROLS or REPORT sections).*/ - if (sect == _CONTROLS || sect == _REPORT) strcpy(id,""); - else if (sect == _ENERGY) strcpy(id,Tok[1]); - else strcpy(id,Tok[0]); - -/* Copy error messge to string variable fmt */ - switch (err) - { - case 201: strcpy(fmt,ERR201); break; - case 202: strcpy(fmt,ERR202); break; - case 203: strcpy(fmt,ERR203); break; - case 204: strcpy(fmt,ERR204); break; - case 205: strcpy(fmt,ERR205); break; - case 206: strcpy(fmt,ERR206); break; - case 207: strcpy(fmt,ERR207); break; - case 208: strcpy(fmt,ERR208); break; - case 209: strcpy(fmt,ERR209); break; - case 210: strcpy(fmt,ERR210); break; - case 211: strcpy(fmt,ERR211); break; - case 212: strcpy(fmt,ERR212); break; - case 213: strcpy(id,""); - strcpy(fmt,ERR213); break; - case 214: strcpy(id,""); - strcpy(fmt,ERR214); break; - case 215: strcpy(fmt,ERR215); break; - case 216: strcpy(fmt,ERR216); break; - case 217: strcpy(fmt,ERR217); break; - case 219: strcpy(fmt,ERR219); break; - case 220: strcpy(fmt,ERR220); break; - -/*** Updated 10/25/00 ***/ - case 222: strcpy(fmt,ERR222); break; - - default: return; - } - -/* Write error message to Report file */ - sprintf(Msg,fmt,RptSectTxt[sect],id); - writeline(Msg); - -/* Echo input line for syntax errors, and */ -/* errors in CONTROLS and OPTIONS sections. */ - if (sect == _CONTROLS || err == 201 || err == 213) writeline(line); - else writeline(""); -} - -/********************** END OF INPUT2.C ************************/ - - +/* +********************************************************************** + +INPUT2.C -- Input data file interpreter for EPANET + +VERSION: 2.00 +DATE: 5/30/00 + 9/7/00 + 10/25/00 +AUTHOR: L. Rossman + US EPA - NRMRL + +This module reads and interprets the input data from file InFile. + +The entry points for this module are: + netsize() -- called from ENopen() in EPANET.C + readdata() -- called from getdata() in INPUT1.C + +The following utility functions are all called from INPUT3.C + addnodeID() + addlinkID() + findID() + getfloat() + +********************************************************************** +*/ + +#include +#include +#include +#ifndef __APPLE__ +#include +#endif +#include "hash.h" +#include "text.h" +#include "types.h" +#include "epanet2.h" +#include "funcs.h" +#include +#define EXTERN extern +#include "vars.h" + +#define MAXERRS 10 /* Max. input errors reported */ + + +/* Defined in enumstxt.h in EPANET.C */ +extern char *SectTxt[]; /* Input section keywords */ + +int netsize(EN_Project *pr) +/* +**-------------------------------------------------------------- +** Input: none +** Output: returns error code +** Purpose: determines number of system components +**-------------------------------------------------------------- +*/ +{ + parser_data_t *par = &pr->parser; + + char line[MAXLINE + 1]; /* Line from input data file */ + char *tok; /* First token of line */ + int sect, newsect; /* Input data sections */ + int errcode = 0; /* Error code */ + + /* Initialize network component counts */ + par->MaxJuncs = 0; + par->MaxTanks = 0; + par->MaxPipes = 0; + par->MaxPumps = 0; + par->MaxValves = 0; + par->MaxControls = 0; + par->MaxRules = 0; + par->MaxCurves = 0; + sect = -1; + + /* Add a default pattern 0 */ + par->MaxPats = -1; + addpattern(par,""); + + if (par->InFile == NULL) { + return (0); + } + + /* Make pass through data file counting number of each component */ + while (fgets(line, MAXLINE, par->InFile) != NULL) { + /* Skip blank lines & those beginning with a comment */ + tok = strtok(line, SEPSTR); + if (tok == NULL) + continue; + if (*tok == ';') + continue; + + /* Check if line begins with a new section heading */ + if (tok[0] == '[') { + newsect = findmatch(tok, SectTxt); + if (newsect >= 0) { + sect = newsect; + if (sect == _END) + break; + continue; + } else + continue; + } + + /* Add to count of current component */ + switch (sect) { + case _JUNCTIONS: + par->MaxJuncs++; + break; + case _RESERVOIRS: + case _TANKS: + par->MaxTanks++; + break; + case _PIPES: + par->MaxPipes++; + break; + case _PUMPS: + par->MaxPumps++; + break; + case _VALVES: + par->MaxValves++; + break; + case _CONTROLS: + par->MaxControls++; + break; + case _RULES: + addrule(par,tok); + break; /* See RULES.C */ + case _PATTERNS: + errcode = addpattern(par, tok); + break; + case _CURVES: + errcode = addcurve(par, tok); + break; + } + if (errcode) + break; + } + + par->MaxNodes = par->MaxJuncs + par->MaxTanks; + par->MaxLinks = par->MaxPipes + par->MaxPumps + par->MaxValves; + if (par->MaxPats < 1) + par->MaxPats = 1; + if (!errcode) { + if (par->MaxJuncs < 1) + errcode = 223; /* Not enough nodes */ + else if (par->MaxTanks == 0) + errcode = 224; /* No tanks */ + } + return (errcode); +} /* End of netsize */ + +int readdata(EN_Project *pr) +/* +**-------------------------------------------------------------- +** Input: none +** Output: returns error code +** Purpose: reads contents of input data file +**-------------------------------------------------------------- +*/ +{ + EN_Network *net = &pr->network; + parser_data_t *par = &pr->parser; + + char line[MAXLINE + 1], /* Line from input data file */ + wline[MAXLINE + 1]; /* Working copy of input line */ + int sect, newsect, /* Data sections */ + errcode = 0, /* Error code */ + inperr, errsum; /* Error code & total error count */ + + /* Allocate input buffer */ + par->X = (double *)calloc(MAXTOKS, sizeof(double)); + ERRCODE(MEMCHECK(par->X)); + + if (!errcode) { + + /* Initialize number of network components */ + par->Ntitle = 0; + net->Nnodes = 0; + net->Njuncs = 0; + net->Ntanks = 0; + net->Nlinks = 0; + net->Npipes = 0; + net->Npumps = 0; + net->Nvalves = 0; + net->Ncontrols = 0; + net->Nrules = 0; + net->Ncurves = par->MaxCurves; + net->Npats = par->MaxPats; + par->PrevPat = NULL; + par->PrevCurve = NULL; + + sect = -1; + errsum = 0; + + /* Read each line from input file. */ + while (fgets(line, MAXLINE, par->InFile) != NULL) { + + /* Make copy of line and scan for tokens */ + strcpy(wline, line); + par->Ntokens = gettokens(wline, par->Tok, MAXTOKS, par->Comment); + + /* Skip blank lines and comments */ + if (par->Ntokens == 0) + continue; + if (*par->Tok[0] == ';') + continue; + + /* Check if max. length exceeded */ + if (strlen(line) >= MAXLINE) { + char errMsg[MAXMSG+1]; + EN_geterror(214, errMsg, MAXMSG); + sprintf(pr->Msg, "%s section: %s", errMsg, SectTxt[sect]); + writeline(pr, pr->Msg); + writeline(pr, line); + errsum++; + } + + /* Check if at start of a new input section */ + if (par->Tok[0][0] == '[') { + newsect = findmatch(par->Tok[0], SectTxt); + if (newsect >= 0) { + sect = newsect; + if (sect == _END) + break; + continue; + } else { + inperrmsg(pr, 201, sect, line); + errsum++; + break; + } + } + + /* Otherwise process next line of input in current section */ + else { + if (sect >= 0) // for cases were no section is present on the top of the + // input file + { + inperr = newline(pr, sect, line); + if (inperr > 0) { + inperrmsg(pr,inperr, sect, line); + errsum++; + } + } else { + errcode = 200; + break; + } + } + + /* Stop if reach end of file or max. error count */ + if (errsum == MAXERRS) + break; + } /* End of while */ + + /* Check for errors */ + if (errsum > 0) + errcode = 200; + } + + /* Check for unlinked nodes */ + if (!errcode) + errcode = unlinked(pr); + + /* Get pattern & curve data from temp. lists */ + if (!errcode) + errcode = getpatterns(pr); + if (!errcode) + errcode = getcurves(pr); + if (!errcode) + errcode = getpumpparams(pr); + + /* Free input buffer */ + free(par->X); + return (errcode); + +} /* End of readdata */ + +int newline(EN_Project *pr, int sect, char *line) +/* +**-------------------------------------------------------------- +** Input: sect = current section of input file +** *line = line read from input file +** Output: returns error code or 0 if no error found +** Purpose: processes a new line of data from input file +**-------------------------------------------------------------- +*/ +{ + parser_data_t *par = &pr->parser; + + int n; + switch (sect) { + case _TITLE: + if (par->Ntitle < 3) { + n = (int)strlen(line); + if (line[n - 1] == 10) + line[n - 1] = ' '; + strncpy(pr->Title[par->Ntitle], line, MAXMSG); + par->Ntitle++; + } + return (0); + case _JUNCTIONS: + return (juncdata(pr)); + case _RESERVOIRS: + case _TANKS: + return (tankdata(pr)); + case _PIPES: + return (pipedata(pr)); + case _PUMPS: + return (pumpdata(pr)); + case _VALVES: + return (valvedata(pr)); + case _PATTERNS: + return (patterndata(pr)); + case _CURVES: + return (curvedata(pr)); + case _DEMANDS: + return (demanddata(pr)); + case _CONTROLS: + return (controldata(pr)); + case _RULES: + return (ruledata(pr)); /* See RULES.C */ + case _SOURCES: + return (sourcedata(pr)); + case _EMITTERS: + return (emitterdata(pr)); + case _QUALITY: + return (qualdata(pr)); + case _STATUS: + return (statusdata(pr)); + case _ROUGHNESS: + return (0); + case _ENERGY: + return (energydata(pr)); + case _REACTIONS: + return (reactdata(pr)); + case _MIXING: + return (mixingdata(pr)); + case _REPORT: + return (reportdata(pr)); + case _TIMES: + return (timedata(pr)); + case _OPTIONS: + return (optiondata(pr)); + + /* Data in these sections are not used for any computations */ + case _COORDS: + if (par->Coordflag == TRUE) { + return (coordata(pr)); + } else + return (0); + case _LABELS: + return (0); + case _TAGS: + return (0); + case _VERTICES: + return (0); + case _BACKDROP: + return (0); + } + return (201); +} /* end of newline */ + +int getpumpparams(EN_Project *pr) +/* +**------------------------------------------------------------- +** Input: none +** Output: returns error code +** Purpose: computes & checks pump curve parameters +**-------------------------------------------------------------- +*/ +{ + int i, j = 0, k, m, n = 0; + double a, b, c, h0 = 0.0, h1 = 0.0, h2 = 0.0, q1 = 0.0, q2 = 0.0; + char errMsg[MAXMSG+1]; + Spump *pump; + Slink *link; + Scurve *curve; + + EN_Network *net = &pr->network; + + for (i = 1; i <= net->Npumps; i++) { + pump = &net->Pump[i]; + k = pump->Link; + link = &net->Link[k]; + if (pump->Ptype == CONST_HP) { /* Constant Hp pump */ + pump->H0 = 0.0; + pump->R = -8.814 * link->Km; + pump->N = -1.0; + pump->Hmax = BIG; /* No head limit */ + pump->Qmax = BIG; /* No flow limit */ + pump->Q0 = 1.0; /* Init. flow = 1 cfs */ + continue; + } + else if (pump->Ptype == NOCURVE) { /* Pump curve specified */ + j = pump->Hcurve; /* Get index of head curve */ + if (j == 0) { /* Error: No head curve */ + EN_geterror(226, errMsg, MAXMSG); + sprintf(pr->Msg, "%s link: %s", errMsg, link->ID); + writeline(pr, pr->Msg); + return (200); + } + curve = &net->Curve[j]; + n = curve->Npts; + if (n == 1) { /* Only a single h-q point supplied so use generic */ + pump->Ptype = POWER_FUNC; /* power function curve. */ + q1 = curve->X[0]; + h1 = curve->Y[0]; + h0 = 1.33334 * h1; + q2 = 2.0 * q1; + h2 = 0.0; + } else if (n == 3 && curve->X[0] == 0.0) /* 3 h-q points supplied with */ + { /* shutoff head so use fitted */ + pump->Ptype = POWER_FUNC; /* power function curve. */ + h0 = curve->Y[0]; + q1 = curve->X[1]; + h1 = curve->Y[1]; + q2 = curve->X[2]; + h2 = curve->Y[2]; + } + else { // use a custom curve, referenced by ID + pump->Ptype = CUSTOM; /* Else use custom pump curve.*/ + // at this point, j is set to that curve's index. + } + + /* Compute shape factors & limits of power function pump curves */ + if (pump->Ptype == POWER_FUNC) { + if (!powercurve(h0, h1, h2, q1, q2, &a, &b, &c)) { /* Error: Invalid curve */ + EN_geterror(227, errMsg, MAXMSG); + sprintf(pr->Msg, "%s link: %s", errMsg, link->ID); + writeline(pr, pr->Msg); + return (200); + } else { + pump->H0 = -a; + pump->R = -b; + pump->N = c; + pump->Q0 = q1; + pump->Qmax = pow((-a / b), (1.0 / c)); + pump->Hmax = h0; + } + } + } + + /* Assign limits to custom pump curves */ + if (pump->Ptype == CUSTOM) { + curve = &net->Curve[j]; + for (m = 1; m < n; m++) { + if (curve->Y[m] >= curve->Y[m - 1]) { /* Error: Invalid curve */ + EN_geterror(227, errMsg, MAXMSG); + sprintf(pr->Msg, "%s link: %s", errMsg, link->ID); + writeline(pr, pr->Msg); + return (200); + } + } + pump->Qmax = curve->X[n - 1]; + pump->Q0 = (curve->X[0] + pump->Qmax) / 2.0; + pump->Hmax = curve->Y[0]; + } + } /* Next pump */ + return (0); +} + +int addnodeID(EN_Network *net, int n, char *id) +/* +**------------------------------------------------------------- +** Input: n = node index +** id = ID label +** Output: returns 0 if ID already in use, 1 if not +** Purpose: adds a node ID to the Node Hash Table +**-------------------------------------------------------------- +*/ +{ + if (findnode(net,id)) { + return (0); /* see EPANET.C */ + } + strncpy(net->Node[n].ID, id, MAXID); + ENHashTableInsert(net->NodeHashTable, net->Node[n].ID, n); /* see HASH.C */ + return (1); +} + +int addlinkID(EN_Network *net, int n, char *id) +/* +**------------------------------------------------------------- +** Input: n = link index +** id = ID label +** Output: returns 0 if ID already in use, 1 if not +** Purpose: adds a link ID to the Link Hash Table +**-------------------------------------------------------------- +*/ +{ + if (findlink(net,id)) { + return (0); /* see EPANET.C */ + } + strncpy(net->Link[n].ID, id, MAXID); + ENHashTableInsert(net->LinkHashTable, net->Link[n].ID, n); /* see HASH.C */ + return (1); +} + +int addpattern(parser_data_t *par, char *id) +/* +**------------------------------------------------------------- +** Input: id = pattern ID label +** Output: returns error code +** Purpose: adds a new pattern to the database +**-------------------------------------------------------------- +*/ +{ + + STmplist *p; + + /* Check if ID is same as last one processed */ + if (par->Patlist != NULL && strcmp(id, par->Patlist->ID) == 0) { + return (0); + } + + /* Check that pattern was not already created */ + if (findID(id, par->Patlist) == NULL) { + + /* Update pattern count & create new list element */ + (par->MaxPats)++; + p = (STmplist *)malloc(sizeof(STmplist)); + if (p == NULL) + return (101); + + /* Initialize list element properties */ + else { + p->i = par->MaxPats; + strncpy(p->ID, id, MAXID); + p->x = NULL; + p->y = NULL; + p->next = par->Patlist; + par->Patlist = p; + } + } + return (0); +} + +int addcurve(parser_data_t *par, char *id) +/* +**------------------------------------------------------------- +** Input: id = curve ID label +** Output: returns error code +** Purpose: adds a new curve to the database +**-------------------------------------------------------------- +*/ +{ + STmplist *c; + + /* Check if ID is same as last one processed */ + if (par->Curvelist != NULL && strcmp(id, par->Curvelist->ID) == 0) + return (0); + + /* Check that curve was not already created */ + if (findID(id, par->Curvelist) == NULL) { + + /* Update curve count & create new list element */ + (par->MaxCurves)++; + c = (STmplist *)malloc(sizeof(STmplist)); + if (c == NULL) + return (101); + + /* Initialize list element properties */ + else { + c->i = par->MaxCurves; + strncpy(c->ID, id, MAXID); + c->x = NULL; + c->y = NULL; + c->next = par->Curvelist; + par->Curvelist = c; + } + } + return (0); +} + +STmplist *findID(char *id, STmplist *list) +/* +**------------------------------------------------------------- +** Input: id = ID label +** list = pointer to head of a temporary list +** Output: returns list item with requested ID label +** Purpose: searches for item in temporary list +**------------------------------------------------------------- +*/ +{ + STmplist *item; + for (item = list; item != NULL; item = item->next) { + if (strcmp(item->ID, id) == 0) { + return (item); + } + } + return (NULL); +} + +int unlinked(EN_Project *pr) +/* +**-------------------------------------------------------------- +** Input: none +** Output: returns error code if any unlinked junctions found +** Purpose: checks for unlinked junctions in network +** +** NOTE: unlinked tanks have no effect on computations. +**-------------------------------------------------------------- +*/ +{ + EN_Network *net = &pr->network; + int *marked; + int i, err, errcode; + char errMsg[MAXMSG+1]; + + errcode = 0; + err = 0; + marked = (int *)calloc(net->Nnodes + 1, sizeof(int)); + ERRCODE(MEMCHECK(marked)); + if (!errcode) { + memset(marked, 0, (net->Nnodes + 1) * sizeof(int)); + for (i = 1; i <= net->Nlinks; i++) /* Mark end nodes of each link */ + { + marked[net->Link[i].N1]++; + marked[net->Link[i].N2]++; + } + for (i = 1; i <= net->Njuncs; i++) /* Check each junction */ + { + if (marked[i] == 0) /* If not marked then error */ + { + err++; + EN_geterror(233, errMsg, MAXMSG); + sprintf(pr->Msg, "%s node: %s", errMsg, net->Node[i].ID); + writeline(pr, pr->Msg); + } + if (err >= MAXERRS) + break; + } + if (err > 0) + errcode = 200; + } + free(marked); + return (errcode); +} /* End of unlinked */ + +int getpatterns(EN_Project *pr) +/* +**----------------------------------------------------------- +** Input: none +** Output: returns error code +** Purpose: retrieves pattern data from temporary linked list +**------------------------------------------------------------- +*/ +{ + int i, j; + SFloatlist *f; + STmplist *pat; + + EN_Network *net = &pr->network; + hydraulics_t *hyd = &pr->hydraulics; + parser_data_t *par = &pr->parser; + + /* Start at head of list */ + pat = par->Patlist; + + /* Traverse list of patterns */ + while (pat != NULL) { + + /* Get index of current pattern in Pattern array */ + i = pat->i; + + /* Check if this is the default pattern */ + if (strcmp(pat->ID, par->DefPatID) == 0) { + hyd->DefPat = i; + } + if (i >= 0 && i <= par->MaxPats) { + /* Save pattern ID */ + + Spattern *pattern = &net->Pattern[i]; + + strcpy(pattern->ID, pat->ID); + + /* Give pattern a length of at least 1 */ + if (pattern->Length == 0) + pattern->Length = 1; + pattern->F = (double *)calloc(pattern->Length, sizeof(double)); + if (pattern->F == NULL) + return (101); + + /* Start at head of pattern multiplier list */ + /* (which holds multipliers in reverse order)*/ + f = pat->x; + j = pattern->Length - 1; + + /* Use at least one multiplier equal to 1.0 */ + if (f == NULL) + pattern->F[0] = 1.0; + + /* Traverse list, storing multipliers in Pattern array */ + else + while (f != NULL && j >= 0) { + pattern->F[j] = f->value; + f = f->next; + j--; + } + } + pat = pat->next; + } + return (0); +} + +int getcurves(EN_Project *pr) +/* +**----------------------------------------------------------- +** Input: none +** Output: returns error code +** Purpose: retrieves curve data from temporary linked list +**----------------------------------------------------------- +*/ +{ + EN_Network *net = &pr->network; + parser_data_t *par = &pr->parser; + + int i, j; + double x; + SFloatlist *fx, *fy; + STmplist *c; + + /* Start at head of curve list */ + c = par->Curvelist; + + /* Traverse list of curves */ + while (c != NULL) { + i = c->i; + if (i >= 1 && i <= par->MaxCurves) { + Scurve *curve = &net->Curve[i]; + + /* Save curve ID */ + strcpy(curve->ID, c->ID); + + /* Check that curve has data points */ + if (curve->Npts <= 0) { + char errMsg[MAXMSG+1]; + EN_geterror(230, errMsg, MAXMSG); + sprintf(pr->Msg, "%s curve: %s", errMsg, curve->ID); + writeline(pr, pr->Msg); + return (200); + } + + /* Allocate memory for curve data */ + curve->X = (double *)calloc(curve->Npts, sizeof(double)); + curve->Y = (double *)calloc(curve->Npts, sizeof(double)); + if (curve->X == NULL || curve->Y == NULL) + return (101); + + /* Traverse list of x,y data */ + x = BIG; + fx = c->x; + fy = c->y; + j = curve->Npts - 1; + while (fx != NULL && fy != NULL && j >= 0) { + + /* Check that x data is in ascending order */ + if (fx->value >= x) { + char errMsg[MAXMSG+1]; + EN_geterror(230, errMsg, MAXMSG); + sprintf(pr->Msg, "%s node: %s", errMsg, curve->ID); + writeline(pr, pr->Msg); + return (200); + } + x = fx->value; + + /* Save x,y data in Curve structure */ + curve->X[j] = fx->value; + fx = fx->next; + curve->Y[j] = fy->value; + fy = fy->next; + j--; + } + } + c = c->next; + } + return (0); +} + +int findmatch(char *line, char *keyword[]) +/* +**-------------------------------------------------------------- +** Input: *line = line from input file +** *keyword[] = list of NULL terminated keywords +** Output: returns index of matching keyword or +** -1 if no match found +** Purpose: determines which keyword appears on input line +**-------------------------------------------------------------- +*/ +{ + int i = 0; + while (keyword[i] != NULL) { + if (match(line, keyword[i])) + return (i); + i++; + } + return (-1); +} /* end of findmatch */ + +int match(const char *str, const char *substr) +/* +**-------------------------------------------------------------- +** Input: *str = string being searched +** *substr = substring being searched for +** Output: returns 1 if substr found in str, 0 if not +** Purpose: sees if substr matches any part of str +** +** (Not case sensitive) +**-------------------------------------------------------------- +*/ +{ + int i, j; + + /*** Updated 9/7/00 ***/ + /* Fail if substring is empty */ + if (!substr[0]) + return (0); + + /* Skip leading blanks of str. */ + for (i = 0; str[i]; i++) + if (str[i] != ' ') + break; + + /* Check if substr matches remainder of str. */ + for (i = i, j = 0; substr[j]; i++, j++) + if (!str[i] || UCHAR(str[i]) != UCHAR(substr[j])) + return (0); + return (1); +} /* end of match */ + +int gettokens(char *s, char** Tok, int maxToks, char *comment) +/* + **-------------------------------------------------------------- + ** Input: *s = string to be tokenized + ** Output: returns number of tokens in s + ** Purpose: scans string for tokens, saving pointers to them + ** in module global variable Tok[] + ** + ** Tokens can be separated by the characters listed in SEPSTR + ** (spaces, tabs, newline, carriage return) which is defined + ** in TYPES.H. Text between quotes is treated as a single token. + **-------------------------------------------------------------- + */ +{ + int m, n; + size_t len; + char *c, *c2; + + // clear comment + comment[0] = '\0'; + + /* Begin with no tokens */ + for (n=0; n 0) { + len = strcspn(c2, "\n\r"); + len = MIN(len, MAXMSG); + strncpy(comment, c2, len); + comment[MIN(len,MAXMSG)] = '\0'; + } + } + *c = '\0'; + } + len = (int)strlen(s); + + /* Scan s for tokens until nothing left */ + while (len > 0 && n < MAXTOKS) + { + m = (int)strcspn(s,SEPSTR); /* Find token length */ + len -= m+1; /* Update length of s */ + if (m == 0) s++; /* No token found */ + else + { + if (*s == '"') /* Token begins with quote */ + { + s++; /* Start token after quote */ + m = (int)strcspn(s,"\"\n\r"); /* Find end quote (or EOL) */ + } + s[m] = '\0'; /* Null-terminate the token */ + Tok[n] = s; /* Save pointer to token */ + n++; /* Update token count */ + s += m+1; /* Begin next token */ + } + } + return(n); +} + +double hour(char *time, char *units) +/* +**--------------------------------------------------------- +** Input: *time = string containing a time value +** *units = string containing time units +** Output: returns numerical value of time in hours, +** or -1 if an error occurs +** Purpose: converts time from units to hours +**--------------------------------------------------------- +*/ +{ + int n; + double y[3]; + char *s; + + /* Separate clock time into hrs, min, sec. */ + for (n = 0; n < 3; n++) + y[n] = 0.0; + n = 0; + s = strtok(time, ":"); + while (s != NULL && n <= 3) { + if (!getfloat(s, &y[n])) + return (-1.0); + s = strtok(NULL, ":"); + n++; + } + + /* If decimal time with units attached then convert to hours. */ + if (n == 1) { + /*if (units[0] == '\0') return(y[0]);*/ + if (strlen(units) == 0) + return (y[0]); + if (match(units, w_SECONDS)) + return (y[0] / 3600.0); + if (match(units, w_MINUTES)) + return (y[0] / 60.0); + if (match(units, w_HOURS)) + return (y[0]); + if (match(units, w_DAYS)) + return (y[0] * 24.0); + } + + /* Convert hh:mm:ss format to decimal hours */ + if (n > 1) + y[0] = y[0] + y[1] / 60.0 + y[2] / 3600.0; + + /* If am/pm attached then adjust hour accordingly */ + /* (12 am is midnight, 12 pm is noon) */ + if (units[0] == '\0') + return (y[0]); + if (match(units, w_AM)) { + if (y[0] >= 13.0) + return (-1.0); + if (y[0] >= 12.0) + return (y[0] - 12.0); + else + return (y[0]); + } + if (match(units, w_PM)) { + if (y[0] >= 13.0) + return (-1.0); + if (y[0] >= 12.0) + return (y[0]); + else + return (y[0] + 12.0); + } + return (-1.0); +} /* end of hour */ + +int getfloat(char *s, double *y) +/* +**----------------------------------------------------------- +** Input: *s = character string +** Output: *y = floating point number +** returns 1 if conversion successful, 0 if not +** Purpose: converts string to floating point number +**----------------------------------------------------------- +*/ +{ + char *endptr; + *y = (double)strtod(s, &endptr); + if (*endptr > 0) + return (0); + return (1); +} + +int setreport(EN_Project *pr, char *s) +/* +**----------------------------------------------------------- +** Input: *s = report format command +** Output: none +** Returns: error code +** Purpose: processes a report formatting command +** issued by the ENsetreport function +**----------------------------------------------------------- +*/ +{ + parser_data_t *par = &pr->parser; + par->Ntokens = gettokens(s, par->Tok, MAXTOKS, par->Comment); + return (reportdata(pr)); +} + +void inperrmsg(EN_Project *pr, int err, int sect, char *line) +/* +**------------------------------------------------------------- +** Input: err = error code +** sect = input data section +** *line = line from input file +** Output: none +** Purpose: displays input error message +**------------------------------------------------------------- +*/ +{ + parser_data_t *par = &pr->parser; + + char errStr[MAXMSG + 1]; + char id[MAXMSG + 1]; + + EN_geterror(err, errStr, MAXMSG); + + /* get text for error message */ + sprintf(pr->Msg, "%s - section: %s", errStr, SectTxt[sect]); + + // append ID? + /* Retrieve ID label of object with input error */ + /* (No ID used for CONTROLS or REPORT sections).*/ + switch (sect) { + case _CONTROLS: + case _REPORT: + // don't append + break; + case _ENERGY: + sprintf(id, " id: %s", par->Tok[1]); + break; + default: + sprintf(id, " id: %s", par->Tok[0]); + break; + } + + strcat(pr->Msg, id); + writeline(pr, pr->Msg); + + /* Echo input line for syntax errors, and */ + /* errors in CONTROLS and OPTIONS sections. */ + if (sect == _CONTROLS || err == 201 || err == 213) + writeline(pr, line); + else + writeline(pr, ""); +} + +/********************** END OF INPUT2.C ************************/ diff --git a/src/input3.c b/src/input3.c old mode 100755 new mode 100644 index 4ab3487..37cb05b --- a/src/input3.c +++ b/src/input3.c @@ -1,1895 +1,2195 @@ -/* -********************************************************************** - -INPUT3.C -- Input data parser for EPANET - -VERSION: 2.00 -DATE: 5/30/00 - 9/7/00 - 10/25/00 - 3/1/01 - 6/24/02 - 8/15/07 (2.00.11) - 2/14/08 (2.00.12) -AUTHOR: L. Rossman - US EPA - NRMRL - -This module parses data from each line of input from file InFile. -All functions in this module are called from newline() in INPUT2.C. - -********************************************************************** -*/ - -#include -#include -#include -#ifndef __APPLE__ -#include -#endif -#include -#include "hash.h" -#include "text.h" -#include "types.h" -#include "funcs.h" -#define EXTERN extern -#include "vars.h" - -/* Defined in enumstxt.h in EPANET.C */ -extern char *MixTxt[]; -extern char *Fldname[]; - -/* Defined in INPUT2.C */ -extern char *Tok[MAXTOKS]; -extern STmplist *PrevPat; -extern STmplist *PrevCurve; -extern int Ntokens; - - -int juncdata() -/* -**-------------------------------------------------------------- -** Input: none -** Output: returns error code -** Purpose: processes junction data -** Format: -** [JUNCTIONS] -** id elev. (demand) (demand pattern) -**-------------------------------------------------------------- -*/ -{ - int n, p = 0; - double el,y = 0.0; - Pdemand demand; - STmplist *pat; - -/* Add new junction to data base */ - n = Ntokens; - if (Nnodes == MaxNodes) return(200); - Njuncs++; - Nnodes++; - if (!addnodeID(Njuncs,Tok[0])) return(215); - -/* Check for valid data */ - if (n < 2) return(201); - if (!getfloat(Tok[1],&el)) return(202); - if (n >= 3 && !getfloat(Tok[2],&y)) return(202); - if (n >= 4) - { - pat = findID(Tok[3],Patlist); - if (pat == NULL) return(205); - p = pat->i; - } - -/* Save junction data */ - Node[Njuncs].El = el; - Node[Njuncs].C0 = 0.0; - Node[Njuncs].S = NULL; - Node[Njuncs].Ke = 0.0; - Node[Njuncs].Rpt = 0; - -/* Create a new demand record */ -/*** Updated 6/24/02 ***/ - if (n >= 3) - { - demand = (struct Sdemand *) malloc(sizeof(struct Sdemand)); - if (demand == NULL) return(101); - demand->Base = y; - demand->Pat = p; - demand->next = Node[Njuncs].D; - Node[Njuncs].D = demand; - NodeDemand[Njuncs] = y; - } - else NodeDemand[Njuncs] = MISSING; -/*** end of update ***/ - return(0); -} /* end of juncdata */ - - -int tankdata() -/* -**-------------------------------------------------------------- -** Input: none -** Output: returns error code -** Purpose: processes tank & reservoir data -** Format: -** [RESERVOIRS] -** id elev (pattern) -** [TANKS] -** id elev (pattern) -** id elev initlevel minlevel maxlevel diam (minvol vcurve) -**-------------------------------------------------------------- -*/ -{ - int i, /* Node index */ - n, /* # data items */ - p = 0, /* Fixed grade time pattern index */ - vcurve = 0; /* Volume curve index */ - double el = 0.0, /* Elevation */ - initlevel = 0.0, /* Initial level */ - minlevel = 0.0, /* Minimum level */ - maxlevel = 0.0, /* Maximum level */ - minvol = 0.0, /* Minimum volume */ - diam = 0.0, /* Diameter */ - area; /* X-sect. area */ - STmplist *t; - -/* Add new tank to data base */ - n = Ntokens; - if (Ntanks == MaxTanks - || Nnodes == MaxNodes) return(200); - Ntanks++; - Nnodes++; - i = MaxJuncs + Ntanks; /* i = node index. */ - if (!addnodeID(i,Tok[0])) return(215); /* Add ID to database. */ - -/* Check for valid data */ - if (n < 2) return(201); /* Too few fields. */ - if (!getfloat(Tok[1],&el)) return(202); /* Read elevation */ - if (n <= 3) /* Tank is reservoir.*/ - { - if (n == 3) /* Pattern supplied */ - { - t = findID(Tok[2],Patlist); - if (t == NULL) return(205); - p = t->i; - } - } - else if (n < 6) return(201); /* Too few fields for tank.*/ - else - { - /* Check for valid input data */ - if (!getfloat(Tok[2],&initlevel)) return(202); - if (!getfloat(Tok[3],&minlevel)) return(202); - if (!getfloat(Tok[4],&maxlevel)) return(202); - if (!getfloat(Tok[5],&diam)) return(202); - if (diam < 0.0) return(202); - if (n >= 7 - && !getfloat(Tok[6],&minvol)) return(202); - - /* If volume curve supplied check it exists */ - if (n == 8) - { - t = findID(Tok[7],Curvelist); - if (t == NULL) return(202); - vcurve = t->i; - } - } - - Node[i].Rpt = 0; - Node[i].El = el; /* Elevation. */ - Node[i].C0 = 0.0; /* Init. quality. */ - Node[i].S = NULL; /* WQ source data */ - Node[i].Ke = 0.0; /* Emitter coeff. */ - Tank[Ntanks].Node = i; /* Node index. */ - Tank[Ntanks].H0 = initlevel; /* Init. level. */ - Tank[Ntanks].Hmin = minlevel; /* Min. level. */ - Tank[Ntanks].Hmax = maxlevel; /* Max level. */ - Tank[Ntanks].A = diam; /* Diameter. */ - Tank[Ntanks].Pat = p; /* Fixed grade pattern. */ - Tank[Ntanks].Kb = MISSING; /* Reaction coeff. */ - /* - ******************************************************************* - NOTE: The min, max, & initial volumes set here are based on a - nominal tank diameter. They will be modified in INPUT1.C if - a volume curve is supplied for this tank. - ******************************************************************* - */ - area = PI*SQR(diam)/4.0; - Tank[Ntanks].Vmin = area*minlevel; - if (minvol > 0.0) Tank[Ntanks].Vmin = minvol; - Tank[Ntanks].V0 = Tank[Ntanks].Vmin + area*(initlevel - minlevel); - Tank[Ntanks].Vmax = Tank[Ntanks].Vmin + area*(maxlevel - minlevel); - - Tank[Ntanks].Vcurve = vcurve; /* Volume curve */ - Tank[Ntanks].MixModel = MIX1; /* Completely mixed */ - Tank[Ntanks].V1max = 1.0; /* Compart. size ratio */ - return(0); -} /* end of tankdata */ - - -int pipedata() -/* -**-------------------------------------------------------------- -** Input: none -** Output: returns error code -** Purpose: processes pipe data -** Format: -** [PIPE] -** id node1 node2 length diam rcoeff (lcoeff) (status) -**-------------------------------------------------------------- -*/ -{ - int j1, /* Start-node index */ - j2, /* End-node index */ - n; /* # data items */ - char type = PIPE, /* Link type */ - status = OPEN; /* Link status */ - double length, /* Link length */ - diam, /* Link diameter */ - rcoeff, /* Roughness coeff. */ - lcoeff = 0.0; /* Minor loss coeff. */ - -/* Add new pipe to data base */ - n = Ntokens; - if (Nlinks == MaxLinks) return(200); - Npipes++; - Nlinks++; - if (!addlinkID(Nlinks,Tok[0])) return(215); - -/* Check for valid data */ - if (n < 6) return(201); - if ((j1 = findnode(Tok[1])) == 0 || - (j2 = findnode(Tok[2])) == 0 - ) return(203); - -/*** Updated 10/25/00 ***/ - if (j1 == j2) return(222); - - if (!getfloat(Tok[3],&length) || - !getfloat(Tok[4],&diam) || - !getfloat(Tok[5],&rcoeff) - ) return(202); - - if (length <= 0.0 || - diam <= 0.0 || - rcoeff <= 0.0 - ) return(202); - - /* Case where either loss coeff. or status supplied */ - if (n == 7) - { - if (match(Tok[6],w_CV)) type = CV; - else if (match(Tok[6],w_CLOSED)) status = CLOSED; - else if (match(Tok[6],w_OPEN)) status = OPEN; - else if (!getfloat(Tok[6],&lcoeff)) return(202); - } - - /* Case where both loss coeff. and status supplied */ - if (n == 8) - { - if (!getfloat(Tok[6],&lcoeff)) return(202); - if (match(Tok[7],w_CV)) type = CV; - else if (match(Tok[7],w_CLOSED)) status = CLOSED; - else if (match(Tok[7],w_OPEN)) status = OPEN; - else return(202); - } - if (lcoeff < 0.0) return(202); - -/* Save pipe data */ - Link[Nlinks].N1 = j1; /* Start-node index */ - Link[Nlinks].N2 = j2; /* End-node index */ - Link[Nlinks].Len = length; /* Length */ - Link[Nlinks].Diam = diam; /* Diameter */ - Link[Nlinks].Kc = rcoeff; /* Rough. coeff */ - Link[Nlinks].Km = lcoeff; /* Loss coeff */ - Link[Nlinks].Kb = MISSING; /* Bulk coeff */ - Link[Nlinks].Kw = MISSING; /* Wall coeff */ - Link[Nlinks].Type = type; /* Link type */ - Link[Nlinks].Stat = status; /* Link status */ - Link[Nlinks].Rpt = 0; /* Report flag */ - return(0); -} /* end of pipedata */ - - -int pumpdata() -/* -**-------------------------------------------------------------- -** Input: none -** Output: returns error code -** Purpose: processes pump data -** Formats: -** [PUMP] -** (Version 1.x Format): -** id node1 node2 power -** id node1 node2 h1 q1 -** id node1 node2 h0 h1 q1 h2 q2 -** (Version 2 Format): -** id node1 node2 KEYWORD value {KEYWORD value ...} -** where KEYWORD = [POWER,HEAD,PATTERN,SPEED] -**-------------------------------------------------------------- -*/ -{ - int j, - j1, /* Start-node index */ - j2, /* End-node index */ - m, n; /* # data items */ - double y; - STmplist *t; /* Pattern record */ - -/* Add new pump to data base */ - n = Ntokens; - if (Nlinks == MaxLinks || - Npumps == MaxPumps - ) return(200); - Nlinks++; - Npumps++; - if (!addlinkID(Nlinks,Tok[0])) return(215); - -/* Check for valid data */ - if (n < 4) return(201); - if ((j1 = findnode(Tok[1])) == 0 || - (j2 = findnode(Tok[2])) == 0 - ) return(203); - -/*** Updated 10/25/00 ***/ - if (j1 == j2) return(222); - -/* Save pump data */ - Link[Nlinks].N1 = j1; /* Start-node index. */ - Link[Nlinks].N2 = j2; /* End-node index. */ - Link[Nlinks].Diam = Npumps; /* Pump index. */ - Link[Nlinks].Len = 0.0; /* Link length. */ - Link[Nlinks].Kc = 1.0; /* Speed factor. */ - Link[Nlinks].Km = 0.0; /* Horsepower. */ - Link[Nlinks].Kb = 0.0; - Link[Nlinks].Kw = 0.0; - Link[Nlinks].Type = PUMP; /* Link type. */ - Link[Nlinks].Stat = OPEN; /* Link status. */ - Link[Nlinks].Rpt = 0; /* Report flag. */ - Pump[Npumps].Link = Nlinks; /* Link index. */ - Pump[Npumps].Ptype = NOCURVE; /* Type of pump curve */ - Pump[Npumps].Hcurve = 0; /* Pump curve index */ - Pump[Npumps].Ecurve = 0; /* Effic. curve index */ - Pump[Npumps].Upat = 0; /* Utilization pattern*/ - Pump[Npumps].Ecost = 0.0; /* Unit energy cost */ - Pump[Npumps].Epat = 0; /* Energy cost pattern*/ - -/* If 4-th token is a number then input follows Version 1.x format */ -/* so retrieve pump curve parameters */ - if (getfloat(Tok[3],&X[0])) - { - m = 1; - for (j=4; ji; - } - else if (match(Tok[m-1],w_PATTERN)) /* Speed/status pattern */ - { - t = findID(Tok[m],Patlist); - if (t == NULL) return(205); - Pump[Npumps].Upat = t->i; - } - else if (match(Tok[m-1],w_SPEED)) /* Speed setting */ - { - if (!getfloat(Tok[m],&y)) return(202); - if (y < 0.0) return(202); - Link[Nlinks].Kc = y; - } - else return(201); - m = m + 2; /* Skip to next keyword token */ - } - return(0); -} /* end of pumpdata */ - - -int valvedata() -/* -**-------------------------------------------------------------- -** Input: none -** Output: returns error code -** Purpose: processes valve data -** Format: -** [VALVE] -** id node1 node2 diam type setting (lcoeff) -**-------------------------------------------------------------- -*/ -{ - int j1, /* Start-node index */ - j2, /* End-node index */ - n; /* # data items */ - char status = ACTIVE, /* Valve status */ - type; /* Valve type */ - double diam = 0.0, /* Valve diameter */ - setting, /* Valve setting */ - lcoeff = 0.0; /* Minor loss coeff. */ - STmplist *t; /* Curve record */ - -/* Add new valve to data base */ - n = Ntokens; - if (Nlinks == MaxLinks || - Nvalves == MaxValves - ) return(200); - Nvalves++; - Nlinks++; - if (!addlinkID(Nlinks,Tok[0])) return(215); - -/* Check for valid data */ - if (n < 6) return(201); - if ((j1 = findnode(Tok[1])) == 0 || - (j2 = findnode(Tok[2])) == 0 - ) return(203); - -/*** Updated 10/25/00 ***/ - if (j1 == j2) return(222); - - if (match(Tok[4],w_PRV)) type = PRV; - else if (match(Tok[4],w_PSV)) type = PSV; - else if (match(Tok[4],w_PBV)) type = PBV; - else if (match(Tok[4],w_FCV)) type = FCV; - else if (match(Tok[4],w_TCV)) type = TCV; - else if (match(Tok[4],w_GPV)) type = GPV; - else return(201); /* Illegal valve type.*/ - if (!getfloat(Tok[3],&diam)) return(202); - if (diam <= 0.0) return(202); /* Illegal diameter.*/ - if (type == GPV) /* Headloss curve for GPV */ - { - t = findID(Tok[5],Curvelist); - if (t == NULL) return(206); - setting = t->i; - -/*** Updated 9/7/00 ***/ - status = OPEN; - - } - else if (!getfloat(Tok[5],&setting)) return(202); - if (n >= 7 && - !getfloat(Tok[6],&lcoeff) - ) return(202); - -/* Check that PRV, PSV, or FCV not connected to a tank & */ -/* check for illegal connections between pairs of valves.*/ - if ((j1 > Njuncs || j2 > Njuncs) && - (type == PRV || type == PSV || type == FCV) - ) return(219); - if (!valvecheck(type,j1,j2)) return(220); - -/* Save valve data */ - Link[Nlinks].N1 = j1; /* Start-node index. */ - Link[Nlinks].N2 = j2; /* End-node index. */ - Link[Nlinks].Diam = diam; /* Valve diameter. */ - Link[Nlinks].Len = 0.0; /* Link length. */ - Link[Nlinks].Kc = setting; /* Valve setting. */ - Link[Nlinks].Km = lcoeff; /* Loss coeff */ - Link[Nlinks].Kb = 0.0; - Link[Nlinks].Kw = 0.0; - Link[Nlinks].Type = type; /* Valve type. */ - Link[Nlinks].Stat = status; /* Valve status. */ - Link[Nlinks].Rpt = 0; /* Report flag. */ - Valve[Nvalves].Link = Nlinks; /* Link index. */ - return(0); -} /* end of valvedata */ - - -int patterndata() -/* -**-------------------------------------------------------------- -** Input: none -** Output: returns error code -** Purpose: processes time pattern data -** Format: -** [PATTERNS] -** id mult1 mult2 ..... -**-------------------------------------------------------------- -*/ -{ - int i,n; - double x; - SFloatlist *f; - STmplist *p; - n = Ntokens - 1; - if (n < 1) return(201); /* Too few values */ - if ( /* Check for new pattern */ - PrevPat != NULL && - strcmp(Tok[0],PrevPat->ID) == 0 - ) p = PrevPat; - else p = findID(Tok[0],Patlist); - if (p == NULL) return(205); - for (i=1; i<=n; i++) /* Add multipliers to list */ - { - if (!getfloat(Tok[i],&x)) return(202); - f = (SFloatlist *) malloc(sizeof(SFloatlist)); - if (f == NULL) return(101); - f->value = x; - f->next = p->x; - p->x = f; - } - Pattern[p->i].Length += n; /* Save # multipliers for pattern */ - PrevPat = p; /* Set previous pattern pointer */ - return(0); -} /* end of patterndata */ - - -int curvedata() -/* -**------------------------------------------------------ -** Input: none -** Output: returns error code -** Purpose: processes curve data -** Format: -** [CURVES] -** CurveID x-value y-value -**------------------------------------------------------ -*/ -{ - double x,y; - SFloatlist *fx, *fy; - STmplist *c; - - /* Check for valid curve ID */ - if (Ntokens < 3) return(201); - if ( - PrevCurve != NULL && - strcmp(Tok[0],PrevCurve->ID) == 0 - ) c = PrevCurve; - else c = findID(Tok[0],Curvelist); - if (c == NULL) return(205); - - /* Check for valid data */ - if (!getfloat(Tok[1],&x)) return(202); - if (!getfloat(Tok[2],&y)) return(202); - - /* Add new data point to curve's linked list */ - fx = (SFloatlist *) malloc(sizeof(SFloatlist)); - fy = (SFloatlist *) malloc(sizeof(SFloatlist)); - if (fx == NULL || fy == NULL) return(101); - fx->value = x; - fx->next = c->x; - c->x = fx; - fy->value = y; - fy->next = c->y; - c->y = fy; - Curve[c->i].Npts++; - - /* Save the pointer to this curve */ - PrevCurve = c; - return(0); -} - -int coordata() -/* - **-------------------------------------------------------------- - ** Input: none - ** Output: returns error code - ** Purpose: processes coordinate data - ** Format: - ** [COORD] - ** id x y - **-------------------------------------------------------------- - */ -{ - double x, y; - int j; - - /* Check for valid node ID */ - if (Ntokens < 3) return(201); - - /* Check for valid data */ - if ((j = findnode(Tok[0])) == 0) return(203); - if (!getfloat(Tok[1],&x)) return(202); - if (!getfloat(Tok[2],&y)) return(202); - - /* Save coord data */ - strncpy(Coord[j].ID, Node[j].ID, MAXID); - Coord[j].X = x; - Coord[j].Y = y; - Coord[j].HaveCoords = TRUE; - - return(0); -} /* end of coordata */ - -int demanddata() -/* -**-------------------------------------------------------------- -** Input: none -** Output: returns error code -** Purpose: processes node demand data -** Format: -** [DEMANDS] -** MULTIPLY factor -** node base_demand (pattern) -** -** NOTE: Demands entered in this section replace those -** entered in the [JUNCTIONS] section -**-------------------------------------------------------------- -*/ -{ - int j,n,p = 0; - double y; - Pdemand demand; - STmplist *pat; - -/* Extract data from tokens */ - n = Ntokens; - if (n < 2) return(201); - if (!getfloat(Tok[1],&y)) return(202); - -/* If MULTIPLY command, save multiplier */ - if (match(Tok[0],w_MULTIPLY)) - { - if (y <= 0.0) return(202); - else Dmult = y; - return(0); - } - -/* Otherwise find node (and pattern) being referenced */ - if ((j = findnode(Tok[0])) == 0) return(208); - if (j > Njuncs) return(208); - if (n >= 3) - { - pat = findID(Tok[2],Patlist); - if (pat == NULL) return(205); - p = pat->i; - } - -/* Replace any demand entered in [JUNCTIONS] section */ -/* (Such demand was temporarily stored in D[]) */ - -/*** Updated 6/24/02 ***/ - demand = Node[j].D; - if (demand && NodeDemand[j] != MISSING) - { - demand->Base = y; - demand->Pat = p; - NodeDemand[j] = MISSING; - } -/*** End of update ***/ - -/* Otherwise add a new demand to this junction */ - else - { - demand = (struct Sdemand *) malloc(sizeof(struct Sdemand)); - if (demand == NULL) return(101); - demand->Base = y; - demand->Pat = p; - demand->next = Node[j].D; - Node[j].D = demand; - } - return(0); -} /* end of demanddata */ - - -int controldata() -/* -**-------------------------------------------------------------- -** Input: none -** Output: returns error code -** Purpose: processes simple controls -** Formats: -** [CONTROLS] -** LINK linkID setting IF NODE nodeID {BELOW/ABOVE} level -** LINK linkID setting AT TIME value (units) -** LINK linkID setting AT CLOCKTIME value (units) -** (0) (1) (2) (3) (4) (5) (6) (7) -**-------------------------------------------------------------- -*/ -{ - int i = 0, /* Node index */ - k, /* Link index */ - n; /* # data items */ - char status = ACTIVE, /* Link status */ - type; /* Link or control type */ - double setting = MISSING, /* Link setting */ - time = 0.0, /* Simulation time */ - level = 0.0; /* Pressure or tank level */ - -/* Check for sufficient number of input tokens */ - n = Ntokens; - if (n < 6) return(201); - -/* Check that controlled link exists */ - k = findlink(Tok[1]); - if (k == 0) return(204); - type = Link[k].Type; - if (type == CV) return(207); /* Cannot control check valve. */ - -/*** Updated 9/7/00 ***/ -/* Parse control setting into a status level or numerical setting. */ - if (match(Tok[2],w_OPEN)) - { - status = OPEN; - if (type == PUMP) setting = 1.0; - if (type == GPV) setting = Link[k].Kc; - } - else if (match(Tok[2],w_CLOSED)) - { - status = CLOSED; - if (type == PUMP) setting = 0.0; - if (type == GPV) setting = Link[k].Kc; - } - else if (type == GPV) return(206); - else if (!getfloat(Tok[2],&setting)) return(202); - -/*** Updated 3/1/01 ***/ -/* Set status for pump in case speed setting was supplied */ -/* or for pipe if numerical setting was supplied */ - - if (type == PUMP || type == PIPE) - { - if (setting != MISSING) - { - if (setting < 0.0) return(202); - else if (setting == 0.0) status = CLOSED; - else status = OPEN; - } - } - -/* Determine type of control */ - if (match(Tok[4],w_TIME)) type = TIMER; - else if (match(Tok[4],w_CLOCKTIME)) type = TIMEOFDAY; - else - { - if (n < 8) return(201); - if ((i = findnode(Tok[5])) == 0) return(203); - if (match(Tok[6],w_BELOW)) type = LOWLEVEL; - else if (match(Tok[6],w_ABOVE)) type = HILEVEL; - else return(201); - } - -/* Parse control level or time */ - switch (type) - { - case TIMER: - case TIMEOFDAY: - if (n == 6) time = hour(Tok[5],""); - if (n == 7) time = hour(Tok[5],Tok[6]); - if (time < 0.0) return(201); - break; - case LOWLEVEL: - case HILEVEL: - if (!getfloat(Tok[7],&level)) return(202); - break; - } - -/* Fill in fields of control data structure */ - Ncontrols++; - if (Ncontrols > MaxControls) return(200); - Control[Ncontrols].Link = k; - Control[Ncontrols].Node = i; - Control[Ncontrols].Type = type; - Control[Ncontrols].Status = status; - Control[Ncontrols].Setting = setting; - Control[Ncontrols].Time = (long)(3600.0*time); - if (type == TIMEOFDAY) - Control[Ncontrols].Time %= SECperDAY; - Control[Ncontrols].Grade = level; - return(0); -} /* end of controldata */ - - -int sourcedata() -/* -**-------------------------------------------------------------- -** Input: none -** Output: returns error code -** Purpose: processes water quality source data -** Formats: -** [SOURCE] -** node sourcetype quality (pattern start stop) -** -** NOTE: units of mass-based source are mass/min -**-------------------------------------------------------------- -*/ -{ - int i, /* Token with quality value */ - j, /* Node index */ - n, /* # data items */ - p = 0; /* Time pattern */ - char type = CONCEN; /* Source type */ - double c0 = 0; /* Init. quality */ - STmplist *pat; - Psource source; - - n = Ntokens; - if (n < 2) return(201); - if ((j = findnode(Tok[0])) == 0) return(203); - /* NOTE: Under old format, SourceType not supplied so let */ - /* i = index of token that contains quality value. */ - i = 2; - if (match(Tok[1],w_CONCEN)) type = CONCEN; - else if (match(Tok[1],w_MASS)) type = MASS; - else if (match(Tok[1],w_SETPOINT)) type = SETPOINT; - else if (match(Tok[1],w_FLOWPACED)) type = FLOWPACED; - else i = 1; - if (!getfloat(Tok[i],&c0)) return(202); /* Illegal WQ value */ - - if (n > i+1 && strlen(Tok[i+1]) > 0 && strcmp(Tok[i+1], "*") != 0 ) //(2.00.11 - LR) - { - pat = findID(Tok[i+1],Patlist); - if (pat == NULL) return(205); /* Illegal pattern. */ - p = pat->i; - } - - source = (struct Ssource *) malloc(sizeof(struct Ssource)); - if (source == NULL) return(101); - source->C0 = c0; - source->Pat = p; - source->Type = type; - Node[j].S = source; - return(0); -} /* end of sourcedata */ - - -int emitterdata() -/* -**-------------------------------------------------------------- -** Input: none -** Output: returns error code -** Purpose: processes junction emitter data -** Format: -** [EMITTER] -** node K -**-------------------------------------------------------------- -*/ -{ - int j, /* Node index */ - n; /* # data items */ - double k; /* Flow coeff, */ - - n = Ntokens; - if (n < 2) return(201); - if ((j = findnode(Tok[0])) == 0) return(203); - if (j > Njuncs) return(209); /* Not a junction.*/ - if (!getfloat(Tok[1],&k)) return(202); - if (k < 0.0) return(202); - Node[j].Ke = k; - return(0); -} - - -int qualdata() -/* -**-------------------------------------------------------------- -** Input: none -** Output: returns error code -** Purpose: processes initial water quality data -** Formats: -** [QUALITY] -** node initqual -** node1 node2 initqual -**-------------------------------------------------------------- -*/ -{ - int j,n; - long i,i0,i1; - double c0; - - if (Nnodes == 0) return(208); /* No nodes defined yet */ - n = Ntokens; - if (n < 2) return(0); - if (n == 2) /* Single node entered */ - { - if ( (j = findnode(Tok[0])) == 0) return(0); - if (!getfloat(Tok[1],&c0)) return(209); - Node[j].C0 = c0; - } - else /* Node range entered */ - { - if (!getfloat(Tok[2],&c0)) return(209); - - /* If numerical range supplied, then use numerical comparison */ - if ((i0 = atol(Tok[0])) > 0 && (i1 = atol(Tok[1])) > 0) - { - for (j=1; j<=Nnodes; j++) - { - i = atol(Node[j].ID); - if (i >= i0 && i <= i1) Node[j].C0 = c0; - } - } - else - { - for (j=1; j<=Nnodes; j++) - if ((strcmp(Tok[0],Node[j].ID) <= 0) && - (strcmp(Tok[1],Node[j].ID) >= 0) - ) Node[j].C0 = c0; - } - } - return(0); -} /* end of qualdata */ - - -int reactdata() -/* -**-------------------------------------------------------------- -** Input: none -** Output: returns error code -** Purpose: processes reaction coeff. data -** Formats: -** [REACTIONS] -** ORDER {BULK/WALL/TANK} value -** GLOBAL BULK coeff -** GLOBAL WALL coeff -** BULK link1 (link2) coeff -** WALL link1 (link2) coeff -** TANK node1 (node2) coeff -** LIMITING POTENTIAL value -** ROUGHNESS CORRELATION value -**-------------------------------------------------------------- -*/ -{ - int item,j,n; - long i,i1,i2; - double y; - -/* Skip line if insufficient data */ - n = Ntokens; - if (n < 3) return(0); - -/* Process input depending on keyword */ - if (match(Tok[0],w_ORDER)) /* Reaction order */ - { - if (!getfloat(Tok[n-1],&y)) return(213); - if (match(Tok[1],w_BULK)) BulkOrder = y; - else if (match(Tok[1],w_TANK)) TankOrder = y; - else if (match(Tok[1],w_WALL)) - { - if (y == 0.0) WallOrder = 0.0; - else if (y == 1.0) WallOrder = 1.0; - else return(213); - } - else return(213); - return(0); - } - if (match(Tok[0],w_ROUGHNESS)) /* Roughness factor */ - { - if (!getfloat(Tok[n-1],&y)) return(213); - Rfactor = y; - return(0); - } - if (match(Tok[0],w_LIMITING)) /* Limiting potential */ - { - if (!getfloat(Tok[n-1],&y)) return(213); - /*if (y < 0.0) return(213);*/ - Climit = y; - return(0); - } - if (match(Tok[0],w_GLOBAL)) /* Global rates */ - { - if (!getfloat(Tok[n-1],&y)) return(213); - if (match(Tok[1],w_BULK)) Kbulk = y; - else if (match(Tok[1],w_WALL)) Kwall = y; - else return(201); - return(0); - } - if (match(Tok[0],w_BULK)) item = 1; /* Individual rates */ - else if (match(Tok[0],w_WALL)) item = 2; - else if (match(Tok[0],w_TANK)) item = 3; - else return(201); - strcpy(Tok[0],Tok[1]); /* Save id in Tok[0] */ - if (item == 3) /* Tank rates */ - { - if (!getfloat(Tok[n-1],&y)) return(209); /* Rate coeff. */ - if (n == 3) - { - if ( (j = findnode(Tok[1])) <= Njuncs) return(0); - Tank[j-Njuncs].Kb = y; - } - else - { - /* If numerical range supplied, then use numerical comparison */ - if ((i1 = atol(Tok[1])) > 0 && (i2 = atol(Tok[2])) > 0) - { - for (j=Njuncs+1; j<=Nnodes; j++) - { - i = atol(Node[j].ID); - if (i >= i1 && i <= i2) Tank[j-Njuncs].Kb = y; - } - } - else for (j=Njuncs+1; j<=Nnodes; j++) - if ((strcmp(Tok[1],Node[j].ID) <= 0) && - (strcmp(Tok[2],Node[j].ID) >= 0) - ) Tank[j-Njuncs].Kb = y; - } - } - else /* Link rates */ - { - if (!getfloat(Tok[n-1],&y)) return(211); /* Rate coeff. */ - if (Nlinks == 0) return(0); - if (n == 3) /* Single link */ - { - if ( (j = findlink(Tok[1])) == 0) return(0); - if (item == 1) Link[j].Kb = y; - else Link[j].Kw = y; - } - else /* Range of links */ - { - /* If numerical range supplied, then use numerical comparison */ - if ((i1 = atol(Tok[1])) > 0 && (i2 = atol(Tok[2])) > 0) - { - for (j=1; j<=Nlinks; j++) - { - i = atol(Link[j].ID); - if (i >= i1 && i <= i2) - { - if (item == 1) Link[j].Kb = y; - else Link[j].Kw = y; - } - } - } - else for (j=1; j<=Nlinks; j++) - if ((strcmp(Tok[1],Link[j].ID) <= 0) && - (strcmp(Tok[2],Link[j].ID) >= 0) ) - { - if (item == 1) Link[j].Kb = y; - else Link[j].Kw = y; - } - } - } - return(0); -} /* end of reactdata */ - - -int mixingdata() -/* -**------------------------------------------------------------- -** Input: none -** Output: returns error code -** Purpose: processes tank mixing data -** Format: -** [MIXING] -** TankID MixModel FractVolume -**------------------------------------------------------------- -*/ -{ - int i,j,n; - double v; - - if (Nnodes == 0) return(208); /* No nodes defined yet */ - n = Ntokens; - if (n < 2) return(0); - if ( (j = findnode(Tok[0])) <= Njuncs) return(0); - if ( (i = findmatch(Tok[1],MixTxt)) < 0) return(201); - v = 1.0; - if ( (i == MIX2) && - (n == 3) && - (!getfloat(Tok[2],&v)) /* Get frac. vol. for 2COMP model */ - ) return(209); - if (v == 0.0) v = 1.0; /* v can't be zero */ - n = j - Njuncs; - if (Tank[n].A == 0.0) return(0); /* Tank is a reservoir */ - Tank[n].MixModel = (char)i; - Tank[n].V1max = v; - return(0); -} - - -int statusdata() -/* -**-------------------------------------------------------------- -** Input: none -** Output: returns error code -** Purpose: processes link initial status data -** Formats: -** [STATUS] -** link value -** link1 (link2) value -**-------------------------------------------------------------- -*/ -{ - int j,n; - long i,i0,i1; - double y = 0.0; - char status = ACTIVE; - - if (Nlinks == 0) return(210); - n = Ntokens - 1; - if (n < 1) return(201); - -/* Check for legal status setting */ - if (match(Tok[n],w_OPEN)) status = OPEN; - else if (match(Tok[n],w_CLOSED)) status = CLOSED; - else if (!getfloat(Tok[n],&y)) return(211); - if (y < 0.0) return(211); - -/* Single link ID supplied */ - if (n == 1) - { - if ( (j = findlink(Tok[0])) == 0) return(0); - /* Cannot change status of a Check Valve */ - if (Link[j].Type == CV) return(211); - -/*** Updated 9/7/00 ***/ - /* Cannot change setting for a GPV */ - if (Link[j].Type == GPV - && status == ACTIVE) return(211); - - changestatus(j,status,y); - } - -/* Range of ID's supplied */ - else - { - /* Numerical range supplied */ - if ((i0 = atol(Tok[0])) > 0 && (i1 = atol(Tok[1])) > 0) - { - for (j=1; j<=Nlinks; j++) - { - i = atol(Link[j].ID); - if (i >= i0 && i <= i1) changestatus(j,status,y); - } - } - else - for (j=1; j<=Nlinks; j++) - if ( (strcmp(Tok[0],Link[j].ID) <= 0) && - (strcmp(Tok[1],Link[j].ID) >= 0) - ) changestatus(j,status,y); - } - return(0); -} /* end of statusdata */ - - -int energydata() -/* -**-------------------------------------------------------------- -** Input: none -** Output: returns error code -** Purpose: processes pump energy data -** Formats: -** [ENERGY] -** GLOBAL {PRICE/PATTERN/EFFIC} value -** PUMP id {PRICE/PATTERN/EFFIC} value -** DEMAND CHARGE value -**-------------------------------------------------------------- -*/ -{ - int j,k,n; - double y; - STmplist *t; - -/* Check for sufficient data */ - n = Ntokens; - if (n < 3) return(201); - -/* Check first keyword */ - if (match(Tok[0],w_DMNDCHARGE)) /* Demand charge */ - { - if (!getfloat(Tok[2], &y)) return(213); - Dcost = y; - return(0); - } - if (match(Tok[0],w_GLOBAL)) /* Global parameter */ - { - j = 0; - } - else if (match(Tok[0],w_PUMP)) /* Pump-specific parameter */ - { - if (n < 4) return(201); - k = findlink(Tok[1]); /* Check that pump exists */ - if (k == 0) return(216); - if (Link[k].Type != PUMP) return(216); - j = PUMPINDEX(k); - } - else return(201); - -/* Find type of energy parameter */ - if (match(Tok[n-2],w_PRICE)) /* Energy price */ - { - if (!getfloat(Tok[n-1],&y)) - { - if (j == 0) return(213); - else return(217); - } - if (j == 0) Ecost = y; - else Pump[j].Ecost = y; - return(0); - } - else if (match(Tok[n-2],w_PATTERN)) /* Price pattern */ - { - t = findID(Tok[n-1],Patlist); /* Check if pattern exists */ - if (t == NULL) - { - if (j == 0) return(213); - else return(217); - } - if (j == 0) Epat = t->i; - else Pump[j].Epat = t->i; - return(0); - } - else if (match(Tok[n-2],w_EFFIC)) /* Pump efficiency */ - { - if (j == 0) - { - if (!getfloat(Tok[n-1], &y)) return(213); - if (y <= 0.0) return(213); - Epump = y; - } - else - { - t = findID(Tok[n-1],Curvelist); /* Check if curve exists */ - if (t == NULL) return(217); - Pump[j].Ecurve = t->i; - } - return(0); - } - return(201); -} - - -int reportdata() -/* -**-------------------------------------------------------------- -** Input: none -** Output: returns error code -** Purpose: processes report options data -** Formats: -** PAGE linesperpage -** STATUS {NONE/YES/FULL} -** SUMMARY {YES/NO} -** MESSAGES {YES/NO} -** ENERGY {NO/YES} -** NODES {NONE/ALL} -** NODES node1 node2 ... -** LINKS {NONE/ALL} -** LINKS link1 link2 ... -** FILE filename -** variable {YES/NO} -** variable {BELOW/ABOVE/PRECISION} value -**-------------------------------------------------------------- -*/ -{ - int i,j,n; - double y; - - n = Ntokens - 1; - if (n < 1) return(201); - -/* Value for page size */ - if (match(Tok[0],w_PAGE)) - { - if (!getfloat(Tok[n],&y)) return(213); - if (y < 0.0 || y > 255.0) return(213); - PageSize = (int) y; - return(0); - } - -/* Request that status reports be written */ - if (match(Tok[0],w_STATUS)) - { - if (match(Tok[n],w_NO)) Statflag = FALSE; - if (match(Tok[n],w_YES)) Statflag = TRUE; - if (match(Tok[n],w_FULL)) Statflag = FULL; - return(0); - } - -/* Request summary report */ - if (match(Tok[0],w_SUMMARY)) - { - if (match(Tok[n],w_NO)) Summaryflag = FALSE; - if (match(Tok[n],w_YES)) Summaryflag = TRUE; - return(0); - } - -/* Request error/warning message reporting */ - if (match(Tok[0],w_MESSAGES)) - { - if (match(Tok[n],w_NO)) Messageflag = FALSE; - if (match(Tok[n],w_YES)) Messageflag = TRUE; - return(0); - } - - -/* Request an energy usage report */ - if (match(Tok[0],w_ENERGY)) - { - if (match(Tok[n],w_NO)) Energyflag = FALSE; - if (match(Tok[n],w_YES)) Energyflag = TRUE; - return(0); - } - -/* Particular reporting nodes specified */ - if (match(Tok[0],w_NODE)) - { - if (match(Tok[n],w_NONE)) Nodeflag = 0; /* No nodes */ - else if (match(Tok[n],w_ALL)) Nodeflag = 1; /* All nodes */ - else - { - if (Nnodes == 0) return(208); - for (i=1; i<=n; i++) - { - if ( (j = findnode(Tok[i])) == 0) return(208); - Node[j].Rpt = 1; - } - Nodeflag = 2; - } - return(0); - } - -/* Particular reporting links specified */ - if (match(Tok[0],w_LINK)) - { - if (match(Tok[n],w_NONE)) Linkflag = 0; - else if (match(Tok[n],w_ALL)) Linkflag = 1; - else - { - if (Nlinks == 0) return(210); - for (i=1; i<=n; i++) - { - if ( (j = findlink(Tok[i])) == 0) return(210); - Link[j].Rpt = 1; - } - Linkflag = 2; - } - return(0); - } - -/* Check if input is a reporting criterion. */ - -/*** Special case needed to distinguish "HEAD" from "HEADLOSS" ***/ //(2.00.11 - LR) - if (strcomp(Tok[0], t_HEADLOSS)) i = HEADLOSS; - else i = findmatch(Tok[0],Fldname); //(2.00.11 - LR) - if (i >= 0) //(2.00.11 - LR) -/*****************************************************************/ //(2.00.11 - LR) - { - if (i > FRICTION) return(201); - if (Ntokens == 1 || match(Tok[1],w_YES)) - { - Field[i].Enabled = TRUE; - return(0); - } - if (match(Tok[1],w_NO)) - { - Field[i].Enabled = FALSE; - return(0); - } - if (Ntokens < 3) return(201); - if (match(Tok[1],w_BELOW)) j = LOW; /* Get relation operator */ - else if (match(Tok[1],w_ABOVE)) j = HI; /* or precision keyword */ - else if (match(Tok[1],w_PRECISION)) j = PREC; - else return(201); - if (!getfloat(Tok[2],&y)) return(201); - if (j == PREC) - { - Field[i].Enabled = TRUE; - Field[i].Precision = ROUND(y); - } - else Field[i].RptLim[j] = y; /* Report limit value */ - return(0); - } - -/* Name of external report file */ - if (match(Tok[0],w_FILE)) - { - strncpy(Rpt2Fname,Tok[1],MAXFNAME); - return(0); - } - -/* If get to here then return error condition */ - return(201); -} /* end of reportdata */ - - -int timedata() -/* -**-------------------------------------------------------------- -** Input: none -** Output: returns error code -** Purpose: processes time options data -** Formats: -** STATISTIC {NONE/AVERAGE/MIN/MAX/RANGE} -** DURATION value (units) -** HYDRAULIC TIMESTEP value (units) -** QUALITY TIMESTEP value (units) -** MINIMUM TRAVELTIME value (units) -** RULE TIMESTEP value (units) -** PATTERN TIMESTEP value (units) -** PATTERN START value (units) -** REPORT TIMESTEP value (units) -** REPORT START value (units) -** START CLOCKTIME value (AM PM) -**------------------------------------------------------------- -*/ -{ - int n; - long t; - double y; - - n = Ntokens - 1; - if (n < 1) return(201); - -/* Check if setting time statistic flag */ - if (match(Tok[0],w_STATISTIC)) - { - if (match(Tok[n],w_NONE)) Tstatflag = SERIES; - else if (match(Tok[n],w_NO)) Tstatflag = SERIES; - else if (match(Tok[n],w_AVG)) Tstatflag = AVG; - else if (match(Tok[n],w_MIN)) Tstatflag = MIN; - else if (match(Tok[n],w_MAX)) Tstatflag = MAX; - else if (match(Tok[n],w_RANGE)) Tstatflag = RANGE; - else return(201); - return(0); - } - -/* Convert text time value to numerical value in seconds */ -/* Examples: -** 5 = 5 * 3600 sec -** 5 MINUTES = 5 * 60 sec -** 13:50 = 13*3600 + 50*60 sec -** 1:50 pm = (12+1)*3600 + 50*60 sec -*/ - - if (!getfloat(Tok[n],&y)) - { - if ( (y = hour(Tok[n],"")) < 0.0) - { - if ( (y = hour(Tok[n-1],Tok[n])) < 0.0) return(213); - } - } - t = (long)(3600.0*y+0.5); -/* Process the value assigned to the matched parameter */ - if (match(Tok[0],w_DURATION)) Dur = t; /* Simulation duration */ - else if (match(Tok[0],w_HYDRAULIC)) Hstep = t; /* Hydraulic time step */ - else if (match(Tok[0],w_QUALITY)) Qstep = t; /* Quality time step */ - else if (match(Tok[0],w_RULE)) Rulestep = t; /* Rule time step */ - else if (match(Tok[0],w_MINIMUM)) return(0); /* Not used anymore */ - else if (match(Tok[0],w_PATTERN)) - { - if (match(Tok[1],w_TIME)) Pstep = t; /* Pattern time step */ - else if (match(Tok[1],w_START)) Pstart = t; /* Pattern start time */ - else return(201); - } - else if (match(Tok[0],w_REPORT)) - { - if (match(Tok[1],w_TIME)) Rstep = t; /* Reporting time step */ - else if (match(Tok[1],w_START)) Rstart = t; /* Reporting start time */ - else return(201); - } /* Simulation start time*/ - else if (match(Tok[0],w_START)) Tstart = t % SECperDAY; - else return(201); - return(0); -} /* end of timedata */ - - -int optiondata() -/* -**-------------------------------------------------------------- -** Input: none -** Output: returns error code -** Purpose: processes [OPTIONS] data -**-------------------------------------------------------------- -*/ -{ - int i,n; - - n = Ntokens - 1; - i = optionchoice(n); /* Option is a named choice */ - if (i >= 0) return(i); - return(optionvalue(n)); /* Option is a numerical value */ -} /* end of optiondata */ - - -int optionchoice(int n) -/* -**-------------------------------------------------------------- -** Input: n = index of last input token saved in Tok[] -** Output: returns error code or 0 if option belongs to -** those listed below, or -1 otherwise -** Purpose: processes fixed choice [OPTIONS] data -** Formats: -** UNITS CFS/GPM/MGD/IMGD/AFD/LPS/LPM/MLD/CMH/CMD/SI -** PRESSURE PSI/KPA/M -** HEADLOSS H-W/D-W/C-M -** HYDRAULICS USE/SAVE filename -** QUALITY NONE/AGE/TRACE/CHEMICAL (TraceNode) -** MAP filename -** VERIFY filename -** UNBALANCED STOP/CONTINUE {Niter} -** PATTERN id -**-------------------------------------------------------------- -*/ -{ - /* Check if 1st token matches a parameter name and */ - /* process the input for the matched parameter */ - if (n < 0) return(201); - if (match(Tok[0],w_UNITS)) - { - if (n < 1) return(0); - else if (match(Tok[1],w_CFS)) Flowflag = CFS; - else if (match(Tok[1],w_GPM)) Flowflag = GPM; - else if (match(Tok[1],w_AFD)) Flowflag = AFD; - else if (match(Tok[1],w_MGD)) Flowflag = MGD; - else if (match(Tok[1],w_IMGD)) Flowflag = IMGD; - else if (match(Tok[1],w_LPS)) Flowflag = LPS; - else if (match(Tok[1],w_LPM)) Flowflag = LPM; - else if (match(Tok[1],w_CMH)) Flowflag = CMH; - else if (match(Tok[1],w_CMD)) Flowflag = CMD; - else if (match(Tok[1],w_MLD)) Flowflag = MLD; - else if (match(Tok[1],w_SI)) Flowflag = LPS; - else return(201); - } - else if (match(Tok[0],w_PRESSURE)) - { - if (n < 1) return(0); - else if (match(Tok[1],w_PSI)) Pressflag = PSI; - else if (match(Tok[1],w_KPA)) Pressflag = KPA; - else if (match(Tok[1],w_METERS)) Pressflag = METERS; - else return(201); - } - else if (match(Tok[0],w_HEADLOSS)) - { - if (n < 1) return(0); - else if (match(Tok[1],w_HW)) Formflag = HW; - else if (match(Tok[1],w_DW)) Formflag = DW; - else if (match(Tok[1],w_CM)) Formflag = CM; - else return(201); - } - else if (match(Tok[0],w_HYDRAULIC)) - { - if (n < 2) return(0); - else if (match(Tok[1],w_USE)) Hydflag = USE; - else if (match(Tok[1],w_SAVE)) Hydflag = SAVE; - else return(201); - strncpy(HydFname,Tok[2],MAXFNAME); - } - else if (match(Tok[0],w_QUALITY)) - { - if (n < 1) return(0); - else if (match(Tok[1],w_NONE)) Qualflag = NONE; - else if (match(Tok[1],w_CHEM)) Qualflag = CHEM; - else if (match(Tok[1],w_AGE)) Qualflag = AGE; - else if (match(Tok[1],w_TRACE)) Qualflag = TRACE; - else - { - Qualflag = CHEM; - strncpy(ChemName,Tok[1],MAXID); - if (n >= 2) strncpy(ChemUnits,Tok[2],MAXID); - } - if (Qualflag == TRACE) /* Source tracing option */ - { - /* Copy Trace Node ID to Tok[0] for error reporting */ - strcpy(Tok[0],""); - if (n < 2) return(212); - strcpy(Tok[0],Tok[2]); - TraceNode = findnode(Tok[2]); - if (TraceNode == 0) return(212); - strncpy(ChemName,u_PERCENT,MAXID); - strncpy(ChemUnits,Tok[2],MAXID); - } - if (Qualflag == AGE) - { - strncpy(ChemName,w_AGE,MAXID); - strncpy(ChemUnits,u_HOURS,MAXID); - } - } - else if (match(Tok[0],w_MAP)) - { - if (n < 1) return(0); - strncpy(MapFname,Tok[1],MAXFNAME); /* Map file name */ - } - else if (match(Tok[0],w_VERIFY)) - { - /* Backward compatibility for verification file */ - } - else if (match(Tok[0],w_UNBALANCED)) /* Unbalanced option */ - { - if (n < 1) return(0); - if (match(Tok[1],w_STOP)) ExtraIter = -1; - else if (match(Tok[1],w_CONTINUE)) - { - if (n >= 2) ExtraIter = atoi(Tok[2]); - else ExtraIter = 0; - } - else return(201); - } - else if (match(Tok[0],w_PATTERN)) /* Pattern option */ - { - if (n < 1) return(0); - strncpy(DefPatID,Tok[1],MAXID); - } - else return(-1); - return(0); -} /* end of optionchoice */ - - -int optionvalue(int n) -/* -**------------------------------------------------------------- -** Input: *line = line read from input file -** Output: returns error code -** Purpose: processes numerical value [OPTIONS] data -** Formats: -** DEMAND MULTIPLIER value -** EMITTER EXPONENT value -** VISCOSITY value -** DIFFUSIVITY value -** SPECIFIC GRAVITY value -** TRIALS value -** ACCURACY value -** TOLERANCE value -** SEGMENTS value (not used) -** ------ Undocumented Options ----- -** HTOL value -** QTOL value -** RQTOL value -** CHECKFREQ value -** MAXCHECK value -** DAMPLIMIT value //(2.00.12 - LR) -**-------------------------------------------------------------- -*/ -{ - int nvalue = 1; /* Index of token with numerical value */ - double y; - -/* Check for obsolete SEGMENTS keyword */ - if (match(Tok[0],w_SEGMENTS)) return(0); - -/* Check for missing value (which is permissible) */ - if (match(Tok[0],w_SPECGRAV) || match(Tok[0],w_EMITTER) - || match(Tok[0],w_DEMAND)) nvalue = 2; - if (n < nvalue) return(0); - -/* Check for valid numerical input */ - if (!getfloat(Tok[nvalue],&y)) return(213); - -/* Check for WQ tolerance option (which can be 0) */ - if (match(Tok[0],w_TOLERANCE)) - { - if (y < 0.0) return(213); - Ctol = y; /* Quality tolerance*/ - return(0); - } - -/* Check for Diffusivity option */ - if (match(Tok[0],w_DIFFUSIVITY)) - { - if (y < 0.0) return(213); - Diffus = y; - return(0); - } - -/* Check for Damping Limit option */ //(2.00.12 - LR) - if (match(Tok[0],w_DAMPLIMIT)) - { - DampLimit = y; - return(0); - } - -/* All other options must be > 0 */ - if (y <= 0.0) return(213); - -/* Assign value to specified option */ - if (match(Tok[0],w_VISCOSITY)) Viscos = y; /* Viscosity */ - else if (match(Tok[0],w_SPECGRAV)) SpGrav = y; /* Spec. gravity */ - else if (match(Tok[0],w_TRIALS)) MaxIter = (int)y; /* Max. trials */ - else if (match(Tok[0],w_ACCURACY)) /* Accuracy */ - { - y = MAX(y,1.e-5); - y = MIN(y,1.e-1); - Hacc = y; - } - else if (match(Tok[0],w_HTOL)) Htol = y; - else if (match(Tok[0],w_QTOL)) Qtol = y; - else if (match(Tok[0],w_RQTOL)) - { - if (y >= 1.0) return(213); - RQtol = y; - } - else if (match(Tok[0],w_CHECKFREQ)) CheckFreq = (int)y; - else if (match(Tok[0],w_MAXCHECK)) MaxCheck = (int)y; - else if (match(Tok[0],w_EMITTER)) Qexp = 1.0/y; - else if (match(Tok[0],w_DEMAND)) Dmult = y; - else return(201); - return(0); -} /* end of optionvalue */ - - -int getpumpcurve(int n) -/* -**-------------------------------------------------------- -** Input: n = number of parameters for pump curve -** Output: returns error code -** Purpose: processes pump curve data for Version 1.1- -** style input data -** Notes: -** 1. Called by pumpdata() in INPUT3.C -** 2. Current link index & pump index of pump being -** processed is found in global variables Nlinks -** and Npumps, respectively -** 3. Curve data read from input line is found in -** global variables X[0],...X[n-1] -**--------------------------------------------------------- -*/ -{ - double a,b,c,h0,h1,h2,q1,q2; - - if (n == 1) /* Const. HP curve */ - { - if (X[0] <= 0.0) return(202); - Pump[Npumps].Ptype = CONST_HP; - Link[Nlinks].Km = X[0]; - } - else - { - if (n == 2) /* Generic power curve */ - { - q1 = X[1]; - h1 = X[0]; - h0 = 1.33334*h1; - q2 = 2.0*q1; - h2 = 0.0; - } - else if (n >= 5) /* 3-pt. power curve */ - { - h0 = X[0]; - h1 = X[1]; - q1 = X[2]; - h2 = X[3]; - q2 = X[4]; - } - else return(202); - Pump[Npumps].Ptype = POWER_FUNC; - if (!powercurve(h0,h1,h2,q1,q2,&a,&b,&c)) return(206); - Pump[Npumps].H0 = -a; - Pump[Npumps].R = -b; - Pump[Npumps].N = c; - Pump[Npumps].Q0 = q1; - Pump[Npumps].Qmax = pow((-a/b),(1.0/c)); - Pump[Npumps].Hmax = h0; - } - return(0); -} - - -int powercurve(double h0, double h1, double h2, double q1, - double q2, double *a, double *b, double *c) -/* -**--------------------------------------------------------- -** Input: h0 = shutoff head -** h1 = design head -** h2 = head at max. flow -** q1 = design flow -** q2 = max. flow -** Output: *a, *b, *c = pump curve coeffs. (H = a-bQ^c), -** Returns 1 if sucessful, 0 otherwise. -** Purpose: computes coeffs. for pump curve -**---------------------------------------------------------- -*/ -{ - double h4,h5; - if ( - h0 < TINY || - h0 - h1 < TINY || - h1 - h2 < TINY || - q1 < TINY || - q2 - q1 < TINY - ) return(0); - *a = h0; - h4 = h0 - h1; - h5 = h0 - h2; - *c = log(h5/h4)/log(q2/q1); - if (*c <= 0.0 || *c > 20.0) return(0); - *b = -h4/pow(q1,*c); - - /*** Updated 6/24/02 ***/ - if (*b >= 0.0) return(0); - - return(1); -} - - -int valvecheck(int type, int j1, int j2) -/* -**-------------------------------------------------------------- -** Input: type = valve type -** j1 = index of upstream node -** j2 = index of downstream node -** Output: returns 1 for legal connection, 0 otherwise -** Purpose: checks for legal connections between PRVs & PSVs -**-------------------------------------------------------------- -*/ -{ - int k, vk, vj1, vj2, vtype; - - /* Examine each existing valve */ - for (k=1; k<=Nvalves; k++) - { - vk = Valve[k].Link; - vj1 = Link[vk].N1; - vj2 = Link[vk].N2; - vtype = Link[vk].Type; - - /* Cannot have two PRVs sharing downstream nodes or in series */ - if (vtype == PRV && type == PRV) - { - if (vj2 == j2 || - vj2 == j1 || - vj1 == j2 ) return(0); - } - - /* Cannot have two PSVs sharing upstream nodes or in series */ - if (vtype == PSV && type == PSV) - { - if (vj1 == j1 || - vj1 == j2 || - vj2 == j1 ) return(0); - } - - /* Cannot have PSV connected to downstream node of PRV */ - if (vtype == PSV && type == PRV && vj1 == j2) return(0); - if (vtype == PRV && type == PSV && vj2 == j1) return(0); - -/*** Updated 3/1/01 ***/ - /* Cannot have PSV connected to downstream node of FCV */ - /* nor have PRV connected to upstream node of FCV */ - if (vtype == FCV && type == PSV && vj2 == j1) return(0); - if (vtype == FCV && type == PRV && vj1 == j2) return(0); - -/*** Updated 4/14/05 ***/ - if (vtype == PSV && type == FCV && vj1 == j2) return (0); - if (vtype == PRV && type == FCV && vj2 == j1) return (0); - } - return(1); -} /* End of valvecheck */ - - -void changestatus(int j, char status, double y) -/* -**-------------------------------------------------------------- -** Input: j = link index -** status = status setting (OPEN, CLOSED) -** y = numerical setting (pump speed, valve -** setting) -** Output: none -** Purpose: changes status or setting of a link -** -** NOTE: If status = ACTIVE, then a numerical setting (y) was -** supplied. If status = OPEN/CLOSED, then numerical -** setting is 0. -**-------------------------------------------------------------- -*/ -{ - if (Link[j].Type == PIPE || Link[j].Type == GPV) - { - if (status != ACTIVE) Link[j].Stat = status; - } - else if (Link[j].Type == PUMP) - { - if (status == ACTIVE) - { - Link[j].Kc = y; - status = OPEN; - if (y == 0.0) status = CLOSED; - } - else if (status == OPEN) Link[j].Kc = 1.0; - Link[j].Stat = status; - } - else if (Link[j].Type >= PRV) - { - Link[j].Kc = y; - Link[j].Stat = status; - if (status != ACTIVE) Link[j].Kc = MISSING; - } -} /* end of changestatus */ - -/********************** END OF INPUT3.C ************************/ - +/* +********************************************************************** + +INPUT3.C -- Input data parser for EPANET + +VERSION: 2.00 +DATE: 5/30/00 + 9/7/00 + 10/25/00 + 3/1/01 + 6/24/02 + 8/15/07 (2.00.11) + 2/14/08 (2.00.12) +AUTHOR: L. Rossman + US EPA - NRMRL + +This module parses data from each line of input from file InFile. +All functions in this module are called from newline() in INPUT2.C. + +********************************************************************** +*/ + +#include +#include +#include +#ifndef __APPLE__ +#include +#endif +#include "epanet2.h" +#include "funcs.h" +#include "hash.h" +#include "text.h" +#include "types.h" +#include +#define EXTERN extern +#include "vars.h" + +/* Defined in enumstxt.h in EPANET.C */ +extern char *MixTxt[]; +extern char *Fldname[]; + +/* Defined in INPUT2.C */ + +int juncdata(EN_Project *pr) +/* +**-------------------------------------------------------------- +** Input: none +** Output: returns error code +** Purpose: processes junction data +** Format: +** [JUNCTIONS] +** id elev. (demand) (demand pattern) +**-------------------------------------------------------------- +*/ +{ + int p = 0; + double el, y = 0.0; + Pdemand demand; + STmplist *pat; + + EN_Network *net = &pr->network; + parser_data_t *par = &pr->parser; + hydraulics_t *hyd = &pr->hydraulics; + int n; + int Njuncs; + Snode *node; + + /* Add new junction to data base */ + n = par->Ntokens; + if (net->Nnodes == par->MaxNodes) { + return (200); + } + net->Njuncs++; + net->Nnodes++; + + Njuncs = net->Njuncs; + + if (!addnodeID(net, net->Njuncs, par->Tok[0])) { + return (215); + } + /* Check for valid data */ + if (n < 2) + return (201); + if (!getfloat(par->Tok[1], &el)) + return (202); + if (n >= 3 && !getfloat(par->Tok[2], &y)) + return (202); + if (n >= 4) { + pat = findID(par->Tok[3], par->Patlist); + if (pat == NULL) + return (205); + p = pat->i; + } + + /* Save junction data */ + node = &net->Node[Njuncs]; + node->El = el; + node->C0 = 0.0; + node->S = NULL; + node->Ke = 0.0; + node->Rpt = 0; + node->Type = EN_JUNCTION; + strcpy(node->Comment, par->Comment); + + + // create a demand record, even if no demand is specified here. + // perhaps the [DEMANDS] section contains data, but not always. + + demand = (struct Sdemand *) malloc(sizeof(struct Sdemand)); + if (demand == NULL) { + return(101); + } + demand->Base = y; + demand->Pat = p; + demand->next = node->D; + node->D = demand; + hyd->NodeDemand[Njuncs] = y; + + return (0); +} /* end of juncdata */ + +int tankdata(EN_Project *pr) +/* +**-------------------------------------------------------------- +** Input: none +** Output: returns error code +** Purpose: processes tank & reservoir data +** Format: +** [RESERVOIRS] +** id elev (pattern) +** [TANKS] +** id elev (pattern) +** id elev initlevel minlevel maxlevel diam (minvol vcurve) +**-------------------------------------------------------------- +*/ +{ + int n, /* # data items */ + p = 0, /* Fixed grade time pattern index */ + vcurve = 0; /* Volume curve index */ + double el = 0.0, /* Elevation */ + initlevel = 0.0, /* Initial level */ + minlevel = 0.0, /* Minimum level */ + maxlevel = 0.0, /* Maximum level */ + minvol = 0.0, /* Minimum volume */ + diam = 0.0, /* Diameter */ + area; /* X-sect. area */ + STmplist *t; + + EN_Network *net = &pr->network; + parser_data_t *par = &pr->parser; + Snode *node; + Stank *tank; + int i; + + /* Add new tank to data base */ + n = par->Ntokens; + if (net->Ntanks == par->MaxTanks || net->Nnodes == par->MaxNodes) { + return (200); + } + net->Ntanks++; + net->Nnodes++; + + i = par->MaxJuncs + net->Ntanks; /* i = node index. */ + if (!addnodeID(net, i, par->Tok[0])) { + return (215); /* Add ID to database. */ + } + + /* Check for valid data */ + if (n < 2) + return (201); /* Too few fields. */ + if (!getfloat(par->Tok[1], &el)) + return (202); /* Read elevation */ + + if (n <= 3) { /* Tank is reservoir.*/ + if (n == 3) { /* Pattern supplied */ + t = findID(par->Tok[2], par->Patlist); + if (t == NULL) + return (205); + p = t->i; + } + } else if (n < 6) { + return (201); /* Too few fields for tank.*/ + } else { + /* Check for valid input data */ + if (!getfloat(par->Tok[2], &initlevel)) + return (202); + if (!getfloat(par->Tok[3], &minlevel)) + return (202); + if (!getfloat(par->Tok[4], &maxlevel)) + return (202); + if (!getfloat(par->Tok[5], &diam)) + return (202); + if (diam < 0.0) + return (202); + if (n >= 7 && !getfloat(par->Tok[6], &minvol)) + return (202); + + /* If volume curve supplied check it exists */ + if (n == 8) { + t = findID(par->Tok[7], par->Curvelist); + if (t == NULL) + return (202); + vcurve = t->i; + } + } + + node = &net->Node[i]; + tank = &net->Tank[net->Ntanks]; + + node->Rpt = 0; + node->El = el; /* Elevation. */ + node->C0 = 0.0; /* Init. quality. */ + node->S = NULL; /* WQ source data */ + node->Ke = 0.0; /* Emitter coeff. */ + node->Type = (diam == 0) ? EN_RESERVOIR : EN_TANK; + strcpy(node->Comment, par->Comment); + tank->Node = i; /* Node index. */ + tank->H0 = initlevel; /* Init. level. */ + tank->Hmin = minlevel; /* Min. level. */ + tank->Hmax = maxlevel; /* Max level. */ + tank->A = diam; /* Diameter. */ + tank->Pat = p; /* Fixed grade pattern. */ + tank->Kb = MISSING; /* Reaction coeff. */ + /* + ******************************************************************* + NOTE: The min, max, & initial volumes set here are based on a + nominal tank diameter. They will be modified in INPUT1.C if + a volume curve is supplied for this tank. + ******************************************************************* + */ + area = PI * SQR(diam) / 4.0; + tank->Vmin = area * minlevel; + if (minvol > 0.0) + tank->Vmin = minvol; + tank->V0 = tank->Vmin + area * (initlevel - minlevel); + tank->Vmax = tank->Vmin + area * (maxlevel - minlevel); + + tank->Vcurve = vcurve; /* Volume curve */ + tank->MixModel = MIX1; /* Completely mixed */ + tank->V1max = 1.0; /* Compart. size ratio */ + return (0); +} /* end of tankdata */ + +int pipedata(EN_Project *pr) +/* +**-------------------------------------------------------------- +** Input: none +** Output: returns error code +** Purpose: processes pipe data +** Format: +** [PIPE] +** id node1 node2 length diam rcoeff (lcoeff) (status) +**-------------------------------------------------------------- +*/ +{ + int j1, /* Start-node index */ + j2, /* End-node index */ + n; /* # data items */ + EN_LinkType type = EN_PIPE; /* Link type */ + StatType status = OPEN; /* Link status */ + double length, /* Link length */ + diam, /* Link diameter */ + rcoeff, /* Roughness coeff. */ + lcoeff = 0.0; /* Minor loss coeff. */ + + EN_Network *net = &pr->network; + parser_data_t *par = &pr->parser; + Slink *link; + + /* Add new pipe to data base */ + n = par->Ntokens; + if (net->Nlinks == par->MaxLinks) + return (200); + net->Npipes++; + net->Nlinks++; + if (!addlinkID(net, net->Nlinks, par->Tok[0])) + return (215); + + /* Check for valid data */ + if (n < 6) + return (201); + if ((j1 = findnode(net,par->Tok[1])) == 0 || (j2 = findnode(net,par->Tok[2])) == 0) + return (203); + + /*** Updated 10/25/00 ***/ + if (j1 == j2) + return (222); + + if (!getfloat(par->Tok[3], &length) || !getfloat(par->Tok[4], &diam) || + !getfloat(par->Tok[5], &rcoeff)) + return (202); + + if (length <= 0.0 || diam <= 0.0 || rcoeff <= 0.0) + return (202); + + /* Case where either loss coeff. or status supplied */ + if (n == 7) { + if (match(par->Tok[6], w_CV)) + type = EN_CVPIPE; + else if (match(par->Tok[6], w_CLOSED)) + status = CLOSED; + else if (match(par->Tok[6], w_OPEN)) + status = OPEN; + else if (!getfloat(par->Tok[6], &lcoeff)) + return (202); + } + + /* Case where both loss coeff. and status supplied */ + if (n == 8) { + if (!getfloat(par->Tok[6], &lcoeff)) + return (202); + if (match(par->Tok[7], w_CV)) + type = EN_CVPIPE; + else if (match(par->Tok[7], w_CLOSED)) + status = CLOSED; + else if (match(par->Tok[7], w_OPEN)) + status = OPEN; + else + return (202); + } + if (lcoeff < 0.0) + return (202); + + /* Save pipe data */ + link = &net->Link[net->Nlinks]; + + link->N1 = j1; /* Start-node index */ + link->N2 = j2; /* End-node index */ + link->Len = length; /* Length */ + link->Diam = diam; /* Diameter */ + link->Kc = rcoeff; /* Rough. coeff */ + link->Km = lcoeff; /* Loss coeff */ + link->Kb = MISSING; /* Bulk coeff */ + link->Kw = MISSING; /* Wall coeff */ + link->Type = type; /* Link type */ + link->Stat = status; /* Link status */ + link->Rpt = 0; /* Report flag */ + strcpy(link->Comment, par->Comment); + return (0); +} /* end of pipedata */ + +int pumpdata(EN_Project *pr) +/* +**-------------------------------------------------------------- +** Input: none +** Output: returns error code +** Purpose: processes pump data +** Formats: +** [PUMP] +** (Version 1.x Format): +** id node1 node2 power +** id node1 node2 h1 q1 +** id node1 node2 h0 h1 q1 h2 q2 +** (Version 2 Format): +** id node1 node2 KEYWORD value {KEYWORD value ...} +** where KEYWORD = [POWER,HEAD,PATTERN,SPEED] +**-------------------------------------------------------------- +*/ +{ + int j, j1, /* Start-node index */ + j2, /* End-node index */ + m, n; /* # data items */ + double y; + STmplist *t; /* Pattern record */ + + EN_Network *net = &pr->network; + parser_data_t *par = &pr->parser; + Slink *link; + Spump *pump; + + /* Add new pump to data base */ + n = par->Ntokens; + if (net->Nlinks == par->MaxLinks || net->Npumps == par->MaxPumps) + return (200); + net->Nlinks++; + net->Npumps++; + if (!addlinkID(net, net->Nlinks, par->Tok[0])) + return (215); + + /* Check for valid data */ + if (n < 4) + return (201); + if ((j1 = findnode(net,par->Tok[1])) == 0 || (j2 = findnode(net,par->Tok[2])) == 0) + return (203); + + /*** Updated 10/25/00 ***/ + if (j1 == j2) + return (222); + + /* Save pump data */ + link = &net->Link[net->Nlinks]; + pump = &net->Pump[net->Npumps]; + + link->N1 = j1; /* Start-node index. */ + link->N2 = j2; /* End-node index. */ + link->Diam = 0; /* no longer Pump index. */ + link->Len = 0.0; /* Link length. */ + link->Kc = 1.0; /* Speed factor. */ + link->Km = 0.0; /* Horsepower. */ + link->Kb = 0.0; + link->Kw = 0.0; + link->Type = EN_PUMP; /* Link type. */ + link->Stat = OPEN; /* Link status. */ + link->Rpt = 0; /* Report flag. */ + strcpy(link->Comment, par->Comment); + pump->Link = net->Nlinks; /* Link index. */ + pump->Ptype = NOCURVE; /* Type of pump curve -- "NOCURVE" is a placeholder. this may be modified in getpumpparams() */ + pump->Hcurve = 0; /* Pump curve index */ + pump->Ecurve = 0; /* Effic. curve index */ + pump->Upat = 0; /* Utilization pattern*/ + pump->Ecost = 0.0; /* Unit energy cost */ + pump->Epat = 0; /* Energy cost pattern*/ + + /* If 4-th token is a number then input follows Version 1.x format */ + /* so retrieve pump curve parameters */ + if (getfloat(par->Tok[3], &par->X[0])) { + m = 1; + for (j = 4; j < n; j++) { + if (!getfloat(par->Tok[j], &par->X[m])) { + return (202); + } + m++; + } + return (getpumpcurve(pr,m)); /* Get pump curve params */ + } + + /* Otherwise input follows Version 2 format */ + /* so retrieve keyword/value pairs. */ + m = 4; + while (m < n) { + if (match(par->Tok[m - 1], w_POWER)) /* Const. HP curve */ + { + y = atof(par->Tok[m]); + if (y <= 0.0) + return (202); + pump->Ptype = CONST_HP; + link->Km = y; + } + else if (match(par->Tok[m - 1], w_HEAD)) /* Custom pump curve */ + { + t = findID(par->Tok[m], par->Curvelist); + if (t == NULL) + return (206); + pump->Hcurve = t->i; + } + else if (match(par->Tok[m - 1], w_PATTERN)) /* Speed/status pattern */ + { + t = findID(par->Tok[m], par->Patlist); + if (t == NULL) + return (205); + pump->Upat = t->i; + } + else if (match(par->Tok[m - 1], w_SPEED)) /* Speed setting */ + { + if (!getfloat(par->Tok[m], &y)) + return (202); + if (y < 0.0) + return (202); + link->Kc = y; + } + else { + return (201); + } + m = m + 2; /* Skip to next keyword token */ + } + return (0); +} /* end of pumpdata */ + +int valvedata(EN_Project *pr) +/* +**-------------------------------------------------------------- +** Input: none +** Output: returns error code +** Purpose: processes valve data +** Format: +** [VALVE] +** id node1 node2 diam type setting (lcoeff) +**-------------------------------------------------------------- +*/ +{ + int j1, /* Start-node index */ + j2, /* End-node index */ + n; /* # data items */ + char status = ACTIVE, /* Valve status */ + type; /* Valve type */ + double diam = 0.0, /* Valve diameter */ + setting, /* Valve setting */ + lcoeff = 0.0; /* Minor loss coeff. */ + STmplist *t; /* Curve record */ + + EN_Network *net = &pr->network; + parser_data_t *par = &pr->parser; + Slink *link; + + /* Add new valve to data base */ + n = par->Ntokens; + if (net->Nlinks == par->MaxLinks || net->Nvalves == par->MaxValves) + return (200); + net->Nvalves++; + net->Nlinks++; + if (!addlinkID(net, net->Nlinks, par->Tok[0])) + return (215); + + /* Check for valid data */ + if (n < 6) + return (201); + if ((j1 = findnode(net,par->Tok[1])) == 0 || (j2 = findnode(net,par->Tok[2])) == 0) + return (203); + + /*** Updated 10/25/00 ***/ + if (j1 == j2) + return (222); + + if (match(par->Tok[4], w_PRV)) + type = EN_PRV; + else if (match(par->Tok[4], w_PSV)) + type = EN_PSV; + else if (match(par->Tok[4], w_PBV)) + type = EN_PBV; + else if (match(par->Tok[4], w_FCV)) + type = EN_FCV; + else if (match(par->Tok[4], w_TCV)) + type = EN_TCV; + else if (match(par->Tok[4], w_GPV)) + type = EN_GPV; + else + return (201); /* Illegal valve type.*/ + if (!getfloat(par->Tok[3], &diam)) + return (202); + if (diam <= 0.0) + return (202); /* Illegal diameter.*/ + if (type == EN_GPV) /* Headloss curve for GPV */ + { + t = findID(par->Tok[5], par->Curvelist); + if (t == NULL) + return (206); + setting = t->i; + + /*** Updated 9/7/00 ***/ + status = OPEN; + + } else if (!getfloat(par->Tok[5], &setting)) + return (202); + if (n >= 7 && !getfloat(par->Tok[6], &lcoeff)) + return (202); + + /* Check that PRV, PSV, or FCV not connected to a tank & */ + /* check for illegal connections between pairs of valves.*/ + if ((j1 > net->Njuncs || j2 > net->Njuncs) && + (type == EN_PRV || type == EN_PSV || type == EN_FCV)) + return (219); + if (!valvecheck(pr, type, j1, j2)) + return (220); + + /* Save valve data */ + link = &net->Link[net->Nlinks]; + link->N1 = j1; /* Start-node index. */ + link->N2 = j2; /* End-node index. */ + link->Diam = diam; /* Valve diameter. */ + link->Len = 0.0; /* Link length. */ + link->Kc = setting; /* Valve setting. */ + link->Km = lcoeff; /* Loss coeff */ + link->Kb = 0.0; + link->Kw = 0.0; + link->Type = type; /* Valve type. */ + link->Stat = status; /* Valve status. */ + link->Rpt = 0; /* Report flag. */ + strcpy(link->Comment, par->Comment); + net->Valve[net->Nvalves].Link = net->Nlinks; /* Link index. */ + return (0); +} /* end of valvedata */ + +int patterndata(EN_Project *pr) +/* +**-------------------------------------------------------------- +** Input: none +** Output: returns error code +** Purpose: processes time pattern data +** Format: +** [PATTERNS] +** id mult1 mult2 ..... +**-------------------------------------------------------------- +*/ +{ + EN_Network *net = &pr->network; + parser_data_t *par = &pr->parser; + + int i, n; + double x; + SFloatlist *f; + STmplist *p; + n = par->Ntokens - 1; + if (n < 1) + return (201); /* Too few values */ + if ( /* Check for new pattern */ + par->PrevPat != NULL && strcmp(par->Tok[0], par->PrevPat->ID) == 0) + p = par->PrevPat; + else + p = findID(par->Tok[0], par->Patlist); + if (p == NULL) + return (205); + for (i = 1; i <= n; i++) /* Add multipliers to list */ + { + if (!getfloat(par->Tok[i], &x)) + return (202); + f = (SFloatlist *)malloc(sizeof(SFloatlist)); + if (f == NULL) + return (101); + f->value = x; + f->next = p->x; + p->x = f; + } + net->Pattern[p->i].Length += n; /* Save # multipliers for pattern */ + par->PrevPat = p; /* Set previous pattern pointer */ + return (0); +} /* end of patterndata */ + +int curvedata(EN_Project *pr) +/* +**------------------------------------------------------ +** Input: none +** Output: returns error code +** Purpose: processes curve data +** Format: +** [CURVES] +** CurveID x-value y-value +**------------------------------------------------------ +*/ +{ + EN_Network *net = &pr->network; + parser_data_t *par = &pr->parser; + + double x, y; + SFloatlist *fx, *fy; + STmplist *c; + + /* Check for valid curve ID */ + if (par->Ntokens < 3) + return (201); + if (par->PrevCurve != NULL && strcmp(par->Tok[0], par->PrevCurve->ID) == 0) + c = par->PrevCurve; + else + c = findID(par->Tok[0], par->Curvelist); + if (c == NULL) + return (205); + + /* Check for valid data */ + if (!getfloat(par->Tok[1], &x)) + return (202); + if (!getfloat(par->Tok[2], &y)) + return (202); + + /* Add new data point to curve's linked list */ + fx = (SFloatlist *)malloc(sizeof(SFloatlist)); + fy = (SFloatlist *)malloc(sizeof(SFloatlist)); + if (fx == NULL || fy == NULL) { + return (101); + } + fx->value = x; + fx->next = c->x; + c->x = fx; + fy->value = y; + fy->next = c->y; + c->y = fy; + net->Curve[c->i].Npts++; + + /* Save the pointer to this curve */ + par->PrevCurve = c; + return (0); +} + +int coordata(EN_Project *pr) +/* + **-------------------------------------------------------------- + ** Input: none + ** Output: returns error code + ** Purpose: processes coordinate data + ** Format: + ** [COORD] + ** id x y + **-------------------------------------------------------------- + */ +{ + EN_Network *net = &pr->network; + parser_data_t *par = &pr->parser; + + double x, y; + int j; + Scoord *coord; + Snode *node; + + /* Check for valid node ID */ + if (par->Ntokens < 3) + return (201); + + /* Check for valid data */ + if ((j = findnode(net, par->Tok[0])) == 0) + return (203); + if (!getfloat(par->Tok[1], &x)) + return (202); + if (!getfloat(par->Tok[2], &y)) + return (202); + + /* Save coord data */ + coord = &net->Coord[j]; + node = &net->Node[j]; + strncpy(coord->ID, node->ID, MAXID); + coord->X = x; + coord->Y = y; + coord->HaveCoords = TRUE; + + return (0); +} /* end of coordata */ + +int demanddata(EN_Project *pr) +/* +**-------------------------------------------------------------- +** Input: none +** Output: returns error code +** Purpose: processes node demand data +** Format: +** [DEMANDS] +** MULTIPLY factor +** node base_demand (pattern) +** +** NOTE: Demands entered in this section replace those +** entered in the [JUNCTIONS] section +**-------------------------------------------------------------- +*/ +{ + EN_Network *net = &pr->network; + hydraulics_t *hyd = &pr->hydraulics; + parser_data_t *par = &pr->parser; + + int j, n, p = 0; + double y; + Pdemand demand; + STmplist *pat; + + /* Extract data from tokens */ + n = par->Ntokens; + if (n < 2) + return (201); + if (!getfloat(par->Tok[1], &y)) + return (202); + + /* If MULTIPLY command, save multiplier */ + if (match(par->Tok[0], w_MULTIPLY)) { + if (y <= 0.0) + return (202); + else + hyd->Dmult = y; + return (0); + } + + /* Otherwise find node (and pattern) being referenced */ + if ((j = findnode(net, par->Tok[0])) == 0) + return (208); + if (j > net->Njuncs) + return (208); + if (n >= 3) { + pat = findID(par->Tok[2], par->Patlist); + if (pat == NULL) + return (205); + p = pat->i; + } + + /* Replace any demand entered in [JUNCTIONS] section */ + + demand = net->Node[j].D; + if (hyd->NodeDemand[j] != MISSING) { + // first category encountered will overwrite "dummy" demand category + // with what is specified in this section + demand->Base = y; + demand->Pat = p; + hyd->NodeDemand[j] = MISSING; // marker - next iteration will append a new category. + } + else { // add new demand to junction + demand = (struct Sdemand *)malloc(sizeof(struct Sdemand)); + if (demand == NULL) + return (101); + demand->Base = y; + demand->Pat = p; + demand->next = net->Node[j].D; + net->Node[j].D = demand; + } + return (0); +} /* end of demanddata */ + +int controldata(EN_Project *pr) +/* +**-------------------------------------------------------------- +** Input: none +** Output: returns error code +** Purpose: processes simple controls +** Formats: +** [CONTROLS] +** LINK linkID setting IF NODE nodeID {BELOW/ABOVE} level +** LINK linkID setting AT TIME value (units) +** LINK linkID setting AT CLOCKTIME value (units) +** (0) (1) (2) (3) (4) (5) (6) (7) +**-------------------------------------------------------------- +*/ +{ + + EN_Network *net = &pr->network; + parser_data_t *par = &pr->parser; + + int i = 0, /* Node index */ + k, /* Link index */ + n; /* # data items */ + StatType status = ACTIVE; /* Link status */ + ControlType c_type; /* control type */ + EN_LinkType l_type; /* Link Type */ + double setting = MISSING, /* Link setting */ + time = 0.0, /* Simulation time */ + level = 0.0; /* Pressure or tank level */ + Scontrol *control; + + /* Check for sufficient number of input tokens */ + n = par->Ntokens; + if (n < 6) + return (201); + + /* Check that controlled link exists */ + k = findlink(net,par->Tok[1]); + if (k == 0) + return (204); + l_type = net->Link[k].Type; + if (l_type == EN_CVPIPE) { + return (207); /* Cannot control check valve. */ + } + /*** Updated 9/7/00 ***/ + /* Parse control setting into a status level or numerical setting. */ + if (match(par->Tok[2], w_OPEN)) { + status = OPEN; + if (l_type == EN_PUMP) + setting = 1.0; + if (l_type == EN_GPV) + setting = net->Link[k].Kc; + } else if (match(par->Tok[2], w_CLOSED)) { + status = CLOSED; + if (l_type == EN_PUMP) + setting = 0.0; + if (l_type == EN_GPV) + setting = net->Link[k].Kc; + } else if (l_type == EN_GPV) { + return (206); + } else if (!getfloat(par->Tok[2], &setting)) { + return (202); + } + + /*** Updated 3/1/01 ***/ + /* Set status for pump in case speed setting was supplied */ + /* or for pipe if numerical setting was supplied */ + + if (l_type == EN_PUMP || l_type == EN_PIPE) { + if (setting != MISSING) { + if (setting < 0.0) + return (202); + else if (setting == 0.0) + status = CLOSED; + else + status = OPEN; + } + } + + /* Determine type of control */ + if (match(par->Tok[4], w_TIME)) { + c_type = TIMER; + } else if (match(par->Tok[4], w_CLOCKTIME)) { + c_type = TIMEOFDAY; + } else { + if (n < 8) + return (201); + + if ((i = findnode(net, par->Tok[5])) == 0) + return (203); + + if (match(par->Tok[6], w_BELOW)) + c_type = LOWLEVEL; + else if (match(par->Tok[6], w_ABOVE)) + c_type = HILEVEL; + else + return (201); + } + + /* Parse control level or time */ + switch (c_type) { + case TIMER: + case TIMEOFDAY: + if (n == 6) + time = hour(par->Tok[5], ""); + if (n == 7) + time = hour(par->Tok[5], par->Tok[6]); + if (time < 0.0) + return (201); + break; + case LOWLEVEL: + case HILEVEL: + if (!getfloat(par->Tok[7], &level)) + return (202); + break; + } + + /* Fill in fields of control data structure */ + net->Ncontrols++; + if (net->Ncontrols > par->MaxControls) + return (200); + + control = &net->Control[net->Ncontrols]; + control->Link = k; + control->Node = i; + control->Type = c_type; + control->Status = status; + control->Setting = setting; + control->Time = (long)(3600.0 * time); + if (c_type == TIMEOFDAY) + control->Time %= SECperDAY; + control->Grade = level; + return (0); +} /* end of controldata */ + +int sourcedata(EN_Project *pr) +/* +**-------------------------------------------------------------- +** Input: none +** Output: returns error code +** Purpose: processes water quality source data +** Formats: +** [SOURCE] +** node sourcetype quality (pattern start stop) +** +** NOTE: units of mass-based source are mass/min +**-------------------------------------------------------------- +*/ +{ + EN_Network *net = &pr->network; + parser_data_t *par = &pr->parser; + + int i, /* Token with quality value */ + j, /* Node index */ + n, /* # data items */ + p = 0; /* Time pattern */ + char type = CONCEN; /* Source type */ + double c0 = 0; /* Init. quality */ + STmplist *pat; + Psource source; + + n = par->Ntokens; + if (n < 2) + return (201); + if ((j = findnode(net, par->Tok[0])) == 0) + return (203); + /* NOTE: Under old format, SourceType not supplied so let */ + /* i = index of token that contains quality value. */ + i = 2; + if (match(par->Tok[1], w_CONCEN)) + type = CONCEN; + else if (match(par->Tok[1], w_MASS)) + type = MASS; + else if (match(par->Tok[1], w_SETPOINT)) + type = SETPOINT; + else if (match(par->Tok[1], w_FLOWPACED)) + type = FLOWPACED; + else + i = 1; + if (!getfloat(par->Tok[i], &c0)) + return (202); /* Illegal WQ value */ + + if (n > i + 1 && strlen(par->Tok[i + 1]) > 0 && + strcmp(par->Tok[i + 1], "*") != 0) + { + pat = findID(par->Tok[i + 1], par->Patlist); + if (pat == NULL) + return (205); /* Illegal pattern. */ + p = pat->i; + } + + source = (struct Ssource *)malloc(sizeof(struct Ssource)); + if (source == NULL) + return (101); + source->C0 = c0; + source->Pat = p; + source->Type = type; + net->Node[j].S = source; + return (0); +} /* end of sourcedata */ + +int emitterdata(EN_Project *pr) +/* +**-------------------------------------------------------------- +** Input: none +** Output: returns error code +** Purpose: processes junction emitter data +** Format: +** [EMITTER] +** node K +**-------------------------------------------------------------- +*/ +{ + EN_Network *net = &pr->network; + parser_data_t *par = &pr->parser; + + int j, /* Node index */ + n; /* # data items */ + double k; /* Flow coeff, */ + + n = par->Ntokens; + if (n < 2) + return (201); + if ((j = findnode(net,par->Tok[0])) == 0) + return (203); + if (j > net->Njuncs) + return (209); /* Not a junction.*/ + if (!getfloat(par->Tok[1], &k)) + return (202); + if (k < 0.0) + return (202); + net->Node[j].Ke = k; + return (0); +} + +int qualdata(EN_Project *pr) +/* +**-------------------------------------------------------------- +** Input: none +** Output: returns error code +** Purpose: processes initial water quality data +** Formats: +** [QUALITY] +** node initqual +** node1 node2 initqual +**-------------------------------------------------------------- +*/ +{ + EN_Network *net = &pr->network; + parser_data_t *par = &pr->parser; + Snode *Node = net->Node; + + int j, n; + long i, i0, i1; + double c0; + + if (net->Nnodes == 0) + return (208); /* No nodes defined yet */ + n = par->Ntokens; + if (n < 2) + return (0); + if (n == 2) /* Single node entered */ + { + if ((j = findnode(net,par->Tok[0])) == 0) + return (0); + if (!getfloat(par->Tok[1], &c0)) + return (209); + Node[j].C0 = c0; + } else /* Node range entered */ + { + if (!getfloat(par->Tok[2], &c0)) + return (209); + + /* If numerical range supplied, then use numerical comparison */ + if ((i0 = atol(par->Tok[0])) > 0 && (i1 = atol(par->Tok[1])) > 0) { + for (j = 1; j <= net->Nnodes; j++) { + i = atol(Node[j].ID); + if (i >= i0 && i <= i1) + Node[j].C0 = c0; + } + } else { + for (j = 1; j <= net->Nnodes; j++) + if ((strcmp(par->Tok[0], Node[j].ID) <= 0) && + (strcmp(par->Tok[1], Node[j].ID) >= 0)) + Node[j].C0 = c0; + } + } + return (0); +} /* end of qualdata */ + +int reactdata(EN_Project *pr) +/* +**-------------------------------------------------------------- +** Input: none +** Output: returns error code +** Purpose: processes reaction coeff. data +** Formats: +** [REACTIONS] +** ORDER {BULK/WALL/TANK} value +** GLOBAL BULK coeff +** GLOBAL WALL coeff +** BULK link1 (link2) coeff +** WALL link1 (link2) coeff +** TANK node1 (node2) coeff +** LIMITING POTENTIAL value +** ROUGHNESS CORRELATION value +**-------------------------------------------------------------- +*/ +{ + EN_Network *net = &pr->network; + quality_t *qu = &pr->quality; + parser_data_t *par = &pr->parser; + + int item, j, n; + long i, i1, i2; + double y; + + /* Skip line if insufficient data */ + n = par->Ntokens; + if (n < 3) + return (0); + + /* Process input depending on keyword */ + if (match(par->Tok[0], w_ORDER)) /* Reaction order */ + { + if (!getfloat(par->Tok[n - 1], &y)) + return (213); + if (match(par->Tok[1], w_BULK)) + qu->BulkOrder = y; + else if (match(par->Tok[1], w_TANK)) + qu->TankOrder = y; + else if (match(par->Tok[1], w_WALL)) { + if (y == 0.0) + qu->WallOrder = 0.0; + else if (y == 1.0) + qu->WallOrder = 1.0; + else + return (213); + } else + return (213); + return (0); + } + if (match(par->Tok[0], w_ROUGHNESS)) /* Roughness factor */ + { + if (!getfloat(par->Tok[n - 1], &y)) + return (213); + qu->Rfactor = y; + return (0); + } + if (match(par->Tok[0], w_LIMITING)) /* Limiting potential */ + { + if (!getfloat(par->Tok[n - 1], &y)) + return (213); + /*if (y < 0.0) return(213);*/ + qu->Climit = y; + return (0); + } + if (match(par->Tok[0], w_GLOBAL)) /* Global rates */ + { + if (!getfloat(par->Tok[n - 1], &y)) + return (213); + if (match(par->Tok[1], w_BULK)) + qu->Kbulk = y; + else if (match(par->Tok[1], w_WALL)) + qu->Kwall = y; + else + return (201); + return (0); + } + if (match(par->Tok[0], w_BULK)) + item = 1; /* Individual rates */ + else if (match(par->Tok[0], w_WALL)) + item = 2; + else if (match(par->Tok[0], w_TANK)) + item = 3; + else + return (201); + strcpy(par->Tok[0], par->Tok[1]); /* Save id in par->Tok[0] */ + if (item == 3) /* Tank rates */ + { + if (!getfloat(par->Tok[n - 1], &y)) + return (209); /* Rate coeff. */ + if (n == 3) { + if ((j = findnode(net,par->Tok[1])) <= net->Njuncs) + return (0); + net->Tank[j - net->Njuncs].Kb = y; + } else { + /* If numerical range supplied, then use numerical comparison */ + if ((i1 = atol(par->Tok[1])) > 0 && (i2 = atol(par->Tok[2])) > 0) { + for (j = net->Njuncs + 1; j <= net->Nnodes; j++) { + i = atol(net->Node[j].ID); + if (i >= i1 && i <= i2) + net->Tank[j - net->Njuncs].Kb = y; + } + } else + for (j = net->Njuncs + 1; j <= net->Nnodes; j++) + if ((strcmp(par->Tok[1], net->Node[j].ID) <= 0) && + (strcmp(par->Tok[2], net->Node[j].ID) >= 0)) + net->Tank[j - net->Njuncs].Kb = y; + } + } else /* Link rates */ + { + if (!getfloat(par->Tok[n - 1], &y)) + return (211); /* Rate coeff. */ + if (net->Nlinks == 0) + return (0); + if (n == 3) /* Single link */ + { + if ((j = findlink(net, par->Tok[1])) == 0) + return (0); + if (item == 1) + net->Link[j].Kb = y; + else + net->Link[j].Kw = y; + } else /* Range of links */ + { + /* If numerical range supplied, then use numerical comparison */ + if ((i1 = atol(par->Tok[1])) > 0 && (i2 = atol(par->Tok[2])) > 0) { + for (j = 1; j <= net->Nlinks; j++) { + i = atol(net->Link[j].ID); + if (i >= i1 && i <= i2) { + if (item == 1) + net->Link[j].Kb = y; + else + net->Link[j].Kw = y; + } + } + } else + for (j = 1; j <= net->Nlinks; j++) + if ((strcmp(par->Tok[1], net->Link[j].ID) <= 0) && + (strcmp(par->Tok[2], net->Link[j].ID) >= 0)) { + if (item == 1) + net->Link[j].Kb = y; + else + net->Link[j].Kw = y; + } + } + } + return (0); +} /* end of reactdata */ + +int mixingdata(EN_Project *pr) +/* +**------------------------------------------------------------- +** Input: none +** Output: returns error code +** Purpose: processes tank mixing data +** Format: +** [MIXING] +** TankID MixModel FractVolume +**------------------------------------------------------------- +*/ +{ + + EN_Network *net = &pr->network; + parser_data_t *par = &pr->parser; + + int i, j, n; + double v; + + if (net->Nnodes == 0) + return (208); /* No nodes defined yet */ + n = par->Ntokens; + if (n < 2) + return (0); + if ((j = findnode(net, par->Tok[0])) <= net->Njuncs) + return (0); + if ((i = findmatch(par->Tok[1], MixTxt)) < 0) + return (201); + v = 1.0; + if ((i == MIX2) && (n == 3) && + (!getfloat(par->Tok[2], &v)) /* Get frac. vol. for 2COMP model */ + ) + return (209); + if (v == 0.0) + v = 1.0; /* v can't be zero */ + n = j - net->Njuncs; + if (net->Tank[n].A == 0.0) + return (0); /* Tank is a reservoir */ + net->Tank[n].MixModel = (char)i; + net->Tank[n].V1max = v; + return (0); +} + +int statusdata(EN_Project *pr) +/* +**-------------------------------------------------------------- +** Input: none +** Output: returns error code +** Purpose: processes link initial status data +** Formats: +** [STATUS] +** link value +** link1 (link2) value +**-------------------------------------------------------------- +*/ +{ + EN_Network *net = &pr->network; + parser_data_t *par = &pr->parser; + + int j, n; + long i, i0, i1; + double y = 0.0; + char status = ACTIVE; + + if (net->Nlinks == 0) + return (210); + n = par->Ntokens - 1; + if (n < 1) + return (201); + + /* Check for legal status setting */ + if (match(par->Tok[n], w_OPEN)) + status = OPEN; + else if (match(par->Tok[n], w_CLOSED)) + status = CLOSED; + else if (!getfloat(par->Tok[n], &y)) + return (211); + if (y < 0.0) + return (211); + + /* Single link ID supplied */ + if (n == 1) { + if ((j = findlink(net, par->Tok[0])) == 0) + return (0); + /* Cannot change status of a Check Valve */ + if (net->Link[j].Type == EN_CVPIPE) + return (211); + + /*** Updated 9/7/00 ***/ + /* Cannot change setting for a GPV */ + if (net->Link[j].Type == EN_GPV && status == ACTIVE) + return (211); + + changestatus(net, j, status, y); + } + + /* Range of ID's supplied */ + else { + /* Numerical range supplied */ + if ((i0 = atol(par->Tok[0])) > 0 && (i1 = atol(par->Tok[1])) > 0) { + for (j = 1; j <= net->Nlinks; j++) { + i = atol(net->Link[j].ID); + if (i >= i0 && i <= i1) + changestatus(net, j, status, y); + } + } else + for (j = 1; j <= net->Nlinks; j++) + if ((strcmp(par->Tok[0], net->Link[j].ID) <= 0) && + (strcmp(par->Tok[1], net->Link[j].ID) >= 0)) + changestatus(net, j, status, y); + } + return (0); +} /* end of statusdata */ + +int energydata(EN_Project *pr) +/* +**-------------------------------------------------------------- +** Input: none +** Output: returns error code +** Purpose: processes pump energy data +** Formats: +** [ENERGY] +** GLOBAL {PRICE/PATTERN/EFFIC} value +** PUMP id {PRICE/PATTERN/EFFIC} value +** DEMAND CHARGE value +**-------------------------------------------------------------- +*/ +{ + EN_Network *net = &pr->network; + hydraulics_t *hyd = &pr->hydraulics; + parser_data_t *par = &pr->parser; + + Slink *Link = net->Link; + Spump *Pump = net->Pump; + + int j, k, n; + double y; + STmplist *t; + + /* Check for sufficient data */ + n = par->Ntokens; + if (n < 3) + return (201); + + /* Check first keyword */ + if (match(par->Tok[0], w_DMNDCHARGE)) /* Demand charge */ + { + if (!getfloat(par->Tok[2], &y)) + return (213); + hyd->Dcost = y; + return (0); + } + if (match(par->Tok[0], w_GLOBAL)) /* Global parameter */ + { + j = 0; + } else if (match(par->Tok[0], w_PUMP)) /* Pump-specific parameter */ + { + if (n < 4) + return (201); + k = findlink(net,par->Tok[1]); /* Check that pump exists */ + if (k == 0) + return (216); + if (Link[k].Type != EN_PUMP) + return (216); + j = findpump(net, k); + } else + return (201); + + /* Find type of energy parameter */ + if (match(par->Tok[n - 2], w_PRICE)) /* Energy price */ + { + if (!getfloat(par->Tok[n - 1], &y)) { + if (j == 0) + return (213); + else + return (217); + } + if (j == 0) + hyd->Ecost = y; + else + Pump[j].Ecost = y; + return (0); + } else if (match(par->Tok[n - 2], w_PATTERN)) /* Price pattern */ + { + t = findID(par->Tok[n - 1], par->Patlist); /* Check if pattern exists */ + if (t == NULL) { + if (j == 0) + return (213); + else + return (217); + } + if (j == 0) + hyd->Epat = t->i; + else + Pump[j].Epat = t->i; + return (0); + } else if (match(par->Tok[n - 2], w_EFFIC)) /* Pump efficiency */ + { + if (j == 0) { + if (!getfloat(par->Tok[n - 1], &y)) + return (213); + if (y <= 0.0) + return (213); + hyd->Epump = y; + } else { + t = findID(par->Tok[n - 1], par->Curvelist); /* Check if curve exists */ + if (t == NULL) + return (217); + Pump[j].Ecurve = t->i; + } + return (0); + } + return (201); +} + +int reportdata(EN_Project *pr) +/* +**-------------------------------------------------------------- +** Input: none +** Output: returns error code +** Purpose: processes report options data +** Formats: +** PAGE linesperpage +** STATUS {NONE/YES/FULL} +** SUMMARY {YES/NO} +** MESSAGES {YES/NO} +** ENERGY {NO/YES} +** NODES {NONE/ALL} +** NODES node1 node2 ... +** LINKS {NONE/ALL} +** LINKS link1 link2 ... +** FILE filename +** variable {YES/NO} +** variable {BELOW/ABOVE/PRECISION} value +**-------------------------------------------------------------- +*/ +{ + + EN_Network *net = &pr->network; + report_options_t *rep = &pr->report; + parser_data_t *par = &pr->parser; + + + int i, j, n; + double y; + + n = par->Ntokens - 1; + if (n < 1) + return (201); + + /* Value for page size */ + if (match(par->Tok[0], w_PAGE)) { + if (!getfloat(par->Tok[n], &y)) + return (213); + if (y < 0.0 || y > 255.0) + return (213); + rep->PageSize = (int)y; + return (0); + } + + /* Request that status reports be written */ + if (match(par->Tok[0], w_STATUS)) { + if (match(par->Tok[n], w_NO)) + rep->Statflag = FALSE; + if (match(par->Tok[n], w_YES)) + rep->Statflag = TRUE; + if (match(par->Tok[n], w_FULL)) + rep->Statflag = FULL; + return (0); + } + + /* Request summary report */ + if (match(par->Tok[0], w_SUMMARY)) { + if (match(par->Tok[n], w_NO)) + rep->Summaryflag = FALSE; + if (match(par->Tok[n], w_YES)) + rep->Summaryflag = TRUE; + return (0); + } + + /* Request error/warning message reporting */ + if (match(par->Tok[0], w_MESSAGES)) { + if (match(par->Tok[n], w_NO)) + rep->Messageflag = FALSE; + if (match(par->Tok[n], w_YES)) + rep->Messageflag = TRUE; + return (0); + } + + /* Request an energy usage report */ + if (match(par->Tok[0], w_ENERGY)) { + if (match(par->Tok[n], w_NO)) + rep->Energyflag = FALSE; + if (match(par->Tok[n], w_YES)) + rep->Energyflag = TRUE; + return (0); + } + + /* Particular reporting nodes specified */ + if (match(par->Tok[0], w_NODE)) { + if (match(par->Tok[n], w_NONE)) + rep->Nodeflag = 0; /* No nodes */ + else if (match(par->Tok[n], w_ALL)) + rep->Nodeflag = 1; /* All nodes */ + else { + if (net->Nnodes == 0) + return (208); + for (i = 1; i <= n; i++) { + if ((j = findnode(net,par->Tok[i])) == 0) + return (208); + net->Node[j].Rpt = 1; + } + rep->Nodeflag = 2; + } + return (0); + } + + /* Particular reporting links specified */ + if (match(par->Tok[0], w_LINK)) { + if (match(par->Tok[n], w_NONE)) + rep->Linkflag = 0; + else if (match(par->Tok[n], w_ALL)) + rep->Linkflag = 1; + else { + if (net->Nlinks == 0) + return (210); + for (i = 1; i <= n; i++) { + if ((j = findlink(net,par->Tok[i])) == 0) + return (210); + net->Link[j].Rpt = 1; + } + rep->Linkflag = 2; + } + return (0); + } + + /* Check if input is a reporting criterion. */ + + /*** Special case needed to distinguish "HEAD" from "HEADLOSS" ***/ //(2.00.11 + //- LR) + if (strcomp(par->Tok[0], t_HEADLOSS)) + i = HEADLOSS; + else + i = findmatch(par->Tok[0], Fldname); + if (i >= 0) + /*****************************************************************/ //(2.00.11 + //- LR) + { + if (i > FRICTION) + return (201); + if (par->Ntokens == 1 || match(par->Tok[1], w_YES)) { + rep->Field[i].Enabled = TRUE; + return (0); + } + if (match(par->Tok[1], w_NO)) { + rep->Field[i].Enabled = FALSE; + return (0); + } + if (par->Ntokens < 3) + return (201); + if (match(par->Tok[1], w_BELOW)) + j = LOW; /* Get relation operator */ + else if (match(par->Tok[1], w_ABOVE)) + j = HI; /* or precision keyword */ + else if (match(par->Tok[1], w_PRECISION)) + j = PREC; + else + return (201); + if (!getfloat(par->Tok[2], &y)) + return (201); + if (j == PREC) { + rep->Field[i].Enabled = TRUE; + rep->Field[i].Precision = ROUND(y); + } else + rep->Field[i].RptLim[j] = y; /* Report limit value */ + return (0); + } + + /* Name of external report file */ + if (match(par->Tok[0], w_FILE)) { + strncpy(rep->Rpt2Fname, par->Tok[1], MAXFNAME); + return (0); + } + + /* If get to here then return error condition */ + return (201); +} /* end of reportdata */ + +int timedata(EN_Project *pr) +/* +**-------------------------------------------------------------- +** Input: none +** Output: returns error code +** Purpose: processes time options data +** Formats: +** STATISTIC {NONE/AVERAGE/MIN/MAX/RANGE} +** DURATION value (units) +** HYDRAULIC TIMESTEP value (units) +** QUALITY TIMESTEP value (units) +** MINIMUM TRAVELTIME value (units) +** RULE TIMESTEP value (units) +** PATTERN TIMESTEP value (units) +** PATTERN START value (units) +** REPORT TIMESTEP value (units) +** REPORT START value (units) +** START CLOCKTIME value (AM PM) +**------------------------------------------------------------- +*/ +{ + + report_options_t *rep = &pr->report; + quality_t *qu = &pr->quality; + parser_data_t *par = &pr->parser; + time_options_t *time = &pr->time_options; + + + int n; + long t; + double y; + + n = par->Ntokens - 1; + if (n < 1) + return (201); + + /* Check if setting time statistic flag */ + if (match(par->Tok[0], w_STATISTIC)) { + if (match(par->Tok[n], w_NONE)) + rep->Tstatflag = SERIES; + else if (match(par->Tok[n], w_NO)) + rep->Tstatflag = SERIES; + else if (match(par->Tok[n], w_AVG)) + rep->Tstatflag = AVG; + else if (match(par->Tok[n], w_MIN)) + rep->Tstatflag = MIN; + else if (match(par->Tok[n], w_MAX)) + rep->Tstatflag = MAX; + else if (match(par->Tok[n], w_RANGE)) + rep->Tstatflag = RANGE; + else + return (201); + return (0); + } + + /* Convert text time value to numerical value in seconds */ + /* Examples: + ** 5 = 5 * 3600 sec + ** 5 MINUTES = 5 * 60 sec + ** 13:50 = 13*3600 + 50*60 sec + ** 1:50 pm = (12+1)*3600 + 50*60 sec + */ + + if (!getfloat(par->Tok[n], &y)) { + if ((y = hour(par->Tok[n], "")) < 0.0) { + if ((y = hour(par->Tok[n - 1], par->Tok[n])) < 0.0) + return (213); + } + } + t = (long)(3600.0 * y + 0.5); + /* Process the value assigned to the matched parameter */ + if (match(par->Tok[0], w_DURATION)) + time->Dur = t; /* Simulation duration */ + else if (match(par->Tok[0], w_HYDRAULIC)) + time->Hstep = t; /* Hydraulic time step */ + else if (match(par->Tok[0], w_QUALITY)) + qu->Qstep = t; /* Quality time step */ + else if (match(par->Tok[0], w_RULE)) + time->Rulestep = t; /* Rule time step */ + else if (match(par->Tok[0], w_MINIMUM)) + return (0); /* Not used anymore */ + else if (match(par->Tok[0], w_PATTERN)) { + if (match(par->Tok[1], w_TIME)) + time->Pstep = t; /* Pattern time step */ + else if (match(par->Tok[1], w_START)) + time->Pstart = t; /* Pattern start time */ + else + return (201); + } else if (match(par->Tok[0], w_REPORT)) { + if (match(par->Tok[1], w_TIME)) + time->Rstep = t; /* Reporting time step */ + else if (match(par->Tok[1], w_START)) + time->Rstart = t; /* Reporting start time */ + else + return (201); + } /* Simulation start time*/ + else if (match(par->Tok[0], w_START)) + time->Tstart = t % SECperDAY; + else + return (201); + return (0); +} /* end of timedata */ + +int optiondata(EN_Project *pr) +/* +**-------------------------------------------------------------- +** Input: none +** Output: returns error code +** Purpose: processes [OPTIONS] data +**-------------------------------------------------------------- +*/ +{ + parser_data_t *par = &pr->parser; + + int i, n; + + n = par->Ntokens - 1; + i = optionchoice(pr,n); /* Option is a named choice */ + if (i >= 0) + return (i); + return (optionvalue(pr,n)); /* Option is a numerical value */ +} /* end of optiondata */ + +int optionchoice(EN_Project *pr, int n) +/* +**-------------------------------------------------------------- +** Input: n = index of last input token saved in par->Tok[] +** Output: returns error code or 0 if option belongs to +** those listed below, or -1 otherwise +** Purpose: processes fixed choice [OPTIONS] data +** Formats: +** UNITS CFS/GPM/MGD/IMGD/AFD/LPS/LPM/MLD/CMH/CMD/SI +** PRESSURE PSI/KPA/M +** HEADLOSS H-W/D-W/C-M +** HYDRAULICS USE/SAVE filename +** QUALITY NONE/AGE/TRACE/CHEMICAL (TraceNode) +** MAP filename +** VERIFY filename +** UNBALANCED STOP/CONTINUE {Niter} +** PATTERN id +**-------------------------------------------------------------- +*/ +{ + EN_Network *net = &pr->network; + hydraulics_t *hyd = &pr->hydraulics; + quality_t *qu = &pr->quality; + parser_data_t *par = &pr->parser; + out_file_t *out = &pr->out_files; + + + /* Check if 1st token matches a parameter name and */ + /* process the input for the matched parameter */ + if (n < 0) + return (201); + if (match(par->Tok[0], w_UNITS)) { + if (n < 1) + return (0); + else if (match(par->Tok[1], w_CFS)) + par->Flowflag = CFS; + else if (match(par->Tok[1], w_GPM)) + par->Flowflag = GPM; + else if (match(par->Tok[1], w_AFD)) + par->Flowflag = AFD; + else if (match(par->Tok[1], w_MGD)) + par->Flowflag = MGD; + else if (match(par->Tok[1], w_IMGD)) + par->Flowflag = IMGD; + else if (match(par->Tok[1], w_LPS)) + par->Flowflag = LPS; + else if (match(par->Tok[1], w_LPM)) + par->Flowflag = LPM; + else if (match(par->Tok[1], w_CMH)) + par->Flowflag = CMH; + else if (match(par->Tok[1], w_CMD)) + par->Flowflag = CMD; + else if (match(par->Tok[1], w_MLD)) + par->Flowflag = MLD; + else if (match(par->Tok[1], w_SI)) + par->Flowflag = LPS; + else + return (201); + } else if (match(par->Tok[0], w_PRESSURE)) { + if (n < 1) + return (0); + else if (match(par->Tok[1], w_PSI)) + par->Pressflag = PSI; + else if (match(par->Tok[1], w_KPA)) + par->Pressflag = KPA; + else if (match(par->Tok[1], w_METERS)) + par->Pressflag = METERS; + else + return (201); + } else if (match(par->Tok[0], w_HEADLOSS)) { + if (n < 1) + return (0); + else if (match(par->Tok[1], w_HW)) + hyd->Formflag = HW; + else if (match(par->Tok[1], w_DW)) + hyd->Formflag = DW; + else if (match(par->Tok[1], w_CM)) + hyd->Formflag = CM; + else + return (201); + } else if (match(par->Tok[0], w_HYDRAULIC)) { + if (n < 2) + return (0); + else if (match(par->Tok[1], w_USE)) + out->Hydflag = USE; + else if (match(par->Tok[1], w_SAVE)) + out->Hydflag = SAVE; + else + return (201); + strncpy(out->HydFname, par->Tok[2], MAXFNAME); + } else if (match(par->Tok[0], w_QUALITY)) { + if (n < 1) + return (0); + else if (match(par->Tok[1], w_NONE)) + qu->Qualflag = NONE; + else if (match(par->Tok[1], w_CHEM)) + qu->Qualflag = CHEM; + else if (match(par->Tok[1], w_AGE)) + qu->Qualflag = AGE; + else if (match(par->Tok[1], w_TRACE)) + qu->Qualflag = TRACE; + else { + qu->Qualflag = CHEM; + strncpy(qu->ChemName, par->Tok[1], MAXID); + if (n >= 2) + strncpy(qu->ChemUnits, par->Tok[2], MAXID); + } + if (qu->Qualflag == TRACE) /* Source tracing option */ + { + /* Copy Trace Node ID to par->Tok[0] for error reporting */ + strcpy(par->Tok[0], ""); + if (n < 2) + return (212); + strcpy(par->Tok[0], par->Tok[2]); + qu->TraceNode = findnode(net,par->Tok[2]); + if (qu->TraceNode == 0) + return (212); + strncpy(qu->ChemName, u_PERCENT, MAXID); + strncpy(qu->ChemUnits, par->Tok[2], MAXID); + } + if (qu->Qualflag == AGE) { + strncpy(qu->ChemName, w_AGE, MAXID); + strncpy(qu->ChemUnits, u_HOURS, MAXID); + } + } else if (match(par->Tok[0], w_MAP)) { + if (n < 1) + return (0); + strncpy(pr->MapFname, par->Tok[1], MAXFNAME); /* Map file name */ + } else if (match(par->Tok[0], w_VERIFY)) { + /* Backward compatibility for verification file */ + } else if (match(par->Tok[0], w_UNBALANCED)) /* Unbalanced option */ + { + if (n < 1) + return (0); + if (match(par->Tok[1], w_STOP)) + hyd->ExtraIter = -1; + else if (match(par->Tok[1], w_CONTINUE)) { + if (n >= 2) + hyd->ExtraIter = atoi(par->Tok[2]); + else + hyd->ExtraIter = 0; + } else + return (201); + } else if (match(par->Tok[0], w_PATTERN)) /* Pattern option */ + { + if (n < 1) + return (0); + strncpy(par->DefPatID, par->Tok[1], MAXID); + } else + return (-1); + return (0); +} /* end of optionchoice */ + +int optionvalue(EN_Project *pr, int n) +/* +**------------------------------------------------------------- +** Input: *line = line read from input file +** Output: returns error code +** Purpose: processes numerical value [OPTIONS] data +** Formats: +** DEMAND MULTIPLIER value +** EMITTER EXPONENT value +** VISCOSITY value +** DIFFUSIVITY value +** SPECIFIC GRAVITY value +** TRIALS value +** ACCURACY value +** TOLERANCE value +** SEGMENTS value (not used) +** ------ Undocumented Options ----- +** HTOL value +** QTOL value +** RQTOL value +** CHECKFREQ value +** MAXCHECK value +** DAMPLIMIT value +**-------------------------------------------------------------- +*/ +{ + hydraulics_t *hyd = &pr->hydraulics; + quality_t *qu = &pr->quality; + parser_data_t *par = &pr->parser; + + + int nvalue = 1; /* Index of token with numerical value */ + double y; + + /* Check for obsolete SEGMENTS keyword */ + if (match(par->Tok[0], w_SEGMENTS)) + return (0); + + /* Check for missing value (which is permissible) */ + if (match(par->Tok[0], w_SPECGRAV) || match(par->Tok[0], w_EMITTER) || + match(par->Tok[0], w_DEMAND)) + nvalue = 2; + if (n < nvalue) + return (0); + + /* Check for valid numerical input */ + if (!getfloat(par->Tok[nvalue], &y)) + return (213); + + /* Check for WQ tolerance option (which can be 0) */ + if (match(par->Tok[0], w_TOLERANCE)) { + if (y < 0.0) + return (213); + qu->Ctol = y; /* Quality tolerance*/ + return (0); + } + + /* Check for Diffusivity option */ + if (match(par->Tok[0], w_DIFFUSIVITY)) { + if (y < 0.0) + return (213); + qu->Diffus = y; + return (0); + } + + /* Check for Damping Limit option */ + if (match(par->Tok[0], w_DAMPLIMIT)) { + hyd->DampLimit = y; + return (0); + } + + /* All other options must be > 0 */ + if (y <= 0.0) + return (213); + + /* Assign value to specified option */ + if (match(par->Tok[0], w_VISCOSITY)) + hyd->Viscos = y; /* Viscosity */ + else if (match(par->Tok[0], w_SPECGRAV)) + hyd->SpGrav = y; /* Spec. gravity */ + else if (match(par->Tok[0], w_TRIALS)) + hyd->MaxIter = (int)y; /* Max. trials */ + else if (match(par->Tok[0], w_ACCURACY)) /* Accuracy */ + { + y = MAX(y, 1.e-5); + y = MIN(y, 1.e-1); + hyd->Hacc = y; + } else if (match(par->Tok[0], w_HTOL)) + hyd->Htol = y; + else if (match(par->Tok[0], w_QTOL)) + hyd->Qtol = y; + else if (match(par->Tok[0], w_RQTOL)) { + if (y >= 1.0) + return (213); + hyd->RQtol = y; + } else if (match(par->Tok[0], w_CHECKFREQ)) + hyd->CheckFreq = (int)y; + else if (match(par->Tok[0], w_MAXCHECK)) + hyd->MaxCheck = (int)y; + else if (match(par->Tok[0], w_EMITTER)) + hyd->Qexp = 1.0 / y; + else if (match(par->Tok[0], w_DEMAND)) + hyd->Dmult = y; + else + return (201); + return (0); +} /* end of optionvalue */ + +int getpumpcurve(EN_Project *pr, int n) +/* +**-------------------------------------------------------- +** Input: n = number of parameters for pump curve +** Output: returns error code +** Purpose: processes pump curve data for Version 1.1- +** style input data +** Notes: +** 1. Called by pumpdata() in INPUT3.C +** 2. Current link index & pump index of pump being +** processed is found in global variables Nlinks +** and Npumps, respectively +** 3. Curve data read from input line is found in +** global variables X[0],...X[n-1] +**--------------------------------------------------------- +*/ +{ + EN_Network *net = &pr->network; + parser_data_t *par = &pr->parser; + + double a, b, c, h0, h1, h2, q1, q2; + + Spump *pump = &net->Pump[net->Npumps]; + + if (n == 1) /* Const. HP curve */ + { + if (par->X[0] <= 0.0) + return (202); + pump->Ptype = CONST_HP; + net->Link[net->Nlinks].Km = par->X[0]; + } else { + if (n == 2) /* Generic power curve */ + { + q1 = par->X[1]; + h1 = par->X[0]; + h0 = 1.33334 * h1; + q2 = 2.0 * q1; + h2 = 0.0; + } + else if (n >= 5) /* 3-pt. power curve */ + { + h0 = par->X[0]; + h1 = par->X[1]; + q1 = par->X[2]; + h2 = par->X[3]; + q2 = par->X[4]; + } else + return (202); + pump->Ptype = POWER_FUNC; + if (!powercurve(h0, h1, h2, q1, q2, &a, &b, &c)) + return (206); + pump->H0 = -a; + pump->R = -b; + pump->N = c; + pump->Q0 = q1; + pump->Qmax = pow((-a / b), (1.0 / c)); + pump->Hmax = h0; + } + return (0); +} + +int powercurve(double h0, double h1, double h2, double q1, double q2, double *a, double *b, double *c) +/* +**--------------------------------------------------------- +** Input: h0 = shutoff head +** h1 = design head +** h2 = head at max. flow +** q1 = design flow +** q2 = max. flow +** Output: *a, *b, *c = pump curve coeffs. (H = a-bQ^c), +** Returns 1 if sucessful, 0 otherwise. +** Purpose: computes coeffs. for pump curve +**---------------------------------------------------------- +*/ +{ + double h4, h5; + if (h0 < TINY || h0 - h1 < TINY || h1 - h2 < TINY || q1 < TINY || + q2 - q1 < TINY) + return (0); + *a = h0; + h4 = h0 - h1; + h5 = h0 - h2; + *c = log(h5 / h4) / log(q2 / q1); + if (*c <= 0.0 || *c > 20.0) + return (0); + *b = -h4 / pow(q1, *c); + + /*** Updated 6/24/02 ***/ + if (*b >= 0.0) + return (0); + + return (1); +} + +int valvecheck(EN_Project *pr, int type, int j1, int j2) +/* +**-------------------------------------------------------------- +** Input: type = valve type +** j1 = index of upstream node +** j2 = index of downstream node +** Output: returns 1 for legal connection, 0 otherwise +** Purpose: checks for legal connections between PRVs & PSVs +**-------------------------------------------------------------- +*/ +{ + EN_Network *net = &pr->network; + + int k, vj1, vj2; + EN_LinkType vtype; + + /* Examine each existing valve */ + for (k = 1; k <= net->Nvalves; k++) { + Svalve *valve = &net->Valve[k]; + Slink *link = &net->Link[valve->Link]; + vj1 = link->N1; + vj2 = link->N2; + vtype = link->Type; + + /* Cannot have two PRVs sharing downstream nodes or in series */ + if (vtype == EN_PRV && type == EN_PRV) { + if (vj2 == j2 || vj2 == j1 || vj1 == j2) + return (0); + } + + /* Cannot have two PSVs sharing upstream nodes or in series */ + if (vtype == EN_PSV && type == EN_PSV) { + if (vj1 == j1 || vj1 == j2 || vj2 == j1) + return (0); + } + + /* Cannot have PSV connected to downstream node of PRV */ + if (vtype == EN_PSV && type == EN_PRV && vj1 == j2) + return (0); + if (vtype == EN_PRV && type == EN_PSV && vj2 == j1) + return (0); + + /*** Updated 3/1/01 ***/ + /* Cannot have PSV connected to downstream node of FCV */ + /* nor have PRV connected to upstream node of FCV */ + if (vtype == EN_FCV && type == EN_PSV && vj2 == j1) + return (0); + if (vtype == EN_FCV && type == EN_PRV && vj1 == j2) + return (0); + + /*** Updated 4/14/05 ***/ + if (vtype == EN_PSV && type == EN_FCV && vj1 == j2) + return (0); + if (vtype == EN_PRV && type == EN_FCV && vj2 == j1) + return (0); + } + return (1); +} /* End of valvecheck */ + +void changestatus(EN_Network *net, int j, StatType status, double y) +/* +**-------------------------------------------------------------- +** Input: j = link index +** status = status setting (OPEN, CLOSED) +** y = numerical setting (pump speed, valve +** setting) +** Output: none +** Purpose: changes status or setting of a link +** +** NOTE: If status = ACTIVE, then a numerical setting (y) was +** supplied. If status = OPEN/CLOSED, then numerical +** setting is 0. +**-------------------------------------------------------------- +*/ +{ + Slink *link = &net->Link[j]; + + if (link->Type == EN_PIPE || link->Type == EN_GPV) { + if (status != ACTIVE) + link->Stat = status; + } else if (link->Type == EN_PUMP) { + if (status == ACTIVE) { + link->Kc = y; + status = OPEN; + if (y == 0.0) + status = CLOSED; + } else if (status == OPEN) { + link->Kc = 1.0; + } + link->Stat = status; + } else if (link->Type >= EN_PRV) { + link->Kc = y; + link->Stat = status; + if (status != ACTIVE) { + link->Kc = MISSING; + } + } +} /* end of changestatus */ + +/********************** END OF INPUT3.C ************************/ diff --git a/src/mempool.c b/src/mempool.c index b31ba13..ef7e645 100755 --- a/src/mempool.c +++ b/src/mempool.c @@ -1,205 +1,209 @@ -/* mempool.c -** -** A simple fast 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. -** -*/ - -#include -#ifndef __APPLE__ -#include -#endif -#include "mempool.h" - -/* -** ALLOC_BLOCK_SIZE - adjust this size to suit your installation - it -** should be reasonably large otherwise you will be mallocing a lot. -*/ - -#define ALLOC_BLOCK_SIZE 64000 /*(62*1024)*/ - -/* -** alloc_hdr_t - Header for each block of memory. -*/ - -typedef struct alloc_hdr_s -{ - struct alloc_hdr_s *next; /* Next Block */ - char *block, /* Start of block */ - *free, /* Next free in block */ - *end; /* block + block size */ -} alloc_hdr_t; - -/* -** alloc_root_t - Header for the whole pool. -*/ - -typedef struct alloc_root_s -{ - alloc_hdr_t *first, /* First header in pool */ - *current; /* Current header */ -} alloc_root_t; - -/* -** 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() -{ - 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); -} - - -/* -** AllocInit() -** -** Create a new memory pool with one block. -** Returns pointer to the new pool. -*/ - -DLLEXPORT alloc_handle_t * AllocInit() -{ - 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); -} - - -/* -** Alloc() -** -** Use as a direct replacement for malloc(). Allocates -** memory from the current pool. -*/ - -DLLEXPORT char *Alloc(long size) -{ - alloc_hdr_t *hdr = root->current; - char *ptr; - - /* - ** Align to 4 byte boundary - should be ok for most machines. - ** Change this if your machine has weird alignment requirements. - */ - size = (size + 3) & 0xfffffffc; - - ptr = hdr->free; - hdr->free += size; - - /* Check if the current block is exhausted. */ - - if (hdr->free >= hdr->end) - { - /* Is the next block already allocated? */ - - if (hdr->next != NULL) - { - /* re-use block */ - hdr->next->free = hdr->next->block; - root->current = hdr->next; - } - else - { - /* extend the pool with a new block */ - if ( (hdr->next = AllocHdr()) == NULL) return(NULL); - root->current = hdr->next; - } - - /* set ptr to the first location in the next block */ - ptr = root->current->free; - root->current->free += size; - } - - /* 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; -} +/* mempool.c +** +** A simple fast 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. +** +*/ + +#include +#ifndef __APPLE__ +#include +#endif +#include "mempool.h" + +/* +** ALLOC_BLOCK_SIZE - adjust this size to suit your installation - it +** should be reasonably large otherwise you will be mallocing a lot. +*/ + +#define ALLOC_BLOCK_SIZE 64000 /*(62*1024)*/ + +/* +** alloc_hdr_t - Header for each block of memory. +*/ + +typedef struct alloc_hdr_s +{ + struct alloc_hdr_s *next; /* Next Block */ + char *block, /* Start of block */ + *free, /* Next free in block */ + *end; /* block + block size */ +} alloc_hdr_t; + +/* +** alloc_root_t - Header for the whole pool. +*/ + +typedef struct alloc_root_s +{ + alloc_hdr_t *first, /* First header in pool */ + *current; /* Current header */ +} alloc_root_t; + +/* +** 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() +{ + 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); +} + + +/* +** AllocInit() +** +** Create a new memory pool with one block. +** Returns pointer to the new pool. +*/ + +DLLEXPORT alloc_handle_t * AllocInit() +{ + 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); +} + + +/* +** Alloc() +** +** Use as a direct replacement for malloc(). Allocates +** memory from the current pool. +*/ + +DLLEXPORT char *Alloc(long size) +{ + alloc_hdr_t *hdr = root->current; + char *ptr; + + /* + ** Align to 4 byte boundary - should be ok for most machines. + ** Change this if your machine has weird alignment requirements. + */ + size = (size + 3) & 0xfffffffc; + + ptr = hdr->free; + hdr->free += size; + + /* Check if the current block is exhausted. */ + + if (hdr->free >= hdr->end) + { + /* Is the next block already allocated? */ + + if (hdr->next != NULL) + { + /* re-use block */ + hdr->next->free = hdr->next->block; + root->current = hdr->next; + } + else + { + /* extend the pool with a new block */ + if ( (hdr->next = AllocHdr()) == NULL) return(NULL); + root->current = hdr->next; + } + + /* set ptr to the first location in the next block */ + ptr = root->current->free; + root->current->free += size; + } + + /* 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; +} diff --git a/src/mempool.h b/src/mempool.h index ca04993..4fe3c0f 100755 --- a/src/mempool.h +++ b/src/mempool.h @@ -15,7 +15,7 @@ #ifdef __cplusplus #define DLLEXPORT extern "C" __declspec(dllexport) #else - #define DLLEXPORT __declspec(dllexport) __stdcall + #define DLLEXPORT __declspec(dllexport) __stdcall #endif #elif defined(CYGWIN) #define DLLEXPORT __stdcall @@ -34,10 +34,10 @@ typedef struct long dummy; } alloc_handle_t; -DLLEXPORT alloc_handle_t *AllocInit(void); -DLLEXPORT char *Alloc(long); -DLLEXPORT alloc_handle_t *AllocSetPool(alloc_handle_t *); -DLLEXPORT void AllocReset(void); -DLLEXPORT void AllocFreePool(void); +alloc_handle_t *AllocInit(void); +char *Alloc(long); +alloc_handle_t *AllocSetPool(alloc_handle_t *); +void AllocReset(void); +void AllocFreePool(void); #endif \ No newline at end of file diff --git a/src/output.c b/src/output.c old mode 100755 new mode 100644 index ced8c73..7e231ac --- a/src/output.c +++ b/src/output.c @@ -1,707 +1,839 @@ -/* -********************************************************************* - -OUTPUT.C -- Binary File Transfer Routines for EPANET Program - -VERSION: 2.00 -DATE: 5/8/00 - 8/15/07 (2.00.11) -AUTHOR: L. Rossman - US EPA - NRMRL - -******************************************************************** -*/ - -#include -#include -#ifndef __APPLE__ -#include -#else -#include -#endif -#include -#include "text.h" -#include "types.h" -#include "funcs.h" -#define EXTERN extern -#include "hash.h" -#include "vars.h" - -/* Macro to write x[1] to x[n] to file OutFile: */ -#define FSAVE(n) (fwrite(x+1,sizeof(REAL4),(n),OutFile)) - -int savenetdata() -/* -**--------------------------------------------------------------- -** Input: none -** Output: returns error code -** Purpose: saves input data in original units to binary -** output file using fixed-sized (4-byte) records -**--------------------------------------------------------------- -*/ -{ - int i,nmax; - INT4 *ibuf; - REAL4 *x; - int errcode = 0; - - /* Allocate buffer arrays */ - nmax = MAX(Nnodes,Nlinks) + 1; - nmax = MAX(nmax,15); - ibuf = (INT4 *) calloc(nmax, sizeof(INT4)); - x = (REAL4 *) calloc(nmax, sizeof(REAL4)); - ERRCODE(MEMCHECK(ibuf)); - ERRCODE(MEMCHECK(x)); - - if (!errcode) - { - /* Write integer variables to OutFile */ - ibuf[0] = MAGICNUMBER; - -/*** CODEVERSION replaces VERSION ***/ //(2.00.11 - LR) - ibuf[1] = CODEVERSION; //(2.00.11 - LR) - - ibuf[2] = Nnodes; - ibuf[3] = Ntanks; - ibuf[4] = Nlinks; - ibuf[5] = Npumps; - ibuf[6] = Nvalves; - ibuf[7] = Qualflag; - ibuf[8] = TraceNode; - ibuf[9] = Flowflag; - ibuf[10] = Pressflag; - ibuf[11] = Tstatflag; - ibuf[12] = Rstart; - ibuf[13] = Rstep; - ibuf[14] = Dur; - fwrite(ibuf,sizeof(INT4),15,OutFile); - - /* Write string variables to OutFile */ - fwrite(Title[0],sizeof(char),MAXMSG+1,OutFile); - fwrite(Title[1],sizeof(char),MAXMSG+1,OutFile); - fwrite(Title[2],sizeof(char),MAXMSG+1,OutFile); - fwrite(InpFname,sizeof(char),MAXFNAME+1,OutFile); - fwrite(Rpt2Fname,sizeof(char),MAXFNAME+1,OutFile); - fwrite(ChemName,sizeof(char),MAXID+1,OutFile); - fwrite(Field[QUALITY].Units,sizeof(char),MAXID+1,OutFile); - - /* Write node ID information to OutFile */ - for (i=1; i<=Nnodes; i++) - fwrite(Node[i].ID, MAXID+1, 1, OutFile); - - /* Write link information to OutFile */ - /* (Note: first transfer values to buffer array,*/ - /* then fwrite buffer array at offset of 1 ) */ - for (i=1; i<=Nlinks; i++) - fwrite(Link[i].ID, MAXID+1, 1, OutFile); - for (i=1; i<=Nlinks; i++) ibuf[i] = Link[i].N1; - fwrite(ibuf+1,sizeof(INT4),Nlinks,OutFile); - for (i=1; i<=Nlinks; i++) ibuf[i] = Link[i].N2; - fwrite(ibuf+1,sizeof(INT4),Nlinks,OutFile); - for (i=1; i<=Nlinks; i++) ibuf[i] = Link[i].Type; - fwrite(ibuf+1,sizeof(INT4),Nlinks,OutFile); - - /* Write tank information to OutFile.*/ - for (i=1; i<=Ntanks; i++) ibuf[i] = Tank[i].Node; - fwrite(ibuf+1,sizeof(INT4),Ntanks,OutFile); - for (i=1; i<=Ntanks; i++) x[i] = (REAL4)Tank[i].A; - FSAVE(Ntanks); - - /* Save node elevations to OutFile.*/ - for (i=1; i<=Nnodes; i++) x[i] = (REAL4)(Node[i].El*Ucf[ELEV]); - FSAVE(Nnodes); - - /* Save link lengths & diameters to OutFile.*/ - for (i=1; i<=Nlinks; i++) x[i] = (REAL4)(Link[i].Len*Ucf[ELEV]); - FSAVE(Nlinks); - for (i=1; i<=Nlinks; i++) - { - if (Link[i].Type != PUMP) - x[i] = (REAL4)(Link[i].Diam*Ucf[DIAM]); - else - x[i] = 0.0f; - } - if (FSAVE(Nlinks) < (unsigned)Nlinks) errcode = 308; - } - - /* Free memory used for buffer arrays */ - free(ibuf); - free(x); - return(errcode); -} - - -int savehyd(long *htime) -/* -**-------------------------------------------------------------- -** Input: *htime = current time -** Output: returns error code -** Purpose: saves current hydraulic solution to file HydFile -** in binary format -**-------------------------------------------------------------- -*/ -{ - int i; - INT4 t; - int errcode = 0; - REAL4 *x = (REAL4 *) calloc(MAX(Nnodes,Nlinks) + 1, sizeof(REAL4)); - if ( x == NULL ) return 101; - - /* Save current time (htime) */ - t = *htime; - fwrite(&t,sizeof(INT4),1,HydFile); - - /* Save current nodal demands (D) */ - for (i=1; i<=Nnodes; i++) x[i] = (REAL4)NodeDemand[i]; - fwrite(x+1,sizeof(REAL4),Nnodes,HydFile); - - /* Copy heads (H) to buffer of floats (x) and save buffer */ - for (i=1; i<=Nnodes; i++) x[i] = (REAL4)NodeHead[i]; - fwrite(x+1,sizeof(REAL4),Nnodes,HydFile); - - /* Force flow in closed links to be zero then save flows */ - for (i=1; i<=Nlinks; i++) - { - if (LinkStatus[i] <= CLOSED) x[i] = 0.0f; - else x[i] = (REAL4)Q[i]; - - } - fwrite(x+1,sizeof(REAL4),Nlinks,HydFile); - - /* Copy link status to buffer of floats (x) & write buffer */ - for (i=1; i<=Nlinks; i++) x[i] = (REAL4)LinkStatus[i]; - fwrite(x+1,sizeof(REAL4),Nlinks,HydFile); - - /* Save link settings & check for successful write-to-disk */ - /* (We assume that if any of the previous fwrites failed, */ - /* then this one will also fail.) */ - for (i=1; i<=Nlinks; i++) x[i] = (REAL4)LinkSetting[i]; - if (fwrite(x+1,sizeof(REAL4),Nlinks,HydFile) < (unsigned)Nlinks) - errcode = 308; - free(x); - fflush(HydFile); /* added TNT */ - return(errcode); -} /* End of savehyd */ - - -int savehydstep(long *hydstep) -/* -**-------------------------------------------------------------- -** Input: *hydstep = next time step -** Output: returns error code -** Purpose: saves next hydraulic timestep to file HydFile -** in binary format -**-------------------------------------------------------------- -*/ -{ - INT4 t; - int errcode = 0; - t = *hydstep; - if (fwrite(&t,sizeof(INT4),1,HydFile) < 1) errcode = 308; - if (t == 0) fputc(EOFMARK, HydFile); - fflush(HydFile); /* added TNT */ - return(errcode); -} - - -int saveenergy() -/* -**-------------------------------------------------------------- -** Input: none -** Output: returns error code -** Purpose: saves energy usage by each pump to OutFile -** in binary format -**-------------------------------------------------------------- -*/ -{ - int i,j; - INT4 index; - REAL4 x[6]; /* work array */ - double hdur, /* total duration in hours */ - t; /* pumping duration */ - - hdur = Dur / 3600.0; - for (i=1; i<=Npumps; i++) - { - if (hdur == 0.0) - { - for (j=0; j<5; j++) x[j] = (REAL4)Pump[i].Energy[j]; - x[5] = (REAL4)(Pump[i].Energy[5]*24.0); - } - else - { - t = Pump[i].Energy[0]; - x[0] = (REAL4)(t/hdur); - x[1] = 0.0f; - x[2] = 0.0f; - x[3] = 0.0f; - x[4] = 0.0f; - if (t > 0.0) - { - x[1] = (REAL4)(Pump[i].Energy[1]/t); - x[2] = (REAL4)(Pump[i].Energy[2]/t); - x[3] = (REAL4)(Pump[i].Energy[3]/t); - } - x[4] = (REAL4)Pump[i].Energy[4]; - x[5] = (REAL4)(Pump[i].Energy[5]*24.0/hdur); - } - x[0] *= 100.0f; - x[1] *= 100.0f; - /* Compute Kw-hr per MilGal (or per cubic meter) */ - if (Unitsflag == SI) x[2] *= (REAL4)(1000.0/LPSperCFS/3600.0); - else x[2] *= (REAL4)(1.0e6/GPMperCFS/60.0); - for (j=0; j<6; j++) Pump[i].Energy[j] = x[j]; - index = Pump[i].Link; - if (fwrite(&index,sizeof(INT4),1,OutFile) < 1) return(308); - if (fwrite(x, sizeof(REAL4), 6, OutFile) < 6) return(308); - } - Emax = Emax*Dcost; - x[0] = (REAL4)Emax; - if (fwrite(&x[0], sizeof(REAL4), 1, OutFile) < 1) return(308); - return(0); -} - - -int readhyd(long *hydtime) -/* -**-------------------------------------------------------------- -** Input: none -** Output: *hydtime = time of hydraulic solution -** Returns: 1 if successful, 0 if not -** Purpose: reads hydraulic solution from file HydFile -** -** NOTE: A hydraulic solution consists of the current time -** (hydtime), nodal demands (D) and heads (H), link -** flows (Q), link status (S), and link settings (K). -**-------------------------------------------------------------- -*/ -{ - int i; - INT4 t; - int result = 1; - REAL4 *x = (REAL4 *) calloc(MAX(Nnodes,Nlinks) + 1, sizeof(REAL4)); - if ( x == NULL ) return 0; - - if (fread(&t,sizeof(INT4),1,HydFile) < 1) result = 0; - *hydtime = t; - - if (fread(x+1,sizeof(REAL4),Nnodes,HydFile) < (unsigned)Nnodes) result = 0; - else for (i=1; i<=Nnodes; i++) NodeDemand[i] = x[i]; - - if (fread(x+1,sizeof(REAL4),Nnodes,HydFile) < (unsigned)Nnodes) result = 0; - else for (i=1; i<=Nnodes; i++) NodeHead[i] = x[i]; - - if (fread(x+1,sizeof(REAL4),Nlinks,HydFile) < (unsigned)Nlinks) result = 0; - else for (i=1; i<=Nlinks; i++) Q[i] = x[i]; - - if (fread(x+1,sizeof(REAL4),Nlinks,HydFile) < (unsigned)Nlinks) result = 0; - else for (i=1; i<=Nlinks; i++) LinkStatus[i] = (char) x[i]; - - if (fread(x+1,sizeof(REAL4),Nlinks,HydFile) < (unsigned)Nlinks) result = 0; - else for (i=1; i<=Nlinks; i++) LinkSetting[i] = x[i]; - - free(x); - return result; -} /* End of readhyd */ - - -int readhydstep(long *hydstep) -/* -**-------------------------------------------------------------- -** Input: none -** Output: *hydstep = next hydraulic time step (sec) -** Returns: 1 if successful, 0 if not -** Purpose: reads hydraulic time step from file HydFile -**-------------------------------------------------------------- -*/ -{ - INT4 t; - if (fread(&t,sizeof(INT4),1,HydFile) < 1) return(0); - *hydstep = t; - return(1); -} /* End of readhydstep */ - - -int saveoutput() -/* -**-------------------------------------------------------------- -** Input: none -** Output: returns error code -** Purpose: writes simulation results to output file -**-------------------------------------------------------------- -*/ -{ - int j; - int errcode = 0; - REAL4 *x = (REAL4 *) calloc(MAX(Nnodes,Nlinks) + 1, sizeof(REAL4)); - if ( x == NULL ) return 101; - - /* Write out node results, then link results */ - for (j=DEMAND; j<=QUALITY; j++) ERRCODE(nodeoutput(j,x,Ucf[j])); - for (j=FLOW; j<=FRICTION; j++) ERRCODE(linkoutput(j,x,Ucf[j])); - free(x); - return(errcode); -} /* End of saveoutput */ - - -int nodeoutput(int j, REAL4 *x, double ucf) -/* -**-------------------------------------------------------------- -** Input: j = type of node variable -** *x = buffer for node values -** ucf = units conversion factor -** Output: returns error code -** Purpose: writes results for node variable j to output file -**----------------------------------------------------------------- -*/ -{ - int i; - - /* Load computed results (in proper units) into buffer x */ - switch(j) - { - case DEMAND: for (i=1; i<=Nnodes; i++) - x[i] = (REAL4)(NodeDemand[i]*ucf); - break; - case HEAD: for (i=1; i<=Nnodes; i++) - x[i] = (REAL4)(NodeHead[i]*ucf); - break; - case PRESSURE: for (i=1; i<=Nnodes; i++) - x[i] = (REAL4)((NodeHead[i] - Node[i].El)*ucf); - break; - case QUALITY: for (i=1; i<=Nnodes; i++) - x[i] = (REAL4)(NodeQual[i]*ucf); - } - - /* Write x[1] to x[Nnodes] to output file */ - if (fwrite(x+1,sizeof(REAL4),Nnodes,TmpOutFile) < (unsigned)Nnodes) - return(308); - return(0); -} /* End of nodeoutput */ - - -int linkoutput(int j, REAL4 *x, double ucf) -/* -**---------------------------------------------------------------- -** Input: j = type of link variable -** *x = buffer for link values -** ucf = units conversion factor -** Output: returns error code -** Purpose: writes results for link variable j to output file -**---------------------------------------------------------------- -*/ -{ - int i; - double a,h,q,f; - - /* Load computed results (in proper units) into buffer x */ - switch(j) - { - case FLOW: for (i=1; i<=Nlinks; i++) - x[i] = (REAL4)(Q[i]*ucf); - break; - case VELOCITY: for (i=1; i<=Nlinks; i++) - { - if (Link[i].Type == PUMP) x[i] = 0.0f; - else - { - q = ABS(Q[i]); - a = PI*SQR(Link[i].Diam)/4.0; - x[i] = (REAL4)(q/a*ucf); - } - } - break; - case HEADLOSS: for (i=1; i<=Nlinks; i++) - { - if (LinkStatus[i] <= CLOSED) x[i] = 0.0f; - else - { - h = NodeHead[Link[i].N1] - NodeHead[Link[i].N2]; - if (Link[i].Type != PUMP) h = ABS(h); - if (Link[i].Type <= PIPE) - x[i] = (REAL4)(1000.0*h/Link[i].Len); - else x[i] = (REAL4)(h*ucf); - } - } - break; - case LINKQUAL: for (i=1; i<=Nlinks; i++) - x[i] = (REAL4)(avgqual(i)*ucf); - break; - case STATUS: for (i=1; i<=Nlinks; i++) - x[i] = (REAL4)LinkStatus[i]; - break; - case SETTING: for (i=1; i<=Nlinks; i++) - { - double setting = LinkSetting[i]; - if (setting != MISSING) - switch (Link[i].Type) - { - case CV: - case PIPE: x[i] = (REAL4)setting; - break; - case PUMP: x[i] = (REAL4)setting; - break; - case PRV: - case PSV: - case PBV: x[i] = (REAL4)(setting*Ucf[PRESSURE]); - break; - case FCV: x[i] = (REAL4)(setting*Ucf[FLOW]); - break; - case TCV: x[i] = (REAL4)setting; - break; - default: x[i] = 0.0f; - } - else x[i] = 0.0f; - } - break; - case REACTRATE: /* Overall reaction rate in mass/L/day */ - if (Qualflag == NONE) memset(x,0,(Nlinks+1 )*sizeof(REAL4)); - else for (i=1; i<=Nlinks; i++) x[i] = (REAL4)(PipeRateCoeff[i]*ucf); - break; - case FRICTION: /* f = 2ghd/(Lu^2) where f = friction factor */ - /* u = velocity, g = grav. accel., h = head */ - /*loss, d = diam., & L = pipe length */ - for (i=1; i<=Nlinks; i++) - { - if (Link[i].Type <= PIPE && ABS(Q[i]) > TINY) - { - h = ABS(NodeHead[Link[i].N1] - NodeHead[Link[i].N2]); - f = 39.725*h*pow(Link[i].Diam,5)/Link[i].Len/SQR(Q[i]); - x[i] = (REAL4)f; - } - else x[i] = 0.0f; - } - break; - } - - /* Write x[1] to x[Nlinks] to output file */ - if (fwrite(x+1,sizeof(REAL4),Nlinks,TmpOutFile) < (unsigned)Nlinks) - return(308); - return(0); -} /* End of linkoutput */ - - -int savefinaloutput() -/* -**-------------------------------------------------------------- -** Input: none -** Output: returns error code -** Purpose: saves time series statistics, reaction rates & -** epilog to output file. -**-------------------------------------------------------------- -*/ -{ - int errcode = 0; - REAL4 *x; - -/* Save time series statistic if computed */ - if (Tstatflag != SERIES && TmpOutFile != NULL) - { - x = (REAL4 *) calloc(MAX(Nnodes,Nlinks) + 1, sizeof(REAL4)); - if ( x == NULL ) return 101; - ERRCODE(savetimestat(x,NODEHDR)); - ERRCODE(savetimestat(x,LINKHDR)); - if (!errcode) Nperiods = 1; - fclose(TmpOutFile); - TmpOutFile=NULL; - free(x); - } - -/* Save avg. reaction rates & file epilog */ - if (OutFile != NULL) - { - ERRCODE(savenetreacts(Wbulk,Wwall,Wtank,Wsource)); - ERRCODE(saveepilog()); - } - return(errcode); -} - - -int savetimestat(REAL4 *x, char objtype) -/* -**-------------------------------------------------------------- -** Input: *x = buffer for node values -** objtype = NODEHDR (for nodes) or LINKHDR (for links) -** Output: returns error code -** Purpose: computes time series statistic for nodes or links -** and saves to normal output file. -** -** NOTE: This routine is dependent on how the output reporting -** variables were assigned to FieldType in TYPES.H. -**-------------------------------------------------------------- -*/ -{ - int n, n1, n2; - int i, j, p, errcode = 0; - long startbyte, skipbytes; - float *stat1, *stat2, xx; - -/* - Compute number of bytes in temp output file to skip over (skipbytes) - when moving from one time period to the next for a particular variable. -*/ - if (objtype == NODEHDR) - { - /* - For nodes, we start at 0 and skip over node output for all - node variables minus 1 plus link output for all link variables. - */ - startbyte = 0; - skipbytes = (Nnodes*(QUALITY-DEMAND) + - Nlinks*(FRICTION-FLOW+1))*sizeof(REAL4); - n = Nnodes; - n1 = DEMAND; - n2 = QUALITY; - } - else - { - /* - For links, we start at the end of all node variables and skip - over node output for all node variables plus link output for - all link variables minus 1. - */ - startbyte = Nnodes*(QUALITY-DEMAND+1)*sizeof(REAL4); - skipbytes = (Nnodes*(QUALITY-DEMAND+1) + - Nlinks*(FRICTION-FLOW))*sizeof(REAL4); - n = Nlinks; - n1 = FLOW; - n2 = FRICTION; - } - stat1 = (float *) calloc(n+1, sizeof(float)); - stat2 = (float *) calloc(n+1, sizeof(float)); - ERRCODE(MEMCHECK(stat1)); - ERRCODE(MEMCHECK(stat2)); - - /* Process each output reporting variable */ - if (!errcode) - { - for (j=n1; j<=n2; j++) - { - - /* Initialize stat arrays */ - if (Tstatflag == AVG) memset(stat1, 0, (n+1)*sizeof(float)); - else for (i=1; i<=n; i++) - { - stat1[i] = -MISSING; /* +1E10 */ - stat2[i] = MISSING; /* -1E10 */ - } - - /* Position temp output file at start of output */ - fseek(TmpOutFile, startbyte + (j-n1)*n*sizeof(REAL4), SEEK_SET); - - /* Process each time period */ - for (p=1; p<=Nperiods; p++) - { - - /* Get output results for time period & update stats */ - fread(x+1, sizeof(REAL4), n, TmpOutFile); - for (i=1; i<=n; i++) - { - xx = x[i]; - if (objtype == LINKHDR) - { - if (j == FLOW) xx = ABS(xx); - if (j == STATUS) - { - if (xx >= OPEN) xx = 1.0; - else xx = 0.0; - } - } - if (Tstatflag == AVG) stat1[i] += xx; - else - { - stat1[i] = MIN(stat1[i], xx); - stat2[i] = MAX(stat2[i], xx); - } - } - - /* Advance file to next period */ - if (p < Nperiods) fseek(TmpOutFile, skipbytes, SEEK_CUR); - } - - /* Compute resultant stat & save to regular output file */ - switch (Tstatflag) - { - case AVG: for (i=1; i<=n; i++) x[i] = stat1[i]/(float)Nperiods; - break; - case MIN: for (i=1; i<=n; i++) x[i] = stat1[i]; - break; - case MAX: for (i=1; i<=n; i++) x[i] = stat2[i]; - break; - case RANGE: for (i=1; i<=n; i++) x[i] = stat2[i] - stat1[i]; - break; - } - if (objtype == LINKHDR && j == STATUS) - { - for (i=1; i<=n; i++) - { - if (x[i] < 0.5f) x[i] = CLOSED; - else x[i] = OPEN; - } - } - if (fwrite(x+1, sizeof(REAL4), n, OutFile) < (unsigned) n) errcode = 308; - - /* Update internal output variables where applicable */ - if (objtype == NODEHDR) switch (j) - { - case DEMAND: for (i=1; i<=n; i++) NodeDemand[i] = x[i]/Ucf[DEMAND]; - break; - case HEAD: for (i=1; i<=n; i++) NodeHead[i] = x[i]/Ucf[HEAD]; - break; - case QUALITY: for (i=1; i<=n; i++) NodeQual[i] = x[i]/Ucf[QUALITY]; - break; - } - else if (j == FLOW) for (i=1; i<=n; i++) Q[i] = x[i]/Ucf[FLOW]; - } - } - - /* Free allocated memory */ - free(stat1); - free(stat2); - return(errcode); -} - - -int savenetreacts(double wbulk, double wwall, double wtank, double wsource) -/* -**----------------------------------------------------- -** Writes average network-wide reaction rates (in -** mass/hr) to binary output file. -**----------------------------------------------------- -*/ -{ - int errcode = 0; - double t; - REAL4 w[4]; - if (Dur > 0) t = (double)Dur/3600.; - else t = 1.; - w[0] = (REAL4)(wbulk/t); - w[1] = (REAL4)(wwall/t); - w[2] = (REAL4)(wtank/t); - w[3] = (REAL4)(wsource/t); - if (fwrite(w,sizeof(REAL4),4,OutFile) < 4) errcode = 308; - return(errcode); -} - - -int saveepilog() -/* -**------------------------------------------------- -** Writes Nperiods, Warnflag, & Magic Number to -** end of binary output file. -**------------------------------------------------- -*/ -{ - int errcode = 0; - INT4 i; - i = Nperiods; - if (fwrite(&i,sizeof(INT4),1,OutFile) < 1) errcode = 308; - i = Warnflag; - if (fwrite(&i,sizeof(INT4),1,OutFile) < 1) errcode = 308; - i = MAGICNUMBER; - if (fwrite(&i,sizeof(INT4),1,OutFile) < 1) errcode = 308; - return(errcode); -} - - -/********************** END OF OUTPUT.C **********************/ +/* +********************************************************************* + +OUTPUT.C -- Binary File Transfer Routines for EPANET Program + +VERSION: 2.00 +DATE: 5/8/00 + 8/15/07 (2.00.11) +AUTHOR: L. Rossman + US EPA - NRMRL + +******************************************************************** +*/ + +#include +#include +#ifndef __APPLE__ +#include +#else +#include +#endif +#include "epanet2.h" +#include "funcs.h" +#include "text.h" +#include "types.h" +#include +#define EXTERN extern +#include "hash.h" +#include "vars.h" + +/* write x[1] to x[n] to file */ +size_t f_save(REAL4 *x, int n, FILE *file) { + return fwrite(x + 1, sizeof(REAL4), n, file); +} + + +int savenetdata(EN_Project *pr) +/* +**--------------------------------------------------------------- +** Input: none +** Output: returns error code +** Purpose: saves input data in original units to binary +** output file using fixed-sized (4-byte) records +**--------------------------------------------------------------- +*/ +{ + int i, nmax; + INT4 *ibuf; + REAL4 *x; + int errcode = 0; + + EN_Network *net = &pr->network; + out_file_t *out = &pr->out_files; + report_options_t *rep = &pr->report; + quality_t *qu = &pr->quality; + parser_data_t *par = &pr->parser; + time_options_t *time = &pr->time_options; + FILE *outFile = out->OutFile; + + /* Allocate buffer arrays */ + nmax = MAX(net->Nnodes, net->Nlinks) + 1; + nmax = MAX(nmax, 15); + ibuf = (INT4 *)calloc(nmax, sizeof(INT4)); + x = (REAL4 *)calloc(nmax, sizeof(REAL4)); + ERRCODE(MEMCHECK(ibuf)); + ERRCODE(MEMCHECK(x)); + + if (!errcode) { + /* Write integer variables to outFile */ + ibuf[0] = MAGICNUMBER; + + /*** CODEVERSION replaces VERSION ***/ + ibuf[1] = CODEVERSION; + + ibuf[2] = net->Nnodes; + ibuf[3] = net->Ntanks; + ibuf[4] = net->Nlinks; + ibuf[5] = net->Npumps; + ibuf[6] = net->Nvalves; + ibuf[7] = qu->Qualflag; + ibuf[8] = qu->TraceNode; + ibuf[9] = par->Flowflag; + ibuf[10] = par->Pressflag; + ibuf[11] = rep->Tstatflag; + ibuf[12] = (INT4)time->Rstart; + ibuf[13] = (INT4)time->Rstep; + ibuf[14] = (INT4)time->Dur; + fwrite(ibuf, sizeof(INT4), 15, outFile); + + /* Write string variables to outFile */ + fwrite(pr->Title[0], sizeof(char), MAXMSG + 1, outFile); + fwrite(pr->Title[1], sizeof(char), MAXMSG + 1, outFile); + fwrite(pr->Title[2], sizeof(char), MAXMSG + 1, outFile); + fwrite(par->InpFname, sizeof(char), MAXFNAME + 1, outFile); + fwrite(rep->Rpt2Fname, sizeof(char), MAXFNAME + 1, outFile); + fwrite(qu->ChemName, sizeof(char), MAXID + 1, outFile); + fwrite(rep->Field[QUALITY].Units, sizeof(char), MAXID + 1, outFile); + + /* Write node ID information to outFile */ + for (i = 1; i <= net->Nnodes; i++) { + Snode *node = &net->Node[i]; + fwrite(node->ID, MAXID + 1, 1, outFile); + } + + /* Write link information to outFile */ + /* (Note: first transfer values to buffer array,*/ + /* then fwrite buffer array at offset of 1 ) */ + for (i = 1; i <= net->Nlinks; i++) + fwrite(net->Link[i].ID, MAXID + 1, 1, outFile); + + for (i = 1; i <= net->Nlinks; i++) + ibuf[i] = net->Link[i].N1; + fwrite(ibuf + 1, sizeof(INT4), net->Nlinks, outFile); + + for (i = 1; i <= net->Nlinks; i++) + ibuf[i] = net->Link[i].N2; + fwrite(ibuf + 1, sizeof(INT4), net->Nlinks, outFile); + + for (i = 1; i <= net->Nlinks; i++) + ibuf[i] = net->Link[i].Type; + fwrite(ibuf + 1, sizeof(INT4), net->Nlinks, outFile); + + /* Write tank information to outFile.*/ + for (i = 1; i <= net->Ntanks; i++) + ibuf[i] = net->Tank[i].Node; + fwrite(ibuf + 1, sizeof(INT4), net->Ntanks, outFile); + + for (i = 1; i <= net->Ntanks; i++) + x[i] = (REAL4)net->Tank[i].A; + f_save(x, net->Ntanks, outFile); + + /* Save node elevations to outFile.*/ + for (i = 1; i <= net->Nnodes; i++) + x[i] = (REAL4)(net->Node[i].El * pr->Ucf[ELEV]); + f_save(x, net->Nnodes, outFile); + + /* Save link lengths & diameters to outFile.*/ + for (i = 1; i <= net->Nlinks; i++) + x[i] = (REAL4)(net->Link[i].Len * pr->Ucf[ELEV]); + f_save(x, net->Nlinks, outFile); + + for (i = 1; i <= net->Nlinks; i++) { + if (net->Link[i].Type != EN_PUMP) + x[i] = (REAL4)(net->Link[i].Diam * pr->Ucf[DIAM]); + else + x[i] = 0.0f; + } + if (f_save(x, net->Nlinks, outFile) < (unsigned)net->Nlinks) + errcode = 308; + } + + /* Free memory used for buffer arrays */ + free(ibuf); + free(x); + return (errcode); +} + +int savehyd(EN_Project *pr, long *htime) +/* +**-------------------------------------------------------------- +** Input: *htime = current time +** Output: returns error code +** Purpose: saves current hydraulic solution to file HydFile +** in binary format +**-------------------------------------------------------------- +*/ +{ + EN_Network *net = &pr->network; + out_file_t *out = &pr->out_files; + FILE *HydFile = out->HydFile; + hydraulics_t *hyd = &pr->hydraulics; + + int i; + INT4 t; + int errcode = 0; + REAL4 *x = (REAL4 *)calloc(MAX(net->Nnodes, net->Nlinks) + 1, sizeof(REAL4)); + if (x == NULL) + return 101; + + /* Save current time (htime) */ + t = (INT4)(*htime); + fwrite(&t, sizeof(INT4), 1, HydFile); + + /* Save current nodal demands (D) */ + for (i = 1; i <= net->Nnodes; i++) + x[i] = (REAL4)hyd->NodeDemand[i]; + fwrite(x + 1, sizeof(REAL4), net->Nnodes, HydFile); + + /* Copy heads (H) to buffer of floats (x) and save buffer */ + for (i = 1; i <= net->Nnodes; i++) + x[i] = (REAL4)hyd->NodeHead[i]; + fwrite(x + 1, sizeof(REAL4), net->Nnodes, HydFile); + + /* Force flow in closed links to be zero then save flows */ + for (i = 1; i <= net->Nlinks; i++) { + if (hyd->LinkStatus[i] <= CLOSED) + x[i] = 0.0f; + else + x[i] = (REAL4)hyd->LinkFlows[i]; + } + fwrite(x + 1, sizeof(REAL4), net->Nlinks, HydFile); + + /* Copy link status to buffer of floats (x) & write buffer */ + for (i = 1; i <= net->Nlinks; i++) + x[i] = (REAL4)hyd->LinkStatus[i]; + fwrite(x + 1, sizeof(REAL4), net->Nlinks, HydFile); + + /* Save link settings & check for successful write-to-disk */ + /* (We assume that if any of the previous fwrites failed, */ + /* then this one will also fail.) */ + for (i = 1; i <= net->Nlinks; i++) + x[i] = (REAL4)hyd->LinkSetting[i]; + if (fwrite(x + 1, sizeof(REAL4), net->Nlinks, HydFile) < + (unsigned)net->Nlinks) + errcode = 308; + free(x); + fflush(HydFile); /* added TNT */ + return (errcode); +} /* End of savehyd */ + +int savehydstep(EN_Project *pr, long *hydstep) +/* +**-------------------------------------------------------------- +** Input: *hydstep = next time step +** Output: returns error code +** Purpose: saves next hydraulic timestep to file HydFile +** in binary format +**-------------------------------------------------------------- +*/ +{ + out_file_t *out = &pr->out_files; + INT4 t; + int errcode = 0; + t = (INT4)(*hydstep); + if (fwrite(&t, sizeof(INT4), 1, out->HydFile) < 1) + errcode = 308; + if (t == 0) + fputc(EOFMARK, out->HydFile); + fflush(out->HydFile); /* added TNT */ + return (errcode); +} + +int saveenergy(EN_Project *pr) +/* +**-------------------------------------------------------------- +** Input: none +** Output: returns error code +** Purpose: saves energy usage by each pump to outFile +** in binary format +**-------------------------------------------------------------- +*/ +{ + + EN_Network *net = &pr->network; + hydraulics_t *hyd = &pr->hydraulics; + out_file_t *out = &pr->out_files; + parser_data_t *par = &pr->parser; + time_options_t *time = &pr->time_options; + FILE *outFile = out->OutFile; + + int i, j; + INT4 index; + REAL4 x[6]; /* work array */ + double hdur, /* total time->Duration in hours */ + t; /* pumping time->Duration */ + + hdur = time->Dur / 3600.0; + for (i = 1; i <= net->Npumps; i++) { + Spump *pump = &net->Pump[i]; + if (hdur == 0.0) { + for (j = 0; j < 5; j++) + x[j] = (REAL4)pump->Energy[j]; + x[5] = (REAL4)(pump->Energy[5] * 24.0); + } else { + t = pump->Energy[0]; + x[0] = (REAL4)(t / hdur); + x[1] = 0.0f; + x[2] = 0.0f; + x[3] = 0.0f; + x[4] = 0.0f; + if (t > 0.0) { + x[1] = (REAL4)(pump->Energy[1] / t); + x[2] = (REAL4)(pump->Energy[2] / t); + x[3] = (REAL4)(pump->Energy[3] / t); + } + x[4] = (REAL4)pump->Energy[4]; + x[5] = (REAL4)(pump->Energy[5] * 24.0 / hdur); + } + x[0] *= 100.0f; + x[1] *= 100.0f; + /* Compute Kw-hr per MilGal (or per cubic meter) */ + if (par->Unitsflag == SI) + x[2] *= (REAL4)(1000.0 / LPSperCFS / 3600.0); + else + x[2] *= (REAL4)(1.0e6 / GPMperCFS / 60.0); + for (j = 0; j < 6; j++) + pump->Energy[j] = x[j]; + index = pump->Link; + if (fwrite(&index, sizeof(INT4), 1, outFile) < 1) + return (308); + if (fwrite(x, sizeof(REAL4), 6, outFile) < 6) + return (308); + } + hyd->Emax = hyd->Emax * hyd->Dcost; + x[0] = (REAL4)hyd->Emax; + if (fwrite(&x[0], sizeof(REAL4), 1, outFile) < 1) + return (308); + return (0); +} + +int readhyd(EN_Project *pr, long *hydtime) +/* +**-------------------------------------------------------------- +** Input: none +** Output: *hydtime = time of hydraulic solution +** Returns: 1 if successful, 0 if not +** Purpose: reads hydraulic solution from file HydFile +** +** NOTE: A hydraulic solution consists of the current time +** (hydtime), nodal demands (D) and heads (H), link +** flows (Q), link status (S), and link settings (K). +**-------------------------------------------------------------- +*/ +{ + EN_Network *net = &pr->network; + hydraulics_t *hyd = &pr->hydraulics; + out_file_t *out = &pr->out_files; + FILE *HydFile = out->HydFile; + + int i; + INT4 t; + int result = 1; + REAL4 *x = (REAL4 *)calloc(MAX(net->Nnodes, net->Nlinks) + 1, sizeof(REAL4)); + if (x == NULL) + return 0; + + if (fread(&t, sizeof(INT4), 1, HydFile) < 1) + result = 0; + *hydtime = t; + + if (fread(x + 1, sizeof(REAL4), net->Nnodes, HydFile) < (unsigned)net->Nnodes) + result = 0; + else + for (i = 1; i <= net->Nnodes; i++) + hyd->NodeDemand[i] = x[i]; + + if (fread(x + 1, sizeof(REAL4), net->Nnodes, HydFile) < (unsigned)net->Nnodes) + result = 0; + else + for (i = 1; i <= net->Nnodes; i++) + hyd->NodeHead[i] = x[i]; + + if (fread(x + 1, sizeof(REAL4), net->Nlinks, HydFile) < (unsigned)net->Nlinks) + result = 0; + else + for (i = 1; i <= net->Nlinks; i++) + hyd->LinkFlows[i] = x[i]; + + if (fread(x + 1, sizeof(REAL4), net->Nlinks, HydFile) < (unsigned)net->Nlinks) + result = 0; + else + for (i = 1; i <= net->Nlinks; i++) + hyd->LinkStatus[i] = (char)x[i]; + + if (fread(x + 1, sizeof(REAL4), net->Nlinks, HydFile) < (unsigned)net->Nlinks) + result = 0; + else + for (i = 1; i <= net->Nlinks; i++) + hyd->LinkSetting[i] = x[i]; + + free(x); + return result; +} /* End of readhyd */ + +int readhydstep(FILE *hydFile, long *hydstep) +/* +**-------------------------------------------------------------- +** Input: none +** Output: *hydstep = next hydraulic time step (sec) +** Returns: 1 if successful, 0 if not +** Purpose: reads hydraulic time step from file HydFile +**-------------------------------------------------------------- +*/ +{ + INT4 t; + if (fread(&t, sizeof(INT4), 1, hydFile) < 1) + return (0); + *hydstep = t; + return (1); +} /* End of readhydstep */ + +int saveoutput(EN_Project *pr) +/* +**-------------------------------------------------------------- +** Input: none +** Output: returns error code +** Purpose: writes simulation results to output file +**-------------------------------------------------------------- +*/ +{ + EN_Network *net = &pr->network; + + int j; + int errcode = 0; + REAL4 *x = (REAL4 *)calloc(MAX(net->Nnodes, net->Nlinks) + 1, sizeof(REAL4)); + if (x == NULL) + return 101; + + /* Write out node results, then link results */ + for (j = DEMAND; j <= QUALITY; j++) + ERRCODE(nodeoutput(pr, j, x, pr->Ucf[j])); + for (j = FLOW; j <= FRICTION; j++) + ERRCODE(linkoutput(pr, j, x, pr->Ucf[j])); + free(x); + return (errcode); +} /* End of saveoutput */ + +int nodeoutput(EN_Project *pr, int j, REAL4 *x, double ucf) +/* +**-------------------------------------------------------------- +** Input: j = type of node variable +** *x = buffer for node values +** ucf = units conversion factor +** Output: returns error code +** Purpose: writes results for node variable j to output file +**----------------------------------------------------------------- +*/ +{ + EN_Network *net = &pr->network; + hydraulics_t *hyd = &pr->hydraulics; + out_file_t *out = &pr->out_files; + quality_t *qu = &pr->quality; + + int i; + + /* Load computed results (in proper units) into buffer x */ + switch (j) { + case DEMAND: + for (i = 1; i <= net->Nnodes; i++) + x[i] = (REAL4)(hyd->NodeDemand[i] * ucf); + break; + case HEAD: + for (i = 1; i <= net->Nnodes; i++) + x[i] = (REAL4)(hyd->NodeHead[i] * ucf); + break; + case PRESSURE: + for (i = 1; i <= net->Nnodes; i++) + x[i] = (REAL4)((hyd->NodeHead[i] - net->Node[i].El) * ucf); + break; + case QUALITY: + for (i = 1; i <= net->Nnodes; i++) + x[i] = (REAL4)(qu->NodeQual[i] * ucf); + } + + /* Write x[1] to x[net->Nnodes] to output file */ + if (fwrite(x + 1, sizeof(REAL4), net->Nnodes, out->TmpOutFile) < (unsigned)net->Nnodes) { + return (308); + } + return (0); +} /* End of nodeoutput */ + +int linkoutput(EN_Project *pr, int j, REAL4 *x, double ucf) +/* +**---------------------------------------------------------------- +** Input: j = type of link variable +** *x = buffer for link values +** ucf = units conversion factor +** Output: returns error code +** Purpose: writes results for link variable j to output file +**---------------------------------------------------------------- +*/ +{ + EN_Network *net = &pr->network; + hydraulics_t *hyd = &pr->hydraulics; + out_file_t *out = &pr->out_files; + quality_t *qu = &pr->quality; + + int i; + double a, h, q, f; + + /* Load computed results (in proper units) into buffer x */ + switch (j) { + case FLOW: + for (i = 1; i <= net->Nlinks; i++) + x[i] = (REAL4)(hyd->LinkFlows[i] * ucf); + break; + case VELOCITY: + for (i = 1; i <= net->Nlinks; i++) { + if (net->Link[i].Type == EN_PUMP) + x[i] = 0.0f; + else { + q = ABS(hyd->LinkFlows[i]); + a = PI * SQR(net->Link[i].Diam) / 4.0; + x[i] = (REAL4)(q / a * ucf); + } + } + break; + case HEADLOSS: + for (i = 1; i <= net->Nlinks; i++) { + if (hyd->LinkStatus[i] <= CLOSED) + x[i] = 0.0f; + else { + h = hyd->NodeHead[net->Link[i].N1] - hyd->NodeHead[net->Link[i].N2]; + if (net->Link[i].Type != EN_PUMP) + h = ABS(h); + if (net->Link[i].Type <= EN_PIPE) + x[i] = (REAL4)(1000.0 * h / net->Link[i].Len); + else + x[i] = (REAL4)(h * ucf); + } + } + break; + case LINKQUAL: + for (i = 1; i <= net->Nlinks; i++) + x[i] = (REAL4)(avgqual(pr,i) * ucf); + break; + case STATUS: + for (i = 1; i <= net->Nlinks; i++) + x[i] = (REAL4)hyd->LinkStatus[i]; + break; + case SETTING: + for (i = 1; i <= net->Nlinks; i++) { + double setting = hyd->LinkSetting[i]; + if (setting != MISSING) + switch (net->Link[i].Type) { + case EN_CVPIPE: + case EN_PIPE: + x[i] = (REAL4)setting; + break; + case EN_PUMP: + x[i] = (REAL4)setting; + break; + case EN_PRV: + case EN_PSV: + case EN_PBV: + x[i] = (REAL4)(setting * pr->Ucf[PRESSURE]); + break; + case EN_FCV: + x[i] = (REAL4)(setting * pr->Ucf[FLOW]); + break; + case EN_TCV: + x[i] = (REAL4)setting; + break; + default: + x[i] = 0.0f; + } + else + x[i] = 0.0f; + } + break; + case REACTRATE: /* Overall reaction rate in mass/L/day */ + if (qu->Qualflag == NONE) + memset(x, 0, (net->Nlinks + 1) * sizeof(REAL4)); + else + for (i = 1; i <= net->Nlinks; i++) + x[i] = (REAL4)(qu->PipeRateCoeff[i] * ucf); + break; + case FRICTION: /* f = 2ghd/(Lu^2) where f = friction factor */ + /* u = velocity, g = grav. accel., h = head */ + /*loss, d = diam., & L = pipe length */ + for (i = 1; i <= net->Nlinks; i++) { + if (net->Link[i].Type <= EN_PIPE && ABS(hyd->LinkFlows[i]) > TINY) { + h = ABS(hyd->NodeHead[net->Link[i].N1] - hyd->NodeHead[net->Link[i].N2]); + f = 39.725 * h * pow(net->Link[i].Diam, 5) / net->Link[i].Len / SQR(hyd->LinkFlows[i]); + x[i] = (REAL4)f; + } else + x[i] = 0.0f; + } + break; + } + + /* Write x[1] to x[net->Nlinks] to output file */ + if (fwrite(x + 1, sizeof(REAL4), net->Nlinks, out->TmpOutFile) < + (unsigned)net->Nlinks) + return (308); + return (0); +} /* End of linkoutput */ + +int savefinaloutput(EN_Project *pr) +/* +**-------------------------------------------------------------- +** Input: none +** Output: returns error code +** Purpose: saves time series statistics, reaction rates & +** epilog to output file. +**-------------------------------------------------------------- +*/ +{ + EN_Network *net = &pr->network; + out_file_t *out = &pr->out_files; + report_options_t *rep = &pr->report; + quality_t *qu = &pr->quality; + FILE *outFile = out->OutFile; + + int errcode = 0; + REAL4 *x; + + /* Save time series statistic if computed */ + if (rep->Tstatflag != SERIES && out->TmpOutFile != NULL) { + x = (REAL4 *)calloc(MAX(net->Nnodes, net->Nlinks) + 1, sizeof(REAL4)); + if (x == NULL) + return 101; + ERRCODE(savetimestat(pr, x, NODEHDR)); + ERRCODE(savetimestat(pr, x, LINKHDR)); + if (!errcode) + rep->Nperiods = 1; + fclose(out->TmpOutFile); + out->TmpOutFile = NULL; + free(x); + } + + /* Save avg. reaction rates & file epilog */ + if (outFile != NULL) { + ERRCODE(savenetreacts(pr, qu->Wbulk, qu->Wwall, qu->Wtank, qu->Wsource)); + ERRCODE(saveepilog(pr)); + } + return (errcode); +} + +int savetimestat(EN_Project *pr, REAL4 *x, HdrType objtype) +/* +**-------------------------------------------------------------- +** Input: *x = buffer for node values +** objtype = NODEHDR (for nodes) or LINKHDR (for links) +** Output: returns error code +** Purpose: computes time series statistic for nodes or links +** and saves to normal output file. +** +** NOTE: This routine is dependent on how the output reporting +** variables were assigned to FieldType in TYPES.H. +**-------------------------------------------------------------- +*/ +{ + EN_Network *net = &pr->network; + hydraulics_t *hyd = &pr->hydraulics; + out_file_t *out = &pr->out_files; + report_options_t *rep = &pr->report; + quality_t *qu = &pr->quality; + FILE *outFile = out->OutFile; + + int n, n1, n2; + int i, j, p, errcode = 0; + long startbyte, skipbytes; + float *stat1, *stat2, xx; + + /* + Compute number of bytes in temp output file to skip over (skipbytes) + when moving from one time period to the next for a particular variable. + */ + if (objtype == NODEHDR) { + /* + For nodes, we start at 0 and skip over node output for all + node variables minus 1 plus link output for all link variables. + */ + startbyte = 0; + skipbytes = (net->Nnodes * (QUALITY - DEMAND) + + net->Nlinks * (FRICTION - FLOW + 1)) * + sizeof(REAL4); + n = net->Nnodes; + n1 = DEMAND; + n2 = QUALITY; + } else { + /* + For links, we start at the end of all node variables and skip + over node output for all node variables plus link output for + all link variables minus 1. + */ + startbyte = net->Nnodes * (QUALITY - DEMAND + 1) * sizeof(REAL4); + skipbytes = (net->Nnodes * (QUALITY - DEMAND + 1) + + net->Nlinks * (FRICTION - FLOW)) * + sizeof(REAL4); + n = net->Nlinks; + n1 = FLOW; + n2 = FRICTION; + } + stat1 = (float *)calloc(n + 1, sizeof(float)); + stat2 = (float *)calloc(n + 1, sizeof(float)); + ERRCODE(MEMCHECK(stat1)); + ERRCODE(MEMCHECK(stat2)); + + /* Process each output reporting variable */ + if (!errcode) { + for (j = n1; j <= n2; j++) { + + /* Initialize stat arrays */ + if (rep->Tstatflag == AVG) + memset(stat1, 0, (n + 1) * sizeof(float)); + else + for (i = 1; i <= n; i++) { + stat1[i] = -MISSING; /* +1E10 */ + stat2[i] = MISSING; /* -1E10 */ + } + + /* Position temp output file at start of output */ + fseek(out->TmpOutFile, startbyte + (j - n1) * n * sizeof(REAL4), SEEK_SET); + + /* Process each time period */ + for (p = 1; p <= rep->Nperiods; p++) { + + /* Get output results for time period & update stats */ + fread(x + 1, sizeof(REAL4), n, out->TmpOutFile); + for (i = 1; i <= n; i++) { + xx = x[i]; + if (objtype == LINKHDR) { + if (j == FLOW) + xx = ABS(xx); + if (j == STATUS) { + if (xx >= OPEN) + xx = 1.0; + else + xx = 0.0; + } + } + if (rep->Tstatflag == AVG) + stat1[i] += xx; + else { + stat1[i] = MIN(stat1[i], xx); + stat2[i] = MAX(stat2[i], xx); + } + } + + /* Advance file to next period */ + if (p < rep->Nperiods) + fseek(out->TmpOutFile, skipbytes, SEEK_CUR); + } + + /* Compute resultant stat & save to regular output file */ + switch (rep->Tstatflag) { + case AVG: + for (i = 1; i <= n; i++) + x[i] = stat1[i] / (float)rep->Nperiods; + break; + case MIN: + for (i = 1; i <= n; i++) + x[i] = stat1[i]; + break; + case MAX: + for (i = 1; i <= n; i++) + x[i] = stat2[i]; + break; + case RANGE: + for (i = 1; i <= n; i++) + x[i] = stat2[i] - stat1[i]; + break; + } + if (objtype == LINKHDR && j == STATUS) { + for (i = 1; i <= n; i++) { + if (x[i] < 0.5f) + x[i] = CLOSED; + else + x[i] = OPEN; + } + } + if (fwrite(x + 1, sizeof(REAL4), n, outFile) < (unsigned)n) + errcode = 308; + + /* Update internal output variables where applicable */ + if (objtype == NODEHDR) + switch (j) { + case DEMAND: + for (i = 1; i <= n; i++) + hyd->NodeDemand[i] = x[i] / pr->Ucf[DEMAND]; + break; + case HEAD: + for (i = 1; i <= n; i++) + hyd->NodeHead[i] = x[i] / pr->Ucf[HEAD]; + break; + case QUALITY: + for (i = 1; i <= n; i++) + qu->NodeQual[i] = x[i] / pr->Ucf[QUALITY]; + break; + } + else if (j == FLOW) + for (i = 1; i <= n; i++) + hyd->LinkFlows[i] = x[i] / pr->Ucf[FLOW]; + } + } + + /* Free allocated memory */ + free(stat1); + free(stat2); + return (errcode); +} + +int savenetreacts(EN_Project *pr, double wbulk, double wwall, double wtank, double wsource) +/* +**----------------------------------------------------- +** Writes average network-wide reaction rates (in +** mass/hr) to binary output file. +**----------------------------------------------------- +*/ +{ + out_file_t *out = &pr->out_files; + time_options_t *time = &pr->time_options; + FILE *outFile = out->OutFile; + + int errcode = 0; + double t; + REAL4 w[4]; + if (time->Dur > 0) + t = (double)time->Dur / 3600.; + else + t = 1.; + w[0] = (REAL4)(wbulk / t); + w[1] = (REAL4)(wwall / t); + w[2] = (REAL4)(wtank / t); + w[3] = (REAL4)(wsource / t); + if (fwrite(w, sizeof(REAL4), 4, outFile) < 4) + errcode = 308; + return (errcode); +} + +int saveepilog(EN_Project *pr) +/* +**------------------------------------------------- +** Writes Nperiods, Warnflag, & Magic Number to +** end of binary output file. +**------------------------------------------------- +*/ +{ + out_file_t *out = &pr->out_files; + report_options_t *rep = &pr->report; + FILE *outFile = out->OutFile; + + int errcode = 0; + INT4 i; + i = rep->Nperiods; + if (fwrite(&i, sizeof(INT4), 1, outFile) < 1) + errcode = 308; + i = pr->Warnflag; + if (fwrite(&i, sizeof(INT4), 1, outFile) < 1) + errcode = 308; + i = MAGICNUMBER; + if (fwrite(&i, sizeof(INT4), 1, outFile) < 1) + errcode = 308; + return (errcode); +} + +/********************** END OF OUTPUT.C **********************/ diff --git a/src/quality.c b/src/quality.c old mode 100755 new mode 100644 index 86f7701..5308e2c --- a/src/quality.c +++ b/src/quality.c @@ -1,1787 +1,1851 @@ -/* -******************************************************************************* - -QUALITY.C -- Water Quality Simulator for EPANET Program - -VERSION: 2.00 -DATE: 5/29/00 - 9/7/00 - 10/25/00 - 8/15/07 (2.00.11) - 2/14/08 (2.00.12) -AUTHOR: L. Rossman - US EPA - NRMRL - - This module contains the network water quality simulator. - - For each time period, hydraulic results are read in from the - binary file HydFile, hydraulic and water quality results are - written to the binary output file OutFile (if the current period - is a reporting period), and the water quality is transported - and reacted over the duration of the time period. - - The entry points for this module are: - openqual() -- called from ENopenQ() in EPANET.C - initqual() -- called from ENinitQ() in EPANET.C - runqual() -- called from ENrunQ() in EPANET.C - nextqual() -- called from ENnextQ() in EPANET.C - stepqual() -- called from ENstepQ() in EPANET.C - closequal() -- called from ENcloseQ() in EPANET.C - - Calls are made to: - AllocInit() - Alloc() - AllocFree() - 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. - - Calls are also made to: - readhyd() - readhydstep() - savenetdata() - saveoutput() - savefinaloutput() - in OUTPUT.C to retrieve hydraulic results and save all results. - -******************************************************************************* -*/ - -#include -#include -#ifndef __APPLE__ -#include -#else -#include -#endif -#include -#include "hash.h" -#include "text.h" -#include "types.h" -#include "funcs.h" -#define EXTERN extern -#include "vars.h" -#include "mempool.h" - -/* -** Macros to identify upstream & downstream nodes of a link -** under the current flow and to compute link volume -*/ -#define UP_NODE(x) ( (FlowDir[(x)]=='+') ? Link[(x)].N1 : Link[(x)].N2 ) -#define DOWN_NODE(x) ( (FlowDir[(x)]=='+') ? Link[(x)].N2 : Link[(x)].N1 ) -#define LINKVOL(k) ( 0.785398*Link[(k)].Len*SQR(Link[(k)].Diam) ) - -Pseg FreeSeg; /* Pointer to unused segment */ -Pseg *FirstSeg, /* First (downstream) segment in each pipe */ - *LastSeg; /* Last (upstream) segment in each pipe */ -char *FlowDir; /* Flow direction for each pipe */ -double *VolIn; /* Total volume inflow to node */ -double *MassIn; /* Total mass inflow to node */ -double Sc; /* Schmidt Number */ -double Bucf; /* Bulk reaction units conversion factor */ -double Tucf; /* Tank reaction units conversion factor */ - -/*** Moved to vars.h ***/ //(2.00.12 - LR) -//char Reactflag; /* Reaction indicator */ - -char OutOfMemory; /* Out of memory indicator */ -static alloc_handle_t *SegPool; // Memory pool for water quality segments //(2.00.11 - LR) - - -int openqual() -/* -**-------------------------------------------------------------- -** Input: none -** Output: returns error code -** Purpose: opens WQ solver system -**-------------------------------------------------------------- -*/ -{ - int errcode = 0; - int n; - - /* Allocate memory pool for WQ segments */ - OutOfMemory = FALSE; - SegPool = AllocInit(); //(2.00.11 - LR) - if (SegPool == NULL) errcode = 101; //(2.00.11 - LR) - - /* Allocate scratch array & reaction rate array*/ - TempQual = (double *) calloc(MAX((Nnodes+1),(Nlinks+1)),sizeof(double)); - PipeRateCoeff = (double *) calloc((Nlinks+1), sizeof(double)); - ERRCODE(MEMCHECK(TempQual)); - ERRCODE(MEMCHECK(PipeRateCoeff)); - - /* Allocate memory for WQ solver */ - n = Nlinks+Ntanks+1; - FirstSeg = (Pseg *) calloc(n, sizeof(Pseg)); - LastSeg = (Pseg *) calloc(n, sizeof(Pseg)); - FlowDir = (char *) calloc(n, sizeof(char)); - n = Nnodes+1; - VolIn = (double *) calloc(n, sizeof(double)); - MassIn = (double *) calloc(n, sizeof(double)); - ERRCODE(MEMCHECK(FirstSeg)); - ERRCODE(MEMCHECK(LastSeg)); - ERRCODE(MEMCHECK(FlowDir)); - ERRCODE(MEMCHECK(VolIn)); - ERRCODE(MEMCHECK(MassIn)); - return(errcode); -} - -/* Local function to compute unit conversion factor for bulk reaction rates */ - double getucf(double order) - { - if (order < 0.0) order = 0.0; - if (order == 1.0) return(1.0); - else return(1./pow(LperFT3,(order-1.0))); - } - - -void initqual() -/* -**-------------------------------------------------------------- -** Input: none -** Output: none -** Purpose: re-initializes WQ solver system -**-------------------------------------------------------------- -*/ -{ - int i; - - /* Initialize quality, tank volumes, & source mass flows */ - for (i=1; i<=Nnodes; i++) NodeQual[i] = Node[i].C0; - for (i=1; i<=Ntanks; i++) Tank[i].C = Node[Tank[i].Node].C0; - for (i=1; i<=Ntanks; i++) Tank[i].V = Tank[i].V0; - for (i=1; i<=Nnodes; i++) { - if (Node[i].S != NULL) Node[i].S->Smass = 0.0; - } - - QTankVolumes = calloc(Ntanks, sizeof(double)); // keep track of previous step's tank volumes. - QLinkFlow = calloc(Nlinks, sizeof(double)); // keep track of previous step's link flows. - - /* Set WQ parameters */ - Bucf = 1.0; - Tucf = 1.0; - Reactflag = 0; - if (Qualflag != NONE) - { - /* Initialize WQ at trace node (if applicable) */ - if (Qualflag == TRACE) NodeQual[TraceNode] = 100.0; - - /* Compute Schmidt number */ - if (Diffus > 0.0) - Sc = Viscos/Diffus; - else - Sc = 0.0; - - /* Compute unit conversion factor for bulk react. coeff. */ - Bucf = getucf(BulkOrder); - Tucf = getucf(TankOrder); - - /* Check if modeling a reactive substance */ - Reactflag = setReactflag(); - - /* Reset memory pool */ - FreeSeg = NULL; - AllocSetPool(SegPool); //(2.00.11 - LR) - AllocReset(); //(2.00.11 - LR) - } - - /* Initialize avg. reaction rates */ - Wbulk = 0.0; - Wwall = 0.0; - Wtank = 0.0; - Wsource = 0.0; - - /* Re-position hydraulics file */ - if (!OpenHflag) { - fseek(HydFile,HydOffset,SEEK_SET); - } - - - /* Set elapsed times to zero */ - Htime = 0; - Qtime = 0; - Rtime = Rstart; - Nperiods = 0; - - initsegs(); -} - - -int runqual(long *t) -/* -**-------------------------------------------------------------- -** Input: none -** Output: t = pointer to current simulation time (sec) -** Returns: error code -** Purpose: retrieves hydraulics for next hydraulic time step -** (at time *t) and saves current results to file -**-------------------------------------------------------------- -*/ -{ - long hydtime; /* Hydraulic solution time */ - long hydstep; /* Hydraulic time step */ - int errcode = 0; - int i; - - /* Update reported simulation time */ - *t = Qtime; - - /* Read hydraulic solution from hydraulics file */ - if (Qtime == Htime) - { - errcode = gethyd(&hydtime, &hydstep); - if (!OpenHflag) { // test for sequential vs stepwise - // sequential - Htime = hydtime + hydstep; - } - else { - // stepwise calculation - hydraulic results are already in memory - for (i=1; i<= Ntanks; ++i) { - QTankVolumes[i-1] = Tank[i].V; - } - - for (i=1; i<= Nlinks; ++i) - { - if (LinkStatus[i] <= CLOSED) { - QLinkFlow[i-1] = Q[i]; - } - } - - } - } - else { - // stepwise calculation - for (i=1; i<= Ntanks; ++i) { - QTankVolumes[i-1] = Tank[i].V; - } - - for (i=1; i<= Nlinks; ++i) - { - if (LinkStatus[i] <= CLOSED) { - QLinkFlow[i-1] = Q[i]; - } - } - - } - - return(errcode); -} - - -int nextqual(long *tstep) -/* -**-------------------------------------------------------------- -** Input: none -** Output: tstep = pointer to time step (sec) -** Returns: error code -** Purpose: updates WQ conditions until next hydraulic -** solution occurs (after *tstep secs.) -**-------------------------------------------------------------- -*/ -{ - long hydstep; /* Hydraulic solution time step */ - int errcode = 0; - double *tankVolumes; - int i; - - /* Determine time step */ - *tstep = 0; - - // hydstep = Htime - Qtime; - - if (Htime <= Dur) hydstep = Htime - Qtime; - else hydstep = 0; - - // if we're operating in stepwise mode, capture the tank levels so we can restore them later. - if (OpenHflag) { - tankVolumes = calloc(Ntanks, sizeof(double)); - for (i=1; i<=Ntanks; ++i) { - if (Tank[i].A != 0) { // skip reservoirs - tankVolumes[i-1] = Tank[i].V; - } - } - - // restore the previous step's tank volumes - for (i=1; i<=Ntanks; i++) { - if (Tank[i].A != 0) { // skip reservoirs again - int n = Tank[i].Node; - Tank[i].V = QTankVolumes[i-1]; - NodeHead[n] = tankgrade(i,Tank[i].V); - } - } - - // restore the previous step's pipe link flows - for (i=1; i<=Nlinks; i++) { - if (LinkStatus[i] <= CLOSED) { - Q[i] = 0.0; - } - } - - } - - /* Perform water quality routing over this time step */ - if (Qualflag != NONE && hydstep > 0) transport(hydstep); - - /* Update current time */ - if (OutOfMemory) errcode = 101; - if (!errcode) *tstep = hydstep; - Qtime += hydstep; - - /* Save final output if no more time steps */ - if (!errcode && Saveflag && *tstep == 0) errcode = savefinaloutput(); - - // restore tank levels to post-runH state, if needed. - if (OpenHflag) { - for (i=1; i<=Ntanks; i++) { - if (Tank[i].A != 0) { // skip reservoirs again - int n = Tank[i].Node; - Tank[i].V = tankVolumes[i-1]; - NodeHead[n] = tankgrade(i,Tank[i].V); - } - } - - for (i=1; i<=Nlinks; ++i) { - if (LinkStatus[i] <= CLOSED) { - Q[i] = QLinkFlow[i-1]; - } - } - - free(tankVolumes); - } - - return(errcode); -} - - -int stepqual(long *tleft) -/* -**-------------------------------------------------------------- -** Input: none -** Output: tleft = pointer to time left in simulation -** Returns: error code -** Purpose: updates WQ conditions over a single WQ time step -**-------------------------------------------------------------- -*/ -{ long dt, hstep, t, tstep; - int errcode = 0; - tstep = Qstep; - do - { - dt = tstep; - hstep = Htime - Qtime; - if (hstep < dt) - { - dt = hstep; - if (Qualflag != NONE) transport(dt); - Qtime += dt; - errcode = runqual(&t); - Qtime = t; - } - else - { - if (Qualflag != NONE) transport(dt); - Qtime += dt; - } - tstep -= dt; - if (OutOfMemory) errcode = 101; - } while (!errcode && tstep > 0); - *tleft = Dur - Qtime; - if (!errcode && Saveflag && *tleft == 0) errcode = savefinaloutput(); - return(errcode); -} - - -int closequal() -/* -**-------------------------------------------------------------- -** Input: none -** Output: returns error code -** Purpose: closes WQ solver system -**-------------------------------------------------------------- -*/ -{ - int errcode = 0; - - /* Free memory pool */ - if ( SegPool ) //(2.00.11 - LR) - { //(2.00.11 - LR) - AllocSetPool(SegPool); //(2.00.11 - LR) - AllocFreePool(); //(2.00.11 - LR) - } //(2.00.11 - LR) - - free(FirstSeg); - free(LastSeg); - free(FlowDir); - free(VolIn); - free(MassIn); - free(PipeRateCoeff); - free(TempQual); - free(QTankVolumes); - free(QLinkFlow); - return(errcode); -} - - -int gethyd(long *hydtime, long *hydstep) -/* -**----------------------------------------------------------- -** Input: none -** Output: hydtime = pointer to hydraulic solution time -** hydstep = pointer to hydraulic time step -** Returns: error code -** Purpose: retrieves hydraulic solution and hydraulic -** time step for next hydraulic event -** -** NOTE: when this function is called, WQ results have -** already been updated to the point in time when -** the next hydraulic event occurs. -**----------------------------------------------------------- -*/ -{ - int errcode = 0; - - // if hydraulics are not open, then we're operating in sequential mode. - // else hydraulics are open, so use the hydraulic results in memory rather than reading from the temp file. - if (!OpenHflag) { - /* Read hydraulic results from file */ - if (!readhyd(hydtime)) return(307); - if (!readhydstep(hydstep)) return(307); - Htime = *hydtime; - } - - /* Save current results to output file */ - if (Htime >= Rtime) - { - if (Saveflag) - { - errcode = saveoutput(); - Nperiods++; - } - Rtime += Rstep; - } - - /* If simulating WQ: */ - if (Qualflag != NONE && Qtime < Dur) - { - - /* Compute reaction rate coeffs. */ - if (Reactflag && Qualflag != AGE) { - ratecoeffs(); - } - - /* Initialize pipe segments (at time 0) or */ - /* else re-orient segments if flow reverses.*/ - //if (Qtime == 0) - // initsegs(); - //else - // if hydraulics are open, or if we're in sequential mode (where qtime can increase) - if (OpenHflag || Qtime != 0) { - reorientsegs(); - } - - } - return(errcode); -} - - -char setReactflag() -/* -**----------------------------------------------------------- -** Input: none -** Output: returns 1 for reactive WQ constituent, 0 otherwise -** Purpose: checks if reactive chemical being simulated -**----------------------------------------------------------- -*/ -{ - int i; - if (Qualflag == TRACE) return(0); - else if (Qualflag == AGE) return(1); - else - { - for (i=1; i<=Nlinks; i++) - { - if (Link[i].Type <= PIPE) - { - if (Link[i].Kb != 0.0 || Link[i].Kw != 0.0) return(1); - } - } - for (i=1; i<=Ntanks; i++) - if (Tank[i].Kb != 0.0) return(1); - } - return(0); -} - - -void transport(long tstep) -/* -**-------------------------------------------------------------- -** Input: tstep = length of current time step -** Output: none -** Purpose: transports constituent mass through pipe network -** under a period of constant hydraulic conditions. -**-------------------------------------------------------------- -*/ -{ - long qtime, dt; - - /* Repeat until elapsed time equals hydraulic time step */ - - AllocSetPool(SegPool); //(2.00.11 - LR) - qtime = 0; - while (!OutOfMemory && qtime < tstep) - { /* Qstep is quality time step */ - dt = MIN(Qstep,tstep-qtime); /* Current time step */ - qtime += dt; /* Update elapsed time */ - if (Reactflag) updatesegs(dt); /* Update quality in inner link segs */ - accumulate(dt); /* Accumulate flow at nodes */ - updatenodes(dt); /* Update nodal quality */ - sourceinput(dt); /* Compute inputs from sources */ - release(dt); /* Release new nodal flows */ - } - updatesourcenodes(tstep); /* Update quality at source nodes */ - -} - - -void initsegs() -/* -**-------------------------------------------------------------- -** Input: none -** Output: none -** Purpose: initializes water quality segments -**-------------------------------------------------------------- -*/ -{ - int j,k; - double c,v; - - /* Examine each link */ - for (k=1; k<=Nlinks; k++) - { - - /* Establish flow direction */ - FlowDir[k] = '+'; - if (Q[k] < 0.) { - FlowDir[k] = '-'; - } - - /* Set segs to zero */ - LastSeg[k] = NULL; - FirstSeg[k] = NULL; - - /* Find quality of downstream node */ - j = DOWN_NODE(k); - if (j <= Njuncs) c = NodeQual[j]; - else c = Tank[j-Njuncs].C; - - /* Fill link with single segment with this quality */ - addseg(k,LINKVOL(k),c); - } - - /* Initialize segments in tanks that use them */ - for (j=1; j<=Ntanks; j++) - { - - /* Skip reservoirs & complete mix tanks */ - if (Tank[j].A == 0.0 - || Tank[j].MixModel == MIX1) continue; - - /* Tank segment pointers are stored after those for links */ - k = Nlinks + j; - c = Tank[j].C; - LastSeg[k] = NULL; - FirstSeg[k] = NULL; - - /* Add 2 segments for 2-compartment model */ - if (Tank[j].MixModel == MIX2) - { - v = MAX(0,Tank[j].V-Tank[j].V1max); - addseg(k,v,c); - v = Tank[j].V - v; - addseg(k,v,c); - } - - /* Add one segment for FIFO & LIFO models */ - else - { - v = Tank[j].V; - addseg(k,v,c); - } - } -} - - -void reorientsegs() -/* -**-------------------------------------------------------------- -** Input: none -** Output: none -** Purpose: re-orients segments (if flow reverses) -**-------------------------------------------------------------- -*/ -{ - Pseg seg, nseg, pseg; - int k; - char newdir; - - /* Examine each link */ - for (k=1; k<=Nlinks; k++) - { - - /* Find new flow direction */ - newdir = '+'; - if (Q[k] == 0.0) { - newdir = FlowDir[k]; - } - else if (Q[k] < 0.0) { - newdir = '-'; - } - - /* If direction changes, then reverse order of segments */ - /* (first to last) and save new direction */ - if (newdir != FlowDir[k]) - { - seg = FirstSeg[k]; - FirstSeg[k] = LastSeg[k]; - LastSeg[k] = seg; - pseg = NULL; - while (seg != NULL) - { - nseg = seg->prev; - seg->prev = pseg; - pseg = seg; - seg = nseg; - } - FlowDir[k] = newdir; - } - } -} - - -void updatesegs(long dt) -/* -**------------------------------------------------------------- -** Input: t = time from last WQ segment update -** Output: none -** Purpose: reacts material in pipe segments up to time t -**------------------------------------------------------------- -*/ -{ - int k; - Pseg seg; - double cseg, rsum, vsum; - - /* Examine each link in network */ - for (k=1; k<=Nlinks; k++) - { - - /* Skip zero-length links (pumps & valves) */ - rsum = 0.0; - vsum = 0.0; - if (Link[k].Len == 0.0) continue; - - /* Examine each segment of the link */ - seg = FirstSeg[k]; - while (seg != NULL) - { - - /* React segment over time dt */ - cseg = seg->c; - seg->c = pipereact(k,seg->c,seg->v,dt); - - /* Accumulate volume-weighted reaction rate */ - if (Qualflag == CHEM) - { - rsum += ABS((seg->c - cseg))*seg->v; - vsum += seg->v; - } - seg = seg->prev; - } - - /* Normalize volume-weighted reaction rate */ - if (vsum > 0.0) PipeRateCoeff[k] = rsum/vsum/dt*SECperDAY; - else PipeRateCoeff[k] = 0.0; - } -} - - -void removesegs(int k) -/* -**------------------------------------------------------------- -** Input: k = link index -** Output: none -** Purpose: removes all segments in link k -**------------------------------------------------------------- -*/ -{ - Pseg seg; - seg = FirstSeg[k]; - while (seg != NULL) - { - FirstSeg[k] = seg->prev; - seg->prev = FreeSeg; - FreeSeg = seg; - seg = FirstSeg[k]; - } - LastSeg[k] = NULL; -} - - -void addseg(int k, double v, double c) -/* -**------------------------------------------------------------- -** Input: k = link segment -** v = segment volume -** c = segment quality -** Output: none -** Purpose: adds a segment to start of link k (i.e., upstream -** of current last segment). -**------------------------------------------------------------- -*/ -{ - Pseg seg; - - if (FreeSeg != NULL) - { - seg = FreeSeg; - FreeSeg = seg->prev; - } - else - { - seg = (struct Sseg *) Alloc(sizeof(struct Sseg)); - if (seg == NULL) - { - OutOfMemory = TRUE; - return; - } - } - seg->v = v; - seg->c = c; - seg->prev = NULL; - if (FirstSeg[k] == NULL) FirstSeg[k] = seg; - if (LastSeg[k] != NULL) LastSeg[k]->prev = seg; - LastSeg[k] = seg; -} - - -void accumulate(long dt) -/* -**------------------------------------------------------------- -** Input: dt = current WQ time step -** Output: none -** Purpose: accumulates mass flow at nodes and updates nodal -** quality -**------------------------------------------------------------- -*/ -{ - int i,j,k; - double cseg,v,vseg; - Pseg seg; - - /* Re-set memory used to accumulate mass & volume */ - memset(VolIn,0,(Nnodes+1)*sizeof(double)); - memset(MassIn,0,(Nnodes+1)*sizeof(double)); - memset(TempQual,0,(Nnodes+1)*sizeof(double)); - - /* Compute average conc. of segments adjacent to each node */ - /* (For use if there is no transport through the node) */ - for (k=1; k<=Nlinks; k++) - { - j = DOWN_NODE(k); /* Downstream node */ - if (FirstSeg[k] != NULL) /* Accumulate concentrations */ - { - MassIn[j] += FirstSeg[k]->c; - VolIn[j]++; - } - j = UP_NODE(k); /* Upstream node */ - if (LastSeg[k] != NULL) /* Accumulate concentrations */ - { - MassIn[j] += LastSeg[k]->c; - VolIn[j]++; - } - } - - for (k=1; k<=Nnodes; k++) { - if (VolIn[k] > 0.0) { - TempQual[k] = MassIn[k]/VolIn[k]; - } - } - - /* Move mass from first segment of each pipe into downstream node */ - memset(VolIn,0,(Nnodes+1)*sizeof(double)); - memset(MassIn,0,(Nnodes+1)*sizeof(double)); - for (k=1; k<=Nlinks; k++) - { - i = UP_NODE(k); /* Upstream node */ - j = DOWN_NODE(k); /* Downstream node */ - v = ABS(Q[k])*dt; /* Flow volume */ - -//// Start of deprecated code segment //// //(2.00.12 - LR) - - /* If link volume < flow volume, then transport upstream */ - /* quality to downstream node and remove all link segments. */ -/* if (LINKVOL(k) < v) - { - VolIn[j] += v; - seg = FirstSeg[k]; - cseg = NodeQuali]; - if (seg != NULL) cseg = seg->c; - MassIn[j] += v*cseg; - removesegs(k); - } -*/ - /* Otherwise remove flow volume from leading segments */ - /* and accumulate flow mass at downstream node */ - //else - -//// End of deprecated code segment. //// //(2.00.12 - LR) - - while (v > 0.0) //(2.00.12 - LR) - { - /* Identify leading segment in pipe */ - seg = FirstSeg[k]; - if (seg == NULL) break; - - /* Volume transported from this segment is */ - /* minimum of flow volume & segment volume */ - /* (unless leading segment is also last segment) */ - vseg = seg->v; - vseg = MIN(vseg,v); - if (seg == LastSeg[k]) vseg = v; - - /* Update volume & mass entering downstream node */ - cseg = seg->c; - VolIn[j] += vseg; - MassIn[j] += vseg*cseg; - - /* Reduce flow volume by amount transported */ - v -= vseg; - - /* If all of segment's volume was transferred, then */ - /* replace leading segment with the one behind it */ - /* (Note that the current seg is recycled for later use.) */ - if (v >= 0.0 && vseg >= seg->v) - { - FirstSeg[k] = seg->prev; - if (FirstSeg[k] == NULL) LastSeg[k] = NULL; - seg->prev = FreeSeg; - FreeSeg = seg; - } - - /* Otherwise reduce segment's volume */ - else - { - seg->v -= vseg; - } - } /* End while */ - } /* Next link */ -} - - -void updatenodes(long dt) -/* -**--------------------------------------------------------------------------- -** Input: dt = current WQ time step -** Output: none -** Purpose: updates concentration at all nodes to mixture of accumulated -** inflow from connecting pipes. -** -** Note: Does not account for source flow effects. TempQual[i] contains -** average concen. of segments adjacent to node i, used in case -** there was no inflow into i. -**--------------------------------------------------------------------------- -*/ -{ - int i; - - /* Update junction quality */ - for (i=1; i<=Njuncs; i++) - { - if (NodeDemand[i] < 0.0) { - VolIn[i] -= NodeDemand[i]*dt; - } - if (VolIn[i] > 0.0) { - NodeQual[i] = MassIn[i]/VolIn[i]; - } - else { - NodeQual[i] = TempQual[i]; - } - } - - /* Update tank quality */ - updatetanks(dt); - - /* For flow tracing, set source node concen. to 100. */ - if (Qualflag == TRACE) NodeQual[TraceNode] = 100.0; -} - - -void sourceinput(long dt) -/* -**--------------------------------------------------------------------- -** Input: dt = current WQ time step -** Output: none -** Purpose: computes contribution (if any) of mass additions from WQ -** sources at each node. -**--------------------------------------------------------------------- -*/ -{ - int j,n; - double massadded = 0.0, s, volout; - double qout, qcutoff; - Psource source; - - /* Establish a flow cutoff which indicates no outflow from a node */ - qcutoff = 10.0*TINY; - - /* Zero-out the work array TempQual */ - memset(TempQual,0,(Nnodes+1)*sizeof(double)); - if (Qualflag != CHEM) return; - - /* Consider each node */ - for (n=1; n<=Nnodes; n++) - { - double thisDemand = NodeDemand[n]; - /* Skip node if no WQ source */ - source = Node[n].S; - if (source == NULL) continue; - if (source->C0 == 0.0) continue; - - /* Find total flow volume leaving node */ - if (n <= Njuncs) volout = VolIn[n]; /* Junctions */ - else volout = VolIn[n] - (thisDemand * dt); /* Tanks */ - qout = volout / (double) dt; - - /* Evaluate source input only if node outflow > cutoff flow */ - if (qout > qcutoff) - { - - /* Mass added depends on type of source */ - s = sourcequal(source); - switch(source->Type) - { - /* Concen. Source: */ - /* Mass added = source concen. * -(demand) */ - case CONCEN: - - /* Only add source mass if demand is negative */ - if (thisDemand < 0.0) - { - massadded = -s*thisDemand*dt; - - /* If node is a tank then set concen. to 0. */ - /* (It will be re-set to true value in updatesourcenodes()) */ - if (n > Njuncs) NodeQual[n] = 0.0; - } - else massadded = 0.0; - break; - - /* Mass Inflow Booster Source: */ - case MASS: - massadded = s*dt; - break; - - /* Setpoint Booster Source: */ - /* Mass added is difference between source */ - /* & node concen. times outflow volume */ - case SETPOINT: - if (s > NodeQual[n]) { - massadded = (s-NodeQual[n])*volout; - } - else { - massadded = 0.0; - } - break; - - /* Flow-Paced Booster Source: */ - /* Mass added = source concen. times outflow volume */ - case FLOWPACED: - massadded = s*volout; - break; - } - - /* Source concen. contribution = (mass added / outflow volume) */ - TempQual[n] = massadded/volout; - - /* Update total mass added for time period & simulation */ - source->Smass += massadded; - if (Htime >= Rstart) Wsource += massadded; - } - } - - /* Add mass inflows from reservoirs to Wsource*/ - if (Htime >= Rstart) - { - for (j=1; j<=Ntanks; j++) - { - if (Tank[j].A == 0.0) - { - n = Njuncs + j; - volout = VolIn[n] - NodeDemand[n]*dt; - if (volout > 0.0) Wsource += volout*NodeQual[n]; - } - } - } -} - - -void release(long dt) -/* -**--------------------------------------------------------- -** Input: dt = current WQ time step -** Output: none -** Purpose: creates new segments in outflow links from nodes. -**--------------------------------------------------------- -*/ -{ - int k,n; - double c,q,v; - Pseg seg; - - /* Examine each link */ - for (k=1; k<=Nlinks; k++) - { - - /* Ignore links with no flow */ - if (Q[k] == 0.0) continue; - - /* Find flow volume released to link from upstream node */ - /* (NOTE: Flow volume is allowed to be > link volume.) */ - n = UP_NODE(k); - q = ABS(Q[k]); - v = q*dt; - - /* Include source contribution in quality released from node. */ - c = NodeQual[n] + TempQual[n]; - - /* If link has a last seg, check if its quality */ - /* differs from that of the flow released from node.*/ - if ( (seg = LastSeg[k]) != NULL) - { - /* Quality of seg close to that of node */ - if (ABS(seg->c - c) < Ctol) - { - seg->c = (seg->c*seg->v + c*v) / (seg->v + v); //(2.00.11 - LR) - seg->v += v; - } - - /* Otherwise add a new seg to end of link */ - else addseg(k,v,c); - } - - /* If link has no segs then add a new one. */ - else addseg(k,LINKVOL(k),c); - } -} - - -void updatesourcenodes(long dt) -/* -**--------------------------------------------------- -** Input: dt = current WQ time step -** Output: none -** Purpose: updates quality at source nodes. -** (TempQual[n] = concen. added by source at node n) -**--------------------------------------------------- -*/ -{ - int i,n; - Psource source; - - if (Qualflag != CHEM) return; - - /* Examine each WQ source node */ - for (n=1; n<=Nnodes; n++) - { - source = Node[n].S; - if (source == NULL) continue; - - /* Add source to current node concen. */ - NodeQual[n] += TempQual[n]; - - /* For tanks, node concen. = internal concen. */ - if (n > Njuncs) - { - i = n - Njuncs; - if (Tank[i].A > 0.0) NodeQual[n] = Tank[i].C; - } - - /* Normalize mass added at source to time step */ - source->Smass /= (double)dt; - } -} - - -void updatetanks(long dt) -/* -**--------------------------------------------------- -** Input: dt = current WQ time step -** Output: none -** Purpose: updates tank volumes & concentrations -**--------------------------------------------------- -*/ -{ - int i,n; - - /* Examine each reservoir & tank */ - for (i=1; i<=Ntanks; i++) - { - n = Tank[i].Node; - /* Use initial quality for reservoirs */ - if (Tank[i].A == 0.0) - { - NodeQual[n] = Node[n].C0; - } - /* Update tank WQ based on mixing model */ - else { - switch(Tank[i].MixModel) - { - case MIX2: tankmix2(i,dt); break; - case FIFO: tankmix3(i,dt); break; - case LIFO: tankmix4(i,dt); break; - default: tankmix1(i,dt); break; - } - - } - } -} - - -//// Deprecated version of tankmix1 //// //(2.00.12 - LR) -//void tankmix1(int i, long dt) -/* -**--------------------------------------------- -** Input: i = tank index -** dt = current WQ time step -** Output: none -** Purpose: complete mix tank model -**--------------------------------------------- -*/ -//{ -// int n; -// double cin; - -// /* Blend inflow with contents */ -// n = Tank[i].Node; -// if (VolIn[n] > 0.0) cin = MassIn[n]/VolIn[n]; -// else cin = 0.0; -// if (Tank[i].V > 0.0) -// Tank[i].C = tankreact(Tank[i].C,Tank[i].V,Tank[i].Kb,dt) + -// (cin - Tank[i].C)*VolIn[n]/Tank[i].V; -// else Tank[i].C = cin; -// Tank[i].C = MAX(0.0, Tank[i].C); - -// /* Update tank volume & nodal quality */ -// Tank[i].V += D[n]*dt; -// NodeQual[n] = Tank[i].C; -//} - - -//// New version of tankmix1 //// //(2.00.12 - LR) -void tankmix1(int i, long dt) -/* -**--------------------------------------------- -** Input: i = tank index -** dt = current WQ time step -** Output: none -** Purpose: complete mix tank model -**--------------------------------------------- -*/ -{ - int n; - double cin; - double c, cmax, vold, vin; - - /* React contents of tank */ - c = tankreact(Tank[i].C,Tank[i].V,Tank[i].Kb,dt); - - /* Determine tank & volumes */ - vold = Tank[i].V; - n = Tank[i].Node; - Tank[i].V += NodeDemand[n]*dt; - vin = VolIn[n]; - - /* Compute inflow concen. */ - if (vin > 0.0) cin = MassIn[n]/vin; - else cin = 0.0; - cmax = MAX(c, cin); - - /* Mix inflow with tank contents */ - if (vin > 0.0) c = (c*vold + cin*vin)/(vold + vin); - c = MIN(c, cmax); - c = MAX(c, 0.0); - Tank[i].C = c; - NodeQual[n] = Tank[i].C; -} - -/*** Updated 10/25/00 ***/ -//// New version of tankmix2 //// //(2.00.12 - LR) -void tankmix2(int i, long dt) -/* -**------------------------------------------------ -** Input: i = tank index -** dt = current WQ time step -** Output: none -** Purpose: 2-compartment tank model -** (seg1 = mixing zone, -** seg2 = ambient zone) -**------------------------------------------------ -*/ -{ - int k,n; - double cin, /* Inflow quality */ - vin, /* Inflow volume */ - vt, /* Transferred volume */ - vnet, /* Net volume change */ - v1max; /* Full mixing zone volume */ - Pseg seg1,seg2; /* Compartment segments */ - - /* Identify segments for each compartment */ - k = Nlinks + i; - seg1 = LastSeg[k]; - seg2 = FirstSeg[k]; - if (seg1 == NULL || seg2 == NULL) return; - - /* React contents of each compartment */ - seg1->c = tankreact(seg1->c,seg1->v,Tank[i].Kb,dt); - seg2->c = tankreact(seg2->c,seg2->v,Tank[i].Kb,dt); - - /* Find inflows & outflows */ - n = Tank[i].Node; - vnet = NodeDemand[n]*dt; - vin = VolIn[n]; - if (vin > 0.0) cin = MassIn[n]/vin; - else cin = 0.0; - v1max = Tank[i].V1max; - - /* Tank is filling */ - vt = 0.0; - if (vnet > 0.0) - { - vt = MAX(0.0, (seg1->v + vnet - v1max)); - if (vin > 0.0) - { - seg1->c = ((seg1->c)*(seg1->v) + cin*vin) / (seg1->v + vin); - } - if (vt > 0.0) - { - seg2->c = ((seg2->c)*(seg2->v) + (seg1->c)*vt) / (seg2->v + vt); - } - } - - /* Tank is emptying */ - if (vnet < 0.0) - { - if (seg2->v > 0.0) - { - vt = MIN(seg2->v, (-vnet)); - } - if (vin + vt > 0.0) - { - seg1->c = ((seg1->c)*(seg1->v) + cin*vin + (seg2->c)*vt) / - (seg1->v + vin + vt); - } - } - - /* Update segment volumes */ - if (vt > 0.0) - { - seg1->v = v1max; - if (vnet > 0.0) seg2->v += vt; - else seg2->v = MAX(0.0, ((seg2->v)-vt)); - } - else - { - seg1->v += vnet; - seg1->v = MIN(seg1->v, v1max); - seg1->v = MAX(0.0, seg1->v); - seg2->v = 0.0; - } - Tank[i].V += vnet; - Tank[i].V = MAX(0.0, Tank[i].V); - - /* Use quality of mixed compartment (seg1) to */ - /* represent quality of tank since this is where */ - /* outflow begins to flow from */ - Tank[i].C = seg1->c; - NodeQual[n] = Tank[i].C; -} - - -void tankmix3(int i, long dt) -/* -**---------------------------------------------------------- -** Input: i = tank index -** dt = current WQ time step -** Output: none -** Purpose: First-In-First-Out (FIFO) tank model -**---------------------------------------------------------- -*/ -{ - int k,n; - double vin,vnet,vout,vseg; - double cin,vsum,csum; - Pseg seg; - - k = Nlinks + i; - if (LastSeg[k] == NULL || FirstSeg[k] == NULL) return; - - /* React contents of each compartment */ - if (Reactflag) - { - seg = FirstSeg[k]; - while (seg != NULL) - { - seg->c = tankreact(seg->c,seg->v,Tank[i].Kb,dt); - seg = seg->prev; - } - } - - /* Find inflows & outflows */ - n = Tank[i].Node; - vnet = NodeDemand[n]*dt; - vin = VolIn[n]; - vout = vin - vnet; - if (vin > 0.0) cin = MassIn[n]/VolIn[n]; - else cin = 0.0; - Tank[i].V += vnet; - Tank[i].V = MAX(0.0, Tank[i].V); //(2.00.12 - LR) - - /* Withdraw flow from first segment */ - vsum = 0.0; - csum = 0.0; - while (vout > 0.0) - { - seg = FirstSeg[k]; - if (seg == NULL) break; - vseg = seg->v; /* Flow volume from leading seg */ - vseg = MIN(vseg,vout); - if (seg == LastSeg[k]) vseg = vout; - vsum += vseg; - csum += (seg->c)*vseg; - vout -= vseg; /* Remaining flow volume */ - if (vout >= 0.0 && vseg >= seg->v) /* Seg used up */ - { - if (seg->prev) //(2.00.12 - LR) - { //(2.00.12 - LR) - FirstSeg[k] = seg->prev; - //if (FirstSeg[k] == NULL) LastSeg[k] = NULL; //(2.00.12 - LR) - seg->prev = FreeSeg; - FreeSeg = seg; - } //(2.00.12 - LR) - } - else /* Remaining volume in segment */ - { - seg->v -= vseg; - } - } - - /* Use quality withdrawn from 1st segment */ - /* to represent overall quality of tank */ - if (vsum > 0.0) Tank[i].C = csum/vsum; - else Tank[i].C = FirstSeg[k]->c; - NodeQual[n] = Tank[i].C; - - /* Add new last segment for new flow entering tank */ - if (vin > 0.0) - { - if ( (seg = LastSeg[k]) != NULL) - { - /* Quality is the same, so just add flow volume to last seg */ - if (ABS(seg->c - cin) < Ctol) seg->v += vin; - - /* Otherwise add a new seg to tank */ - else addseg(k,vin,cin); - } - - /* If no segs left then add a new one. */ - else addseg(k,vin,cin); - } -} - - -void tankmix4(int i, long dt) -/* -**---------------------------------------------------------- -** Input: i = tank index -** dt = current WQ time step -** Output: none -** Purpose: Last In-First Out (LIFO) tank model -**---------------------------------------------------------- -*/ -{ - int k, n; - double vin, vnet, cin, vsum, csum, vseg; - Pseg seg, tmpseg; - - k = Nlinks + i; - if (LastSeg[k] == NULL || FirstSeg[k] == NULL) return; - - /* React contents of each compartment */ - if (Reactflag) - { - seg = LastSeg[k]; - while (seg != NULL) - { - seg->c = tankreact(seg->c,seg->v,Tank[i].Kb,dt); - seg = seg->prev; - } - } - - /* Find inflows & outflows */ - n = Tank[i].Node; - vnet = NodeDemand[n]*dt; - vin = VolIn[n]; - if (vin > 0.0) cin = MassIn[n]/VolIn[n]; - else cin = 0.0; - Tank[i].V += vnet; - Tank[i].V = MAX(0.0, Tank[i].V); //(2.00.12 - LR) - Tank[i].C = LastSeg[k]->c; - - /* If tank filling, then create new last seg */ - if (vnet > 0.0) - { - if ( (seg = LastSeg[k]) != NULL) - { - /* Quality is the same, so just add flow volume to last seg */ - if (ABS(seg->c - cin) < Ctol) seg->v += vnet; - - /* Otherwise add a new last seg to tank */ - /* which points to old last seg */ - else - { - tmpseg = seg; - LastSeg[k] = NULL; - addseg(k,vnet,cin); - LastSeg[k]->prev = tmpseg; - } - } - - /* If no segs left then add a new one. */ - else addseg(k,vnet,cin); - - /* Update reported tank quality */ - Tank[i].C = LastSeg[k]->c; - } - - /* If net emptying then remove last segments until vnet consumed */ - else if (vnet < 0.0) - { - vsum = 0.0; - csum = 0.0; - vnet = -vnet; - while (vnet > 0.0) - { - seg = LastSeg[k]; - if (seg == NULL) break; - vseg = seg->v; - vseg = MIN(vseg,vnet); - if (seg == FirstSeg[k]) vseg = vnet; - vsum += vseg; - csum += (seg->c)*vseg; - vnet -= vseg; - if (vnet >= 0.0 && vseg >= seg->v) /* Seg used up */ - { - if (seg->prev) //(2.00.12 - LR) - { //(2.00.12 - LR) - LastSeg[k] = seg->prev; - //if (LastSeg[k] == NULL) FirstSeg[k] = NULL; //(2.00.12 - LR) - seg->prev = FreeSeg; - FreeSeg = seg; - } //(2.00.12 - LR) - } - else /* Remaining volume in segment */ - { - seg->v -= vseg; - } - } - /* Reported tank quality is mixture of flow released and any inflow */ - Tank[i].C = (csum + MassIn[n])/(vsum + vin); - } - NodeQual[n] = Tank[i].C; -} - - -double sourcequal(Psource source) -/* -**-------------------------------------------------------------- -** Input: j = source index -** Output: returns source WQ value -** Purpose: determines source concentration in current time period -**-------------------------------------------------------------- -*/ -{ - int i; - long k; - double c; - - /* Get source concentration (or mass flow) in original units */ - c = source->C0; - - /* Convert mass flow rate from min. to sec. */ - /* and convert concen. from liters to cubic feet */ - if (source->Type == MASS) c /= 60.0; - else c /= Ucf[QUALITY]; - - /* Apply time pattern if assigned */ - i = source->Pat; - if (i == 0) return(c); - k = ((Qtime+Pstart)/Pstep) % (long)Pattern[i].Length; - return(c*Pattern[i].F[k]); -} - - -double avgqual(int k) -/* -**-------------------------------------------------------------- -** Input: k = link index -** Output: returns WQ value -** Purpose: computes average quality in link k -**-------------------------------------------------------------- -*/ -{ - double vsum = 0.0, - msum = 0.0; - Pseg seg; - - if (Qualflag == NONE) return(0.); - seg = FirstSeg[k]; - while (seg != NULL) - { - vsum += seg->v; - msum += (seg->c)*(seg->v); - seg = seg->prev; - } - if (vsum > 0.0 && Qtime > 0) return(msum/vsum); - else return( (NodeQual[Link[k].N1] + NodeQual[Link[k].N2])/2. ); -} - - -void ratecoeffs() -/* -**-------------------------------------------------------------- -** Input: none -** Output: none -** Purpose: determines wall reaction coeff. for each pipe -**-------------------------------------------------------------- -*/ -{ - int k; - double kw; - - for (k=1; k<=Nlinks; k++) - { - kw = Link[k].Kw; - if (kw != 0.0) kw = piperate(k); - Link[k].Rc = kw; - PipeRateCoeff[k] = 0.0; - } -} /* End of ratecoeffs */ - - -double piperate(int k) -/* -**-------------------------------------------------------------- -** Input: k = link index -** Output: returns reaction rate coeff. for 1st-order wall -** reactions or mass transfer rate coeff. for 0-order -** reactions -** Purpose: finds wall reaction rate coeffs. -**-------------------------------------------------------------- -*/ -{ - double a,d,u,kf,kw,y,Re,Sh; - - d = Link[k].Diam; /* Pipe diameter, ft */ - -/* Ignore mass transfer if Schmidt No. is 0 */ - if (Sc == 0.0) - { - if (WallOrder == 0.0) return(BIG); - else return(Link[k].Kw*(4.0/d)/Ucf[ELEV]); - } - -/* Compute Reynolds No. */ - a = PI*d*d/4.0; - u = ABS(Q[k])/a; - Re = u*d/Viscos; - -/* Compute Sherwood No. for stagnant flow */ -/* (mass transfer coeff. = Diffus./radius) */ - if (Re < 1.0) Sh = 2.0; - -/* Compute Sherwood No. for turbulent flow */ -/* using the Notter-Sleicher formula. */ - else if (Re >= 2300.0) - Sh = 0.0149*pow(Re,0.88)*pow(Sc,0.333); - -/* Compute Sherwood No. for laminar flow */ -/* using Graetz solution formula. */ - else - { - y = d/Link[k].Len*Re*Sc; - Sh = 3.65+0.0668*y/(1.0+0.04*pow(y,0.667)); - } - -/* Compute mass transfer coeff. (in ft/sec) */ - kf = Sh*Diffus/d; - -/* For zero-order reaction, return mass transfer coeff. */ - if (WallOrder == 0.0) return(kf); - -/* For first-order reaction, return apparent wall coeff. */ - kw = Link[k].Kw/Ucf[ELEV]; /* Wall coeff, ft/sec */ - kw = (4.0/d)*kw*kf/(kf+ABS(kw)); /* Wall coeff, 1/sec */ - return(kw); -} /* End of piperate */ - - -double pipereact(int k, double c, double v, long dt) -/* -**------------------------------------------------------------ -** Input: k = link index -** c = current WQ in segment -** v = segment volume -** dt = time step -** Output: returns new WQ value -** Purpose: computes new quality in a pipe segment after -** reaction occurs -**------------------------------------------------------------ -*/ -{ - double cnew, dc, dcbulk, dcwall, rbulk, rwall; - - /* For water age (hrs), update concentration by timestep */ - if (Qualflag == AGE) return(c+(double)dt/3600.0); - - /* Otherwise find bulk & wall reaction rates */ - rbulk = bulkrate(c,Link[k].Kb,BulkOrder)*Bucf; - rwall = wallrate(c,Link[k].Diam,Link[k].Kw,Link[k].Rc); - - /* Find change in concentration over timestep */ - dcbulk = rbulk*(double)dt; - dcwall = rwall*(double)dt; - - /* Update cumulative mass reacted */ - if (Htime >= Rstart) - { - Wbulk += ABS(dcbulk)*v; - Wwall += ABS(dcwall)*v; - } - - /* Update concentration */ - dc = dcbulk + dcwall; - cnew = c + dc; - cnew = MAX(0.0,cnew); - return(cnew); -} - - -double tankreact(double c, double v, double kb, long dt) -/* -**------------------------------------------------------- -** Input: c = current WQ in tank -** v = tank volume -** kb = reaction coeff. -** dt = time step -** Output: returns new WQ value -** Purpose: computes new quality in a tank after -** reaction occurs -**------------------------------------------------------- -*/ -{ - double cnew, dc, rbulk; - -/*** Updated 9/7/00 ***/ - /* If no reaction then return current WQ */ - if (!Reactflag) return(c); - - /* For water age, update concentration by timestep */ - if (Qualflag == AGE) return(c + (double)dt/3600.0); - - /* Find bulk reaction rate */ - rbulk = bulkrate(c,kb,TankOrder)*Tucf; - - /* Find concentration change & update quality */ - dc = rbulk*(double)dt; - if (Htime >= Rstart) Wtank += ABS(dc)*v; - cnew = c + dc; - cnew = MAX(0.0,cnew); - return(cnew); -} - - -double bulkrate(double c, double kb, double order) -/* -**----------------------------------------------------------- -** Input: c = current WQ concentration -** kb = bulk reaction coeff. -** order = bulk reaction order -** Output: returns bulk reaction rate -** Purpose: computes bulk reaction rate (mass/volume/time) -**----------------------------------------------------------- -*/ -{ - double c1; - - /* Find bulk reaction potential taking into account */ - /* limiting potential & reaction order. */ - - /* Zero-order kinetics: */ - if (order == 0.0) c = 1.0; - - /* Michaelis-Menton kinetics: */ - else if (order < 0.0) - { - c1 = Climit + SGN(kb)*c; - if (ABS(c1) < TINY) c1 = SGN(c1)*TINY; - c = c/c1; - } - - /* N-th order kinetics: */ - else - { - /* Account for limiting potential */ - if (Climit == 0.0) c1 = c; - else c1 = MAX(0.0, SGN(kb)*(Climit-c)); - - /* Compute concentration potential */ - if (order == 1.0) c = c1; - else if (order == 2.0) c = c1*c; - else c = c1*pow(MAX(0.0,c),order-1.0); - } - - /* Reaction rate = bulk coeff. * potential) */ - if (c < 0) c = 0; - return(kb*c); -} - - -double wallrate(double c, double d, double kw, double kf) -/* -**------------------------------------------------------------ -** Input: c = current WQ concentration -** d = pipe diameter -** kw = intrinsic wall reaction coeff. -** kf = mass transfer coeff. for 0-order reaction -** (ft/sec) or apparent wall reaction coeff. -** for 1-st order reaction (1/sec) -** Output: returns wall reaction rate in mass/ft3/sec -** Purpose: computes wall reaction rate -**------------------------------------------------------------ -*/ -{ - if (kw == 0.0 || d == 0.0) return(0.0); - if (WallOrder == 0.0) /* 0-order reaction */ - { - kf = SGN(kw)*c*kf; /* Mass transfer rate (mass/ft2/sec)*/ - kw = kw*SQR(Ucf[ELEV]); /* Reaction rate (mass/ft2/sec) */ - if (ABS(kf) < ABS(kw)) /* Reaction mass transfer limited */ - kw = kf; - return(kw*4.0/d); /* Reaction rate (mass/ft3/sec) */ - } - else return(c*kf); /* 1st-order reaction */ -} - -/************************* End of QUALITY.C ***************************/ +/* +******************************************************************************* + +QUALITY.C -- Water Quality Simulator for EPANET Program + +VERSION: 2.00 +DATE: 5/29/00 + 9/7/00 + 10/25/00 + 8/15/07 (2.00.11) + 2/14/08 (2.00.12) +AUTHOR: L. Rossman + US EPA - NRMRL + + This module contains the network water quality simulator. + + For each time period, hydraulic results are read in from the + binary file HydFile, hydraulic and water quality results are + written to the binary output file OutFile (if the current period + is a reporting period), and the water quality is transported + and reacted over the duration of the time period. + + The entry points for this module are: + openqual() -- called from ENopenQ() in EPANET.C + initqual() -- called from ENinitQ() in EPANET.C + runqual() -- called from ENrunQ() in EPANET.C + nextqual() -- called from ENnextQ() in EPANET.C + stepqual() -- called from ENstepQ() in EPANET.C + closequal() -- called from ENcloseQ() in EPANET.C + + Calls are made to: + AllocInit() + Alloc() + AllocFree() + 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. + + Calls are also made to: + readhyd() + readhydstep() + savenetdata() + saveoutput() + savefinaloutput() + in OUTPUT.C to retrieve hydraulic results and save all results. + +******************************************************************************* +*/ + +#include +#include +#ifndef __APPLE__ +#include +#else +#include +#endif +#include "hash.h" +#include "text.h" +#include "types.h" +#include "epanet2.h" +#include "funcs.h" +#include +#define EXTERN extern +#include "mempool.h" +#include "vars.h" + +/* +** Macros to identify upstream & downstream nodes of a link +** under the current flow and to compute link volume +*/ +#define UP_NODE(x) \ + ((qu->FlowDir[(x)] == POSITIVE) ? net->Link[(x)].N1 : net->Link[(x)].N2) +#define DOWN_NODE(x) \ + ((qu->FlowDir[(x)] == POSITIVE) ? net->Link[(x)].N2 : net->Link[(x)].N1) +#define LINKVOL(k) (0.785398 * net->Link[(k)].Len * SQR(net->Link[(k)].Diam)) + +int openqual(EN_Project *pr) +/* +**-------------------------------------------------------------- +** Input: none +** Output: returns error code +** Purpose: opens WQ solver system +**-------------------------------------------------------------- +*/ +{ + int errcode = 0; + int n; + + quality_t *qu = &pr->quality; + EN_Network *net = &pr->network; + + /* Allocate memory pool for WQ segments */ + qu->OutOfMemory = FALSE; + qu->SegPool = AllocInit(); + if (qu->SegPool == NULL) { + errcode = 101; + } + + /* Allocate scratch array & reaction rate array*/ + qu->TempQual = (double *)calloc(MAX((net->Nnodes + 1), (net->Nlinks + 1)), + sizeof(double)); + qu->PipeRateCoeff = (double *)calloc((net->Nlinks + 1), sizeof(double)); + ERRCODE(MEMCHECK(qu->TempQual)); + ERRCODE(MEMCHECK(qu->PipeRateCoeff)); + + /* Allocate memory for WQ solver */ + n = net->Nlinks + net->Ntanks + 1; + qu->FirstSeg = (Pseg *)calloc(n, sizeof(Pseg)); + qu->LastSeg = (Pseg *)calloc(n, sizeof(Pseg)); + qu->FlowDir = (FlowDirection *)calloc(n, sizeof(FlowDirection)); + n = net->Nnodes + 1; + qu->VolIn = (double *)calloc(n, sizeof(double)); + qu->MassIn = (double *)calloc(n, sizeof(double)); + ERRCODE(MEMCHECK(qu->FirstSeg)); + ERRCODE(MEMCHECK(qu->LastSeg)); + ERRCODE(MEMCHECK(qu->FlowDir)); + ERRCODE(MEMCHECK(qu->VolIn)); + ERRCODE(MEMCHECK(qu->MassIn)); + return (errcode); +} + +/* Local function to compute unit conversion factor for bulk reaction rates */ +double getucf(double order) { + if (order < 0.0) { + order = 0.0; + } + if (order == 1.0) { + return (1.0); + } else { + return (1. / pow(LperFT3, (order - 1.0))); + } +} + +void initqual(EN_Project *pr) +/* +**-------------------------------------------------------------- +** Input: none +** Output: none +** Purpose: re-initializes WQ solver system +**-------------------------------------------------------------- +*/ +{ + int i; + quality_t *qu = &pr->quality; + EN_Network *net = &pr->network; + hydraulics_t *hyd = &pr->hydraulics; + time_options_t *time = &pr->time_options; + + /* Initialize quality, tank volumes, & source mass flows */ + for (i = 1; i <= net->Nnodes; i++) { + qu->NodeQual[i] = net->Node[i].C0; + } + for (i = 1; i <= net->Ntanks; i++) { + net->Tank[i].C = net->Node[net->Tank[i].Node].C0; + } + for (i = 1; i <= net->Ntanks; i++) { + net->Tank[i].V = net->Tank[i].V0; + } + for (i = 1; i <= net->Nnodes; i++) { + if (net->Node[i].S != NULL) + net->Node[i].S->Smass = 0.0; + } + + qu->QTankVolumes = + calloc(net->Ntanks, + sizeof(double)); // keep track of previous step's tank volumes. + qu->QLinkFlow = calloc( + net->Nlinks, sizeof(double)); // keep track of previous step's link flows. + + /* Set WQ parameters */ + qu->Bucf = 1.0; + qu->Tucf = 1.0; + qu->Reactflag = 0; + if (qu->Qualflag != NONE) { + /* Initialize WQ at trace node (if applicable) */ + if (qu->Qualflag == TRACE) { + qu->NodeQual[qu->TraceNode] = 100.0; + } + + /* Compute Schmidt number */ + if (qu->Diffus > 0.0) + qu->Sc = hyd->Viscos / qu->Diffus; + else + qu->Sc = 0.0; + + /* Compute unit conversion factor for bulk react. coeff. */ + qu->Bucf = getucf(qu->BulkOrder); + qu->Tucf = getucf(qu->TankOrder); + + /* Check if modeling a reactive substance */ + qu->Reactflag = setReactflag(pr); + + /* Reset memory pool */ + qu->FreeSeg = NULL; + AllocSetPool(qu->SegPool); + AllocReset(); + } + + /* Initialize avg. reaction rates */ + qu->Wbulk = 0.0; + qu->Wwall = 0.0; + qu->Wtank = 0.0; + qu->Wsource = 0.0; + + /* Re-position hydraulics file */ + if (!hyd->OpenHflag) { + fseek(pr->out_files.HydFile, pr->out_files.HydOffset, SEEK_SET); + } + + /* Set elapsed times to zero */ + time->Htime = 0; + qu->Qtime = 0; + pr->time_options.Rtime = pr->time_options.Rstart; + pr->report.Nperiods = 0; + + initsegs(pr); +} + +int runqual(EN_Project *pr, long *t) +/* +**-------------------------------------------------------------- +** Input: none +** Output: t = pointer to current simulation time (sec) +** Returns: error code +** Purpose: retrieves hydraulics for next hydraulic time step +** (at time *t) and saves current results to file +**-------------------------------------------------------------- +*/ +{ + long hydtime; /* Hydraulic solution time */ + long hydstep; /* Hydraulic time step */ + int errcode = 0; + int i; + + EN_Network *net = &pr->network; + hydraulics_t *hyd = &pr->hydraulics; + quality_t *qu = &pr->quality; + time_options_t *time = &pr->time_options; + + /* Update reported simulation time */ + *t = qu->Qtime; + + /* Read hydraulic solution from hydraulics file */ + if (qu->Qtime == time->Htime) { + errcode = gethyd(pr, &hydtime, &hydstep); + if (!hyd->OpenHflag) { // test for sequential vs stepwise + // sequential + time->Htime = hydtime + hydstep; + } else { + // stepwise calculation - hydraulic results are already in memory + for (i = 1; i <= net->Ntanks; ++i) { + qu->QTankVolumes[i - 1] = net->Tank[i].V; + } + + for (i = 1; i <= net->Nlinks; ++i) { + if (hyd->LinkStatus[i] <= CLOSED) { + qu->QLinkFlow[i - 1] = hyd->LinkFlows[i]; + } + } + } + } else { + // stepwise calculation + for (i = 1; i <= net->Ntanks; ++i) { + qu->QTankVolumes[i - 1] = net->Tank[i].V; + } + + for (i = 1; i <= net->Nlinks; ++i) { + if (hyd->LinkStatus[i] <= CLOSED) { + qu->QLinkFlow[i - 1] = hyd->LinkFlows[i]; + } + } + } + + return (errcode); +} + +int nextqual(EN_Project *pr, long *tstep) +/* +**-------------------------------------------------------------- +** Input: none +** Output: tstep = pointer to time step (sec) +** Returns: error code +** Purpose: updates WQ conditions until next hydraulic +** solution occurs (after *tstep secs.) +**-------------------------------------------------------------- +*/ +{ + long hydstep; /* Hydraulic solution time step */ + int errcode = 0; + double *tankVolumes; + int i; + EN_Network *net; + hydraulics_t *hyd; + quality_t *qu; + time_options_t *time; + save_options_t *sav; + + /* Determine time step */ + *tstep = 0; + + net = &pr->network; + hyd = &pr->hydraulics; + qu = &pr->quality; + time = &pr->time_options; + sav = &pr->save_options; + + // hydstep = time->Htime - qu->Qtime; + + if (time->Htime <= time->Dur) { + hydstep = time->Htime - qu->Qtime; + } else { + hydstep = 0; + } + + // if we're operating in stepwise mode, capture the tank levels so we can + // restore them later. + if (hyd->OpenHflag) { + tankVolumes = calloc(net->Ntanks, sizeof(double)); + for (i = 1; i <= net->Ntanks; ++i) { + if (net->Tank[i].A != 0) { // skip reservoirs + tankVolumes[i - 1] = net->Tank[i].V; + } + } + + // restore the previous step's tank volumes + for (i = 1; i <= net->Ntanks; i++) { + if (net->Tank[i].A != 0) { // skip reservoirs again + int n = net->Tank[i].Node; + net->Tank[i].V = qu->QTankVolumes[i - 1]; + hyd->NodeHead[n] = tankgrade(pr, i, net->Tank[i].V); + } + } + + // restore the previous step's pipe link flows + for (i = 1; i <= net->Nlinks; i++) { + if (hyd->LinkStatus[i] <= CLOSED) { + hyd->LinkFlows[i] = 0.0; + } + } + } + + /* Perform water quality routing over this time step */ + if (qu->Qualflag != NONE && hydstep > 0) + transport(pr,hydstep); + + /* Update current time */ + if (qu->OutOfMemory) + errcode = 101; + if (!errcode) + *tstep = hydstep; + qu->Qtime += hydstep; + + /* Save final output if no more time steps */ + if (!errcode && sav->Saveflag && *tstep == 0) + errcode = savefinaloutput(pr); + + // restore tank levels to post-runH state, if needed. + if (hyd->OpenHflag) { + for (i = 1; i <= net->Ntanks; i++) { + if (net->Tank[i].A != 0) { // skip reservoirs again + int n = net->Tank[i].Node; + net->Tank[i].V = tankVolumes[i - 1]; + hyd->NodeHead[n] = tankgrade(pr, i, net->Tank[i].V); + } + } + + for (i = 1; i <= net->Nlinks; ++i) { + if (hyd->LinkStatus[i] <= CLOSED) { + hyd->LinkFlows[i] = qu->QLinkFlow[i - 1]; + } + } + + free(tankVolumes); + } + + return (errcode); +} + +int stepqual(EN_Project *pr, long *tleft) +/* +**-------------------------------------------------------------- +** Input: none +** Output: tleft = pointer to time left in simulation +** Returns: error code +** Purpose: updates WQ conditions over a single WQ time step +**-------------------------------------------------------------- +*/ +{ + quality_t *qu = &pr->quality; + time_options_t *time = &pr->time_options; + save_options_t *sav = &pr->save_options; + + long dt, hstep, t, tstep; + int errcode = 0; + tstep = qu->Qstep; + do { + dt = tstep; + hstep = time->Htime - qu->Qtime; + if (hstep < dt) { + dt = hstep; + if (qu->Qualflag != NONE) { + transport(pr,dt); + } + qu->Qtime += dt; + errcode = runqual(pr,&t); + qu->Qtime = t; + } else { + if (qu->Qualflag != NONE) + transport(pr,dt); + qu->Qtime += dt; + } + tstep -= dt; + if (qu->OutOfMemory) { + errcode = 101; + } + } while (!errcode && tstep > 0); + *tleft = time->Dur - qu->Qtime; + + if (!errcode && sav->Saveflag && *tleft == 0) { + errcode = savefinaloutput(pr); + } + return (errcode); +} + +int closequal(EN_Project *pr) +/* +**-------------------------------------------------------------- +** Input: none +** Output: returns error code +** Purpose: closes WQ solver system +**-------------------------------------------------------------- +*/ +{ + quality_t *qu = &pr->quality; + int errcode = 0; + + /* Free memory pool */ + if (qu->SegPool) + { + AllocSetPool(qu->SegPool); + AllocFreePool(); + } + + free(qu->FirstSeg); + free(qu->LastSeg); + free(qu->FlowDir); + free(qu->VolIn); + free(qu->MassIn); + free(qu->PipeRateCoeff); + free(qu->TempQual); + free(qu->QTankVolumes); + free(qu->QLinkFlow); + return (errcode); +} + +int gethyd(EN_Project *pr, long *hydtime, long *hydstep) +/* +**----------------------------------------------------------- +** Input: none +** Output: hydtime = pointer to hydraulic solution time +** hydstep = pointer to hydraulic time step +** Returns: error code +** Purpose: retrieves hydraulic solution and hydraulic +** time step for next hydraulic event +** +** NOTE: when this function is called, WQ results have +** already been updated to the point in time when +** the next hydraulic event occurs. +**----------------------------------------------------------- +*/ +{ + int errcode = 0; + + hydraulics_t *hyd = &pr->hydraulics; + report_options_t *rep = &pr->report; + quality_t *qu = &pr->quality; + time_options_t *time = &pr->time_options; + save_options_t *sav = &pr->save_options; + + // if hydraulics are not open, then we're operating in sequential mode. + // else hydraulics are open, so use the hydraulic results in memory rather + // than reading from the temp file. + if (!hyd->OpenHflag) { + /* Read hydraulic results from file */ + if (!readhyd(pr,hydtime)) { + return (307); + } + if (!readhydstep(pr->out_files.HydFile, hydstep)) { + return (307); + } + time->Htime = *hydtime; + } + + /* Save current results to output file */ + if (time->Htime >= time->Rtime) { + if (sav->Saveflag) { + errcode = saveoutput(pr); + rep->Nperiods++; + } + time->Rtime += time->Rstep; + } + + /* If simulating WQ: */ + if (qu->Qualflag != NONE && qu->Qtime < time->Dur) { + + /* Compute reaction rate coeffs. */ + if (qu->Reactflag && qu->Qualflag != AGE) { + ratecoeffs(pr); + } + + /* Initialize pipe segments (at time 0) or */ + /* else re-orient segments if flow reverses.*/ + // if (qu->Qtime == 0) + // initsegs(); + // else + // if hydraulics are open, or if we're in sequential mode (where qtime can + // increase) + if (hyd->OpenHflag || qu->Qtime != 0) { + reorientsegs(pr); + } + } + return (errcode); +} + +char setReactflag(EN_Project *pr) +/* +**----------------------------------------------------------- +** Input: none +** Output: returns 1 for reactive WQ constituent, 0 otherwise +** Purpose: checks if reactive chemical being simulated +**----------------------------------------------------------- +*/ +{ + EN_Network *net = &pr->network; + quality_t *qu = &pr->quality; + + int i; + if (qu->Qualflag == TRACE) { + return (0); + } else if (qu->Qualflag == AGE) { + return (1); + } else { + for (i = 1; i <= net->Nlinks; i++) { + if (net->Link[i].Type <= EN_PIPE) { + if (net->Link[i].Kb != 0.0 || net->Link[i].Kw != 0.0) { + return (1); + } + } + } + for (i = 1; i <= net->Ntanks; i++) { + if (net->Tank[i].Kb != 0.0) { + return (1); + } + } + } + return (0); +} + +void transport(EN_Project *pr, long tstep) +/* +**-------------------------------------------------------------- +** Input: tstep = length of current time step +** Output: none +** Purpose: transports constituent mass through pipe network +** under a period of constant hydraulic conditions. +**-------------------------------------------------------------- +*/ +{ + long qtime, dt; + quality_t *qu = &pr->quality; + + /* 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 */ + qtime += dt; /* Update elapsed time */ + if (qu->Reactflag) { + updatesegs(pr, dt); /* Update quality in inner link segs */ + } + accumulate(pr, dt); /* Accumulate flow at nodes */ + updatenodes(pr, dt); /* Update nodal quality */ + sourceinput(pr, dt); /* Compute inputs from sources */ + release(pr, dt); /* Release new nodal flows */ + } + updatesourcenodes(pr, tstep); /* Update quality at source nodes */ +} + +void initsegs(EN_Project *pr) +/* +**-------------------------------------------------------------- +** Input: none +** Output: none +** Purpose: initializes water quality segments +**-------------------------------------------------------------- +*/ +{ + int j, k; + double c, v; + EN_Network *net = &pr->network; + hydraulics_t *hyd = &pr->hydraulics; + quality_t *qu = &pr->quality; + + /* Examine each link */ + for (k = 1; k <= net->Nlinks; k++) { + + /* Establish flow direction */ + qu->FlowDir[k] = POSITIVE; + if (hyd->LinkFlows[k] < 0.) { + qu->FlowDir[k] = NEGATIVE; + } + + /* Set segs to zero */ + qu->LastSeg[k] = NULL; + qu->FirstSeg[k] = NULL; + + /* Find quality of downstream node */ + j = DOWN_NODE(k); + if (j <= net->Njuncs) { + c = qu->NodeQual[j]; + } else { + c = net->Tank[j - net->Njuncs].C; + } + + /* Fill link with single segment with this quality */ + addseg(pr, k, LINKVOL(k), c); + } + + /* Initialize segments in tanks that use them */ + for (j = 1; j <= net->Ntanks; j++) { + + /* Skip reservoirs & complete mix tanks */ + if (net->Tank[j].A == 0.0 || net->Tank[j].MixModel == MIX1) + continue; + + /* Tank segment pointers are stored after those for links */ + k = net->Nlinks + j; + c = net->Tank[j].C; + qu->LastSeg[k] = NULL; + qu->FirstSeg[k] = NULL; + + /* Add 2 segments for 2-compartment model */ + if (net->Tank[j].MixModel == MIX2) { + v = MAX(0, net->Tank[j].V - net->Tank[j].V1max); + addseg(pr, k, v, c); + v = net->Tank[j].V - v; + addseg(pr, k, v, c); + } + + /* Add one segment for FIFO & LIFO models */ + else { + v = net->Tank[j].V; + addseg(pr, k, v, c); + } + } +} + +void reorientsegs(EN_Project *pr) +/* +**-------------------------------------------------------------- +** Input: none +** Output: none +** Purpose: re-orients segments (if flow reverses) +**-------------------------------------------------------------- +*/ +{ + Pseg seg, nseg, pseg; + int k; + FlowDirection newdir; + EN_Network *net = &pr->network; + hydraulics_t *hyd = &pr->hydraulics; + quality_t *qu = &pr->quality; + + /* Examine each link */ + for (k = 1; k <= net->Nlinks; k++) { + + /* Find new flow direction */ + newdir = POSITIVE; + if (hyd->LinkFlows[k] == 0.0) { + newdir = qu->FlowDir[k]; + } else if (hyd->LinkFlows[k] < 0.0) { + newdir = NEGATIVE; + } + + /* If direction changes, then reverse order of segments */ + /* (first to last) and save new direction */ + if (newdir != qu->FlowDir[k]) { + seg = qu->FirstSeg[k]; + qu->FirstSeg[k] = qu->LastSeg[k]; + qu->LastSeg[k] = seg; + pseg = NULL; + while (seg != NULL) { + nseg = seg->prev; + seg->prev = pseg; + pseg = seg; + seg = nseg; + } + qu->FlowDir[k] = newdir; + } + } +} + +void updatesegs(EN_Project *pr, long dt) +/* +**------------------------------------------------------------- +** Input: t = time from last WQ segment update +** Output: none +** Purpose: reacts material in pipe segments up to time t +**------------------------------------------------------------- +*/ +{ + int k; + Pseg seg; + double cseg, rsum, vsum; + EN_Network *net = &pr->network; + quality_t *qu = &pr->quality; + + /* Examine each link in network */ + for (k = 1; k <= net->Nlinks; k++) { + + /* Skip zero-length links (pumps & valves) */ + rsum = 0.0; + vsum = 0.0; + if (net->Link[k].Len == 0.0) { + continue; + } + + /* Examine each segment of the link */ + seg = qu->FirstSeg[k]; + while (seg != NULL) { + + /* React segment over time dt */ + cseg = seg->c; + seg->c = pipereact(pr, k, seg->c, seg->v, dt); + + /* Accumulate volume-weighted reaction rate */ + if (qu->Qualflag == CHEM) { + rsum += ABS((seg->c - cseg)) * seg->v; + vsum += seg->v; + } + seg = seg->prev; + } + + /* Normalize volume-weighted reaction rate */ + if (vsum > 0.0) { + qu->PipeRateCoeff[k] = rsum / vsum / dt * SECperDAY; + } else + qu->PipeRateCoeff[k] = 0.0; + } +} + +void removesegs(EN_Project *pr, int k) +/* +**------------------------------------------------------------- +** Input: k = link index +** Output: none +** Purpose: removes all segments in link k +**------------------------------------------------------------- +*/ +{ + Pseg seg; + quality_t *qu = &pr->quality; + + seg = qu->FirstSeg[k]; + while (seg != NULL) { + qu->FirstSeg[k] = seg->prev; + seg->prev = qu->FreeSeg; + qu->FreeSeg = seg; + seg = qu->FirstSeg[k]; + } + qu->LastSeg[k] = NULL; +} + +void addseg(EN_Project *pr, int k, double v, double c) +/* +**------------------------------------------------------------- +** Input: k = link segment +** v = segment volume +** c = segment quality +** Output: none +** Purpose: adds a segment to start of link k (i.e., upstream +** of current last segment). +**------------------------------------------------------------- +*/ +{ + Pseg seg; + quality_t *qu = &pr->quality; + + if (qu->FreeSeg != NULL) { + seg = qu->FreeSeg; + qu->FreeSeg = seg->prev; + } else { + seg = (struct Sseg *)Alloc(sizeof(struct Sseg)); + if (seg == NULL) { + qu->OutOfMemory = TRUE; + return; + } + } + seg->v = v; + seg->c = c; + seg->prev = NULL; + if (qu->FirstSeg[k] == NULL) { + qu->FirstSeg[k] = seg; + } + if (qu->LastSeg[k] != NULL) { + qu->LastSeg[k]->prev = seg; + } + qu->LastSeg[k] = seg; +} + +void accumulate(EN_Project *pr, long dt) +/* +**------------------------------------------------------------- +** Input: dt = current WQ time step +** Output: none +** Purpose: accumulates mass flow at nodes and updates nodal +** quality +**------------------------------------------------------------- +*/ +{ + int i, j, k; + double cseg, v, vseg; + Pseg seg; + + EN_Network *net = &pr->network; + hydraulics_t *hyd = &pr->hydraulics; + quality_t *qu = &pr->quality; + + /* Re-set memory used to accumulate mass & volume */ + memset(qu->VolIn, 0, (net->Nnodes + 1) * sizeof(double)); + memset(qu->MassIn, 0, (net->Nnodes + 1) * sizeof(double)); + memset(qu->TempQual, 0, (net->Nnodes + 1) * sizeof(double)); + + /* Compute average conc. of segments adjacent to each node */ + /* (For use if there is no transport through the node) */ + for (k = 1; k <= net->Nlinks; k++) { + j = DOWN_NODE(k); /* Downstream node */ + if (qu->FirstSeg[k] != NULL) /* Accumulate concentrations */ + { + qu->MassIn[j] += qu->FirstSeg[k]->c; + qu->VolIn[j]++; + } + j = UP_NODE(k); /* Upstream node */ + if (qu->LastSeg[k] != NULL) /* Accumulate concentrations */ + { + qu->MassIn[j] += qu->LastSeg[k]->c; + qu->VolIn[j]++; + } + } + + for (k = 1; k <= net->Nnodes; k++) { + if (qu->VolIn[k] > 0.0) { + qu->TempQual[k] = qu->MassIn[k] / qu->VolIn[k]; + } + } + + /* Move mass from first segment of each pipe into downstream node */ + memset(qu->VolIn, 0, (net->Nnodes + 1) * sizeof(double)); + memset(qu->MassIn, 0, (net->Nnodes + 1) * sizeof(double)); + for (k = 1; k <= net->Nlinks; k++) { + i = UP_NODE(k); /* Upstream node */ + j = DOWN_NODE(k); /* Downstream node */ + v = ABS(hyd->LinkFlows[k]) * dt; /* Flow volume */ + + while (v > 0.0) + { + /* Identify leading segment in pipe */ + seg = qu->FirstSeg[k]; + if (seg == NULL) + break; + + /* Volume transported from this segment is */ + /* minimum of flow volume & segment volume */ + /* (unless leading segment is also last segment) */ + vseg = seg->v; + vseg = MIN(vseg, v); + if (seg == qu->LastSeg[k]) + vseg = v; + + /* Update volume & mass entering downstream node */ + cseg = seg->c; + qu->VolIn[j] += vseg; + qu->MassIn[j] += vseg * cseg; + + /* Reduce flow volume by amount transported */ + v -= vseg; + + /* If all of segment's volume was transferred, then */ + /* replace leading segment with the one behind it */ + /* (Note that the current seg is recycled for later use.) */ + if (v >= 0.0 && vseg >= seg->v) { + qu->FirstSeg[k] = seg->prev; + if (qu->FirstSeg[k] == NULL) + qu->LastSeg[k] = NULL; + seg->prev = qu->FreeSeg; + qu->FreeSeg = seg; + } + + /* Otherwise reduce segment's volume */ + else { + seg->v -= vseg; + } + } /* End while */ + } /* Next link */ +} + +void updatenodes(EN_Project *pr, long dt) +/* +**--------------------------------------------------------------------------- +** Input: dt = current WQ time step +** Output: none +** Purpose: updates concentration at all nodes to mixture of accumulated +** inflow from connecting pipes. +** +** Note: Does not account for source flow effects. TempQual[i] contains +** average concen. of segments adjacent to node i, used in case +** there was no inflow into i. +**--------------------------------------------------------------------------- +*/ +{ + int i; + EN_Network *net = &pr->network; + hydraulics_t *hyd = &pr->hydraulics; + quality_t *qu = &pr->quality; + + /* Update junction quality */ + for (i = 1; i <= net->Njuncs; i++) { + if (hyd->NodeDemand[i] < 0.0) { + qu->VolIn[i] -= hyd->NodeDemand[i] * dt; + } + if (qu->VolIn[i] > 0.0) { + qu->NodeQual[i] = qu->MassIn[i] / qu->VolIn[i]; + } else { + qu->NodeQual[i] = qu->TempQual[i]; + } + } + + /* Update tank quality */ + updatetanks(pr, dt); + + /* For flow tracing, set source node concen. to 100. */ + if (qu->Qualflag == TRACE) + qu->NodeQual[qu->TraceNode] = 100.0; +} + +void sourceinput(EN_Project *pr, long dt) +/* +**--------------------------------------------------------------------- +** Input: dt = current WQ time step +** Output: none +** Purpose: computes contribution (if any) of mass additions from WQ +** sources at each node. +**--------------------------------------------------------------------- +*/ +{ + int j, n; + double massadded = 0.0, s, volout; + double qout, qcutoff; + Psource source; + + EN_Network *net = &pr->network; + hydraulics_t *hyd = &pr->hydraulics; + quality_t *qu = &pr->quality; + time_options_t *time = &pr->time_options; + + /* Establish a flow cutoff which indicates no outflow from a node */ + qcutoff = 10.0 * TINY; + + /* Zero-out the work array TempQual */ + memset(qu->TempQual, 0, (net->Nnodes + 1) * sizeof(double)); + if (qu->Qualflag != CHEM) + return; + + /* Consider each node */ + for (n = 1; n <= net->Nnodes; n++) { + double thisDemand = hyd->NodeDemand[n]; + /* Skip node if no WQ source */ + source = net->Node[n].S; + if (source == NULL) + continue; + if (source->C0 == 0.0) + continue; + + /* Find total flow volume leaving node */ + if (n <= net->Njuncs) { + volout = qu->VolIn[n]; /* Junctions */ + } else { + volout = qu->VolIn[n] - (thisDemand * dt); /* Tanks */ + } + qout = volout / (double)dt; + + /* Evaluate source input only if node outflow > cutoff flow */ + if (qout > qcutoff) { + + /* Mass added depends on type of source */ + s = sourcequal(pr,source); + switch (source->Type) { + /* Concen. Source: */ + /* Mass added = source concen. * -(demand) */ + case CONCEN: + + /* Only add source mass if demand is negative */ + if (thisDemand < 0.0) { + massadded = -s * thisDemand * dt; + + /* If node is a tank then set concen. to 0. */ + /* (It will be re-set to true value in updatesourcenodes()) */ + if (n > net->Njuncs) + qu->NodeQual[n] = 0.0; + } else + massadded = 0.0; + break; + + /* Mass Inflow Booster Source: */ + case MASS: + massadded = s * dt; + break; + + /* Setpoint Booster Source: */ + /* Mass added is difference between source */ + /* & node concen. times outflow volume */ + case SETPOINT: + if (s > qu->NodeQual[n]) { + massadded = (s - qu->NodeQual[n]) * volout; + } else { + massadded = 0.0; + } + break; + + /* Flow-Paced Booster Source: */ + /* Mass added = source concen. times outflow volume */ + case FLOWPACED: + massadded = s * volout; + break; + } + + /* Source concen. contribution = (mass added / outflow volume) */ + qu->TempQual[n] = massadded / volout; + + /* Update total mass added for time period & simulation */ + source->Smass += massadded; + if (time->Htime >= time->Rstart) { + qu->Wsource += massadded; + } + } + } + + /* Add mass inflows from reservoirs to Wsource*/ + if (time->Htime >= time->Rstart) { + for (j = 1; j <= net->Ntanks; j++) { + if (net->Tank[j].A == 0.0) { + n = net->Njuncs + j; + volout = qu->VolIn[n] - hyd->NodeDemand[n] * dt; + if (volout > 0.0) + qu->Wsource += volout * qu->NodeQual[n]; + } + } + } +} + +void release(EN_Project *pr, long dt) +/* +**--------------------------------------------------------- +** Input: dt = current WQ time step +** Output: none +** Purpose: creates new segments in outflow links from nodes. +**--------------------------------------------------------- +*/ +{ + EN_Network *net = &pr->network; + hydraulics_t *hyd = &pr->hydraulics; + quality_t *qu = &pr->quality; + + int k, n; + double c, q, v; + Pseg seg; + + /* Examine each link */ + for (k = 1; k <= net->Nlinks; k++) { + + /* Ignore links with no flow */ + if (hyd->LinkFlows[k] == 0.0) + continue; + + /* Find flow volume released to link from upstream node */ + /* (NOTE: Flow volume is allowed to be > link volume.) */ + n = UP_NODE(k); + q = ABS(hyd->LinkFlows[k]); + v = q * dt; + + /* Include source contribution in quality released from node. */ + c = qu->NodeQual[n] + qu->TempQual[n]; + + /* If link has a last seg, check if its quality */ + /* differs from that of the flow released from node.*/ + if ((seg = qu->LastSeg[k]) != NULL) { + /* Quality of seg close to that of node */ + if (ABS(seg->c - c) < qu->Ctol) { + seg->c = (seg->c * seg->v + c * v) / (seg->v + v); + seg->v += v; + } + + /* Otherwise add a new seg to end of link */ + else + addseg(pr, k, v, c); + } + + /* If link has no segs then add a new one. */ + else + addseg(pr, k, LINKVOL(k), c); + } +} + +void updatesourcenodes(EN_Project *pr, long dt) +/* +**--------------------------------------------------- +** Input: dt = current WQ time step +** Output: none +** Purpose: updates quality at source nodes. +** (TempQual[n] = concen. added by source at node n) +**--------------------------------------------------- +*/ +{ + EN_Network *net = &pr->network; + quality_t *qu = &pr->quality; + + int i, n; + Psource source; + + if (qu->Qualflag != CHEM) + return; + + /* Examine each WQ source node */ + for (n = 1; n <= net->Nnodes; n++) { + source = net->Node[n].S; + if (source == NULL) + continue; + + /* Add source to current node concen. */ + qu->NodeQual[n] += qu->TempQual[n]; + + /* For tanks, node concen. = internal concen. */ + if (n > net->Njuncs) { + i = n - net->Njuncs; + if (net->Tank[i].A > 0.0) + qu->NodeQual[n] = net->Tank[i].C; + } + + /* Normalize mass added at source to time step */ + source->Smass /= (double)dt; + } +} + +void updatetanks(EN_Project *pr, long dt) +/* +**--------------------------------------------------- +** Input: dt = current WQ time step +** Output: none +** Purpose: updates tank volumes & concentrations +**--------------------------------------------------- +*/ +{ + EN_Network *net = &pr->network; + quality_t *qu = &pr->quality; + + int i, n; + + /* Examine each reservoir & tank */ + for (i = 1; i <= net->Ntanks; i++) { + n = net->Tank[i].Node; + /* Use initial quality for reservoirs */ + if (net->Tank[i].A == 0.0) { + qu->NodeQual[n] = net->Node[n].C0; + } + /* Update tank WQ based on mixing model */ + else { + switch (net->Tank[i].MixModel) { + case MIX2: + tankmix2(pr, i, dt); + break; + case FIFO: + tankmix3(pr, i, dt); + break; + case LIFO: + tankmix4(pr, i, dt); + break; + default: + tankmix1(pr, i, dt); + break; + } + } + } +} + +//// New version of tankmix1 //// +void tankmix1(EN_Project *pr, int i, long dt) +/* +**--------------------------------------------- +** Input: i = tank index +** dt = current WQ time step +** Output: none +** Purpose: complete mix tank model +**--------------------------------------------- +*/ +{ + EN_Network *net = &pr->network; + hydraulics_t *hyd = &pr->hydraulics; + quality_t *qu = &pr->quality; + + int n; + double cin; + double c, cmax, vold, vin; + + Stank *tank = &net->Tank[i]; + + /* React contents of tank */ + c = tankreact(pr, tank->C, tank->V, tank->Kb, dt); + + /* Determine tank & volumes */ + vold = tank->V; + n = tank->Node; + tank->V += hyd->NodeDemand[n] * dt; + vin = qu->VolIn[n]; + + /* Compute inflow concen. */ + if (vin > 0.0) + cin = qu->MassIn[n] / vin; + else + cin = 0.0; + cmax = MAX(c, cin); + + /* Mix inflow with tank contents */ + if (vin > 0.0) + c = (c * vold + cin * vin) / (vold + vin); + c = MIN(c, cmax); + c = MAX(c, 0.0); + tank->C = c; + qu->NodeQual[n] = tank->C; +} + +/*** Updated 10/25/00 ***/ +//// New version of tankmix2 //// +void tankmix2(EN_Project *pr, int i, long dt) +/* +**------------------------------------------------ +** Input: i = tank index +** dt = current WQ time step +** Output: none +** Purpose: 2-compartment tank model +** (seg1 = mixing zone, +** seg2 = ambient zone) +**------------------------------------------------ +*/ +{ + EN_Network *net = &pr->network; + hydraulics_t *hyd = &pr->hydraulics; + quality_t *qu = &pr->quality; + + int k, n; + double cin, /* Inflow quality */ + vin, /* Inflow volume */ + vt, /* Transferred volume */ + vnet, /* Net volume change */ + v1max; /* Full mixing zone volume */ + Pseg seg1, seg2; /* Compartment segments */ + + Stank *tank = &pr->network.Tank[i]; + + /* Identify segments for each compartment */ + k = net->Nlinks + i; + seg1 = qu->LastSeg[k]; + seg2 = qu->FirstSeg[k]; + if (seg1 == NULL || seg2 == NULL) + return; + + /* React contents of each compartment */ + seg1->c = tankreact(pr, seg1->c, seg1->v, tank->Kb, dt); + seg2->c = tankreact(pr, seg2->c, seg2->v, tank->Kb, dt); + + /* Find inflows & outflows */ + n = tank->Node; + vnet = hyd->NodeDemand[n] * dt; + vin = qu->VolIn[n]; + if (vin > 0.0) + cin = qu->MassIn[n] / vin; + else + cin = 0.0; + v1max = tank->V1max; + + /* Tank is filling */ + vt = 0.0; + if (vnet > 0.0) { + vt = MAX(0.0, (seg1->v + vnet - v1max)); + if (vin > 0.0) { + seg1->c = ((seg1->c) * (seg1->v) + cin * vin) / (seg1->v + vin); + } + if (vt > 0.0) { + seg2->c = ((seg2->c) * (seg2->v) + (seg1->c) * vt) / (seg2->v + vt); + } + } + + /* Tank is emptying */ + if (vnet < 0.0) { + if (seg2->v > 0.0) { + vt = MIN(seg2->v, (-vnet)); + } + if (vin + vt > 0.0) { + seg1->c = ((seg1->c) * (seg1->v) + cin * vin + (seg2->c) * vt) / + (seg1->v + vin + vt); + } + } + + /* Update segment volumes */ + if (vt > 0.0) { + seg1->v = v1max; + if (vnet > 0.0) + seg2->v += vt; + else + seg2->v = MAX(0.0, ((seg2->v) - vt)); + } else { + seg1->v += vnet; + seg1->v = MIN(seg1->v, v1max); + seg1->v = MAX(0.0, seg1->v); + seg2->v = 0.0; + } + tank->V += vnet; + tank->V = MAX(0.0, tank->V); + + /* Use quality of mixed compartment (seg1) to */ + /* represent quality of tank since this is where */ + /* outflow begins to flow from */ + tank->C = seg1->c; + qu->NodeQual[n] = tank->C; +} + +void tankmix3(EN_Project *pr, int i, long dt) +/* +**---------------------------------------------------------- +** Input: i = tank index +** dt = current WQ time step +** Output: none +** Purpose: First-In-First-Out (FIFO) tank model +**---------------------------------------------------------- +*/ +{ + EN_Network *net = &pr->network; + hydraulics_t *hyd = &pr->hydraulics; + quality_t *qu = &pr->quality; + + int k, n; + double vin, vnet, vout, vseg; + double cin, vsum, csum; + Pseg seg; + Stank *tank = &pr->network.Tank[i]; + k = net->Nlinks + i; + if (qu->LastSeg[k] == NULL || qu->FirstSeg[k] == NULL) + return; + + /* React contents of each compartment */ + if (qu->Reactflag) { + seg = qu->FirstSeg[k]; + while (seg != NULL) { + seg->c = tankreact(pr, seg->c, seg->v, tank->Kb, dt); + seg = seg->prev; + } + } + + /* Find inflows & outflows */ + n = tank->Node; + vnet = hyd->NodeDemand[n] * dt; + vin = qu->VolIn[n]; + vout = vin - vnet; + if (vin > 0.0) + cin = qu->MassIn[n] / qu->VolIn[n]; + else + cin = 0.0; + tank->V += vnet; + tank->V = MAX(0.0, tank->V); + + /* Withdraw flow from first segment */ + vsum = 0.0; + csum = 0.0; + while (vout > 0.0) { + seg = qu->FirstSeg[k]; + if (seg == NULL) + break; + vseg = seg->v; /* Flow volume from leading seg */ + vseg = MIN(vseg, vout); + if (seg == qu->LastSeg[k]) + vseg = vout; + vsum += vseg; + csum += (seg->c) * vseg; + vout -= vseg; /* Remaining flow volume */ + if (vout >= 0.0 && vseg >= seg->v) /* Seg used up */ + { + if (seg->prev) + { + qu->FirstSeg[k] = seg->prev; + seg->prev = qu->FreeSeg; + qu->FreeSeg = seg; + } + } else /* Remaining volume in segment */ + { + seg->v -= vseg; + } + } + + /* Use quality withdrawn from 1st segment */ + /* to represent overall quality of tank */ + if (vsum > 0.0) + tank->C = csum / vsum; + else + tank->C = qu->FirstSeg[k]->c; + qu->NodeQual[n] = tank->C; + + /* Add new last segment for new flow entering tank */ + if (vin > 0.0) { + if ((seg = qu->LastSeg[k]) != NULL) { + /* Quality is the same, so just add flow volume to last seg */ + if (ABS(seg->c - cin) < qu->Ctol) + seg->v += vin; + + /* Otherwise add a new seg to tank */ + else + addseg(pr, k, vin, cin); + } + + /* If no segs left then add a new one. */ + else + addseg(pr, k, vin, cin); + } +} + +void tankmix4(EN_Project *pr, int i, long dt) +/* +**---------------------------------------------------------- +** Input: i = tank index +** dt = current WQ time step +** Output: none +** Purpose: Last In-First Out (LIFO) tank model +**---------------------------------------------------------- +*/ +{ + EN_Network *net = &pr->network; + hydraulics_t *hyd = &pr->hydraulics; + quality_t *qu = &pr->quality; + + int k, n; + double vin, vnet, cin, vsum, csum, vseg; + Pseg seg, tmpseg; + Stank *tank = &pr->network.Tank[i]; + k = net->Nlinks + i; + if (qu->LastSeg[k] == NULL || qu->FirstSeg[k] == NULL) + return; + + /* React contents of each compartment */ + if (qu->Reactflag) { + seg = qu->LastSeg[k]; + while (seg != NULL) { + seg->c = tankreact(pr, seg->c, seg->v, tank->Kb, dt); + seg = seg->prev; + } + } + + /* Find inflows & outflows */ + n = tank->Node; + vnet = hyd->NodeDemand[n] * dt; + vin = qu->VolIn[n]; + if (vin > 0.0) + cin = qu->MassIn[n] / qu->VolIn[n]; + else + cin = 0.0; + tank->V += vnet; + tank->V = MAX(0.0, tank->V); + tank->C = qu->LastSeg[k]->c; + + /* If tank filling, then create new last seg */ + if (vnet > 0.0) { + if ((seg = qu->LastSeg[k]) != NULL) { + /* Quality is the same, so just add flow volume to last seg */ + if (ABS(seg->c - cin) < qu->Ctol) + seg->v += vnet; + + /* Otherwise add a new last seg to tank */ + /* which points to old last seg */ + else { + tmpseg = seg; + qu->LastSeg[k] = NULL; + addseg(pr, k, vnet, cin); + qu->LastSeg[k]->prev = tmpseg; + } + } + + /* If no segs left then add a new one. */ + else + addseg(pr, k, vnet, cin); + + /* Update reported tank quality */ + tank->C = qu->LastSeg[k]->c; + } + + /* If net emptying then remove last segments until vnet consumed */ + else if (vnet < 0.0) { + vsum = 0.0; + csum = 0.0; + vnet = -vnet; + while (vnet > 0.0) { + seg = qu->LastSeg[k]; + if (seg == NULL) + break; + vseg = seg->v; + vseg = MIN(vseg, vnet); + if (seg == qu->FirstSeg[k]) + vseg = vnet; + vsum += vseg; + csum += (seg->c) * vseg; + vnet -= vseg; + if (vnet >= 0.0 && vseg >= seg->v) /* Seg used up */ + { + if (seg->prev) + { + qu->LastSeg[k] = seg->prev; + seg->prev = qu->FreeSeg; + qu->FreeSeg = seg; + } + } else /* Remaining volume in segment */ + { + seg->v -= vseg; + } + } + /* Reported tank quality is mixture of flow released and any inflow */ + tank->C = (csum + qu->MassIn[n]) / (vsum + vin); + } + qu->NodeQual[n] = tank->C; +} + +double sourcequal(EN_Project *pr, Psource source) +/* +**-------------------------------------------------------------- +** Input: j = source index +** Output: returns source WQ value +** Purpose: determines source concentration in current time period +**-------------------------------------------------------------- +*/ +{ + int i; + long k; + double c; + EN_Network *net = &pr->network; + quality_t *qu = &pr->quality; + time_options_t *time = &pr->time_options; + + /* Get source concentration (or mass flow) in original units */ + c = source->C0; + + /* Convert mass flow rate from min. to sec. */ + /* and convert concen. from liters to cubic feet */ + if (source->Type == MASS) + c /= 60.0; + else + c /= pr->Ucf[QUALITY]; + + /* Apply time pattern if assigned */ + i = source->Pat; + if (i == 0) + return (c); + k = ((qu->Qtime + time->Pstart) / time->Pstep) % (long)net->Pattern[i].Length; + return (c * net->Pattern[i].F[k]); +} + +double avgqual(EN_Project *pr, int k) +/* +**-------------------------------------------------------------- +** Input: k = link index +** Output: returns WQ value +** Purpose: computes average quality in link k +**-------------------------------------------------------------- +*/ +{ + double vsum = 0.0, msum = 0.0; + Pseg seg; + EN_Network *net = &pr->network; + quality_t *qu = &pr->quality; + + if (qu->Qualflag == NONE) + return (0.); + seg = qu->FirstSeg[k]; + while (seg != NULL) { + vsum += seg->v; + msum += (seg->c) * (seg->v); + seg = seg->prev; + } + if (vsum > 0.0 && qu->Qtime > 0) + return (msum / vsum); + else + return ((qu->NodeQual[net->Link[k].N1] + qu->NodeQual[net->Link[k].N2]) / + 2.); +} + +void ratecoeffs(EN_Project *pr) +/* +**-------------------------------------------------------------- +** Input: none +** Output: none +** Purpose: determines wall reaction coeff. for each pipe +**-------------------------------------------------------------- +*/ +{ + int k; + double kw; + EN_Network *net = &pr->network; + quality_t *qu = &pr->quality; + + for (k = 1; k <= net->Nlinks; k++) { + kw = net->Link[k].Kw; + if (kw != 0.0) + kw = piperate(pr, k); + net->Link[k].Rc = kw; + qu->PipeRateCoeff[k] = 0.0; + } +} /* End of ratecoeffs */ + +double piperate(EN_Project *pr, int k) +/* +**-------------------------------------------------------------- +** Input: k = link index +** Output: returns reaction rate coeff. for 1st-order wall +** reactions or mass transfer rate coeff. for 0-order +** reactions +** Purpose: finds wall reaction rate coeffs. +**-------------------------------------------------------------- +*/ +{ + double a, d, u, kf, kw, y, Re, Sh; + + EN_Network *net = &pr->network; + hydraulics_t *hyd = &pr->hydraulics; + quality_t *qu = &pr->quality; + + d = net->Link[k].Diam; /* Pipe diameter, ft */ + + /* Ignore mass transfer if Schmidt No. is 0 */ + if (qu->Sc == 0.0) { + if (qu->WallOrder == 0.0) + return (BIG); + else + return (net->Link[k].Kw * (4.0 / d) / pr->Ucf[ELEV]); + } + + /* Compute Reynolds No. */ + a = PI * d * d / 4.0; + u = ABS(hyd->LinkFlows[k]) / a; + Re = u * d / hyd->Viscos; + + /* Compute Sherwood No. for stagnant flow */ + /* (mass transfer coeff. = Diffus./radius) */ + if (Re < 1.0) + Sh = 2.0; + + /* Compute Sherwood No. for turbulent flow */ + /* using the Notter-Sleicher formula. */ + else if (Re >= 2300.0) + Sh = 0.0149 * pow(Re, 0.88) * pow(qu->Sc, 0.333); + + /* Compute Sherwood No. for laminar flow */ + /* using Graetz solution formula. */ + else { + y = d / net->Link[k].Len * Re * qu->Sc; + Sh = 3.65 + 0.0668 * y / (1.0 + 0.04 * pow(y, 0.667)); + } + + /* Compute mass transfer coeff. (in ft/sec) */ + kf = Sh * qu->Diffus / d; + + /* For zero-order reaction, return mass transfer coeff. */ + if (qu->WallOrder == 0.0) + return (kf); + + /* For first-order reaction, return apparent wall coeff. */ + kw = net->Link[k].Kw / pr->Ucf[ELEV]; /* Wall coeff, ft/sec */ + kw = (4.0 / d) * kw * kf / (kf + ABS(kw)); /* Wall coeff, 1/sec */ + return (kw); +} /* End of piperate */ + +double pipereact(EN_Project *pr, int k, double c, double v, long dt) +/* +**------------------------------------------------------------ +** Input: k = link index +** c = current WQ in segment +** v = segment volume +** dt = time step +** Output: returns new WQ value +** Purpose: computes new quality in a pipe segment after +** reaction occurs +**------------------------------------------------------------ +*/ +{ + double cnew, dc, dcbulk, dcwall, rbulk, rwall; + EN_Network *net = &pr->network; + quality_t *qu = &pr->quality; + time_options_t *time = &pr->time_options; + + /* For water age (hrs), update concentration by timestep */ + if (qu->Qualflag == AGE) + return (c + (double)dt / 3600.0); + + /* Otherwise find bulk & wall reaction rates */ + rbulk = bulkrate(pr, c, net->Link[k].Kb, qu->BulkOrder) * qu->Bucf; + rwall = wallrate(pr, c, net->Link[k].Diam, net->Link[k].Kw, net->Link[k].Rc); + + /* Find change in concentration over timestep */ + dcbulk = rbulk * (double)dt; + dcwall = rwall * (double)dt; + + /* Update cumulative mass reacted */ + if (time->Htime >= time->Rstart) { + qu->Wbulk += ABS(dcbulk) * v; + qu->Wwall += ABS(dcwall) * v; + } + + /* Update concentration */ + dc = dcbulk + dcwall; + cnew = c + dc; + cnew = MAX(0.0, cnew); + return (cnew); +} + +double tankreact(EN_Project *pr, double c, double v, double kb, long dt) +/* +**------------------------------------------------------- +** Input: c = current WQ in tank +** v = tank volume +** kb = reaction coeff. +** dt = time step +** Output: returns new WQ value +** Purpose: computes new quality in a tank after +** reaction occurs +**------------------------------------------------------- +*/ +{ + double cnew, dc, rbulk; + quality_t *qu = &pr->quality; + time_options_t *time = &pr->time_options; + + /*** Updated 9/7/00 ***/ + /* If no reaction then return current WQ */ + if (!qu->Reactflag) { + return (c); + } + + /* For water age, update concentration by timestep */ + if (qu->Qualflag == AGE) { + return (c + (double)dt / 3600.0); + } + + /* Find bulk reaction rate */ + rbulk = bulkrate(pr, c, kb, qu->TankOrder) * qu->Tucf; + + /* Find concentration change & update quality */ + dc = rbulk * (double)dt; + if (time->Htime >= time->Rstart) { + qu->Wtank += ABS(dc) * v; + } + cnew = c + dc; + cnew = MAX(0.0, cnew); + return (cnew); +} + +double bulkrate(EN_Project *pr, double c, double kb, double order) +/* +**----------------------------------------------------------- +** Input: c = current WQ concentration +** kb = bulk reaction coeff. +** order = bulk reaction order +** Output: returns bulk reaction rate +** Purpose: computes bulk reaction rate (mass/volume/time) +**----------------------------------------------------------- +*/ +{ + double c1; + quality_t *qu = &pr->quality; + + /* Find bulk reaction potential taking into account */ + /* limiting potential & reaction order. */ + + /* Zero-order kinetics: */ + if (order == 0.0) + c = 1.0; + + /* Michaelis-Menton kinetics: */ + else if (order < 0.0) { + c1 = qu->Climit + SGN(kb) * c; + if (ABS(c1) < TINY) + c1 = SGN(c1) * TINY; + c = c / c1; + } + + /* N-th order kinetics: */ + else { + /* Account for limiting potential */ + if (qu->Climit == 0.0) + c1 = c; + else + c1 = MAX(0.0, SGN(kb) * (qu->Climit - c)); + + /* Compute concentration potential */ + if (order == 1.0) + c = c1; + else if (order == 2.0) + c = c1 * c; + else + c = c1 * pow(MAX(0.0, c), order - 1.0); + } + + /* Reaction rate = bulk coeff. * potential) */ + if (c < 0) + c = 0; + return (kb * c); +} + +double wallrate(EN_Project *pr, double c, double d, double kw, double kf) +/* +**------------------------------------------------------------ +** Input: c = current WQ concentration +** d = pipe diameter +** kw = intrinsic wall reaction coeff. +** kf = mass transfer coeff. for 0-order reaction +** (ft/sec) or apparent wall reaction coeff. +** for 1-st order reaction (1/sec) +** Output: returns wall reaction rate in mass/ft3/sec +** Purpose: computes wall reaction rate +**------------------------------------------------------------ +*/ +{ + quality_t *qu = &pr->quality; + if (kw == 0.0 || d == 0.0) { + return (0.0); + } + if (qu->WallOrder == 0.0) { /* 0-order reaction */ + kf = SGN(kw) * c * kf; /* Mass transfer rate (mass/ft2/sec)*/ + kw = kw * SQR(pr->Ucf[ELEV]); /* Reaction rate (mass/ft2/sec) */ + if (ABS(kf) < ABS(kw)) { /* Reaction mass transfer limited */ + kw = kf; + } + return (kw * 4.0 / d); /* Reaction rate (mass/ft3/sec) */ + } else + return (c * kf); /* 1st-order reaction */ +} + +/************************* End of QUALITY.C ***************************/ diff --git a/src/report.c b/src/report.c old mode 100755 new mode 100644 index 15555c3..9620f1f --- a/src/report.c +++ b/src/report.c @@ -1,1240 +1,1355 @@ -/* -********************************************************************* - -REPORT.C -- Reporting Routines for EPANET Program - -VERSION: 2.00 -DATE: 5/30/00 - 6/24/02 - 8/15/07 (2.00.11) - 2/14/08 (2.00.12) -AUTHOR: L. Rossman - US EPA - NRMRL - -This module contains various procedures (all beginning with -'write') that are called from other modules to write formatted -output to a report file. - -It also contains function disconnected(), called from writehydwarn() -and writehyderr(), that checks if a hydraulic solution causes a -network to become disconnected. - -The function writeline(S) is used throughout to write a -formatted string S to the report file. - -******************************************************************** -*/ - -#include -#include -#ifndef __APPLE__ -#include -#else -#include -#endif -#include -#include -#include "hash.h" -#include "text.h" -#include "types.h" -#include "funcs.h" -#define EXTERN extern -#include "vars.h" - -#define MAXCOUNT 10 /* Max. # of disconnected nodes listed */ -long LineNum; /* Current line number */ -long PageNum; /* Current page number */ -char DateStamp[26]; /* Current date & time */ -char Fprinterr; /* File write error flag */ - -/* Defined in enumstxt.h in EPANET.C */ -extern char *NodeTxt[]; -extern char *LinkTxt[]; -extern char *StatTxt[]; -extern char *TstatTxt[]; -extern char *RptFormTxt[]; - -typedef REAL4 *Pfloat; -void writenodetable(Pfloat *); -void writelinktable(Pfloat *); - - -int writereport() -/* -**------------------------------------------------------ -** Input: none -** Output: returns error code -** Purpose: writes formatted output report to file -** -** Calls strcomp() from the EPANET.C module. -**------------------------------------------------------ -*/ -{ - char tflag; - FILE *tfile; - int errcode = 0; - - /* If no secondary report file specified then */ - /* write formatted output to primary report file. */ - Fprinterr = FALSE; - if (Rptflag && strlen(Rpt2Fname) == 0 && RptFile != NULL) - { - writecon(FMT17); - writecon(Rpt1Fname); - if (Energyflag) writeenergy(); - errcode = writeresults(); - } - - /* A secondary report file was specified */ - else if (strlen(Rpt2Fname) > 0) - { - - /* If secondary report file has same name as either input */ - /* or primary report file then use primary report file. */ - if (strcomp(Rpt2Fname,InpFname) || - strcomp(Rpt2Fname,Rpt1Fname)) - { - writecon(FMT17); - writecon(Rpt1Fname); - if (Energyflag) writeenergy(); - errcode = writeresults(); - } - - /* Otherwise write report to secondary report file. */ - else - { - - /* Try to open file */ - tfile = RptFile; - tflag = Rptflag; - if ((RptFile = fopen(Rpt2Fname,"wt")) == NULL) - { - RptFile = tfile; - Rptflag = tflag; - errcode = 303; - } - - /* Write full formatted report to file */ - else - { - Rptflag = 1; - writecon(FMT17); - writecon(Rpt2Fname); - writelogo(); - if (Summaryflag) writesummary(); - if (Energyflag) writeenergy(); - errcode = writeresults(); - fclose(RptFile); - RptFile = tfile; - Rptflag = tflag; - } - } - } - - /* Special error handler for write-to-file error */ - if (Fprinterr) errmsg(309); - return(errcode); -} /* End of writereport */ - - -void writelogo() -/* -**-------------------------------------------------------------- -** Input: none -** Output: none -** Purpose: writes program logo to report file. -**-------------------------------------------------------------- -*/ -{ - int i; - int version; - int major; - int minor; - char s[80]; - time_t timer; /* time_t structure & functions time() & */ - /* ctime() are defined in time.h */ - - version = CODEVERSION; - major= version/10000; - minor= (version%10000)/100; - - time(&timer); - strcpy(DateStamp,ctime(&timer)); - PageNum = 1; - LineNum = 2; - fprintf(RptFile,FMT18); - fprintf(RptFile,"%s",DateStamp); - writeline(LOGO1); - writeline(LOGO2); - writeline(LOGO3); - writeline(LOGO4); - sprintf(s,LOGO5, major , minor); - writeline(s); - writeline(LOGO6); - writeline(""); -} /* End of writelogo */ - - -void writesummary() -/* -**-------------------------------------------------------------- -** Input: none -** Output: none -** Purpose: writes summary system information to report file -**-------------------------------------------------------------- -*/ -{ - char s[MAXFNAME+1]; - int i; - int nres = 0; - - for (i=0; i<3; i++) - { - if (strlen(Title[i]) > 0) - { - sprintf(s,"%-.70s",Title[i]); - writeline(s); - } - } - writeline(" "); - sprintf(s,FMT19,InpFname); - writeline(s); - sprintf(s,FMT20,Njuncs); - writeline(s); - for (i=1; i<=Ntanks; i++) - if (Tank[i].A == 0.0) nres++; - sprintf(s,FMT21a,nres); - writeline(s); - sprintf(s,FMT21b,Ntanks-nres); - writeline(s); - sprintf(s,FMT22,Npipes); - writeline(s); - sprintf(s,FMT23,Npumps); - writeline(s); - sprintf(s,FMT24,Nvalves); - writeline(s); - sprintf(s,FMT25,RptFormTxt[Formflag]); - writeline(s); - sprintf(s,FMT26,Hstep*Ucf[TIME],Field[TIME].Units); - writeline(s); - sprintf(s,FMT27,Hacc); - writeline(s); - - sprintf(s,FMT27a,CheckFreq); //(2.00.12 - LR) - writeline(s); //(2.00.12 - LR) - sprintf(s,FMT27b,MaxCheck); //(2.00.12 - LR) - writeline(s); //(2.00.12 - LR) - sprintf(s,FMT27c,DampLimit); //(2.00.12 - LR) - writeline(s); //(2.00.12 - LR) - - sprintf(s,FMT28,MaxIter); - writeline(s); - if (Qualflag == NONE || Dur == 0.0) - sprintf(s,FMT29); - else if (Qualflag == CHEM) - sprintf(s,FMT30,ChemName); - else if (Qualflag == TRACE) - sprintf(s,FMT31,Node[TraceNode].ID); - else if (Qualflag == AGE) - sprintf(s,FMT32); - writeline(s); - if (Qualflag != NONE && Dur > 0) - { - sprintf(s,FMT33,(float)Qstep/60.0); - writeline(s); - sprintf(s,FMT34,Ctol*Ucf[QUALITY],Field[QUALITY].Units); - writeline(s); - } - sprintf(s,FMT36,SpGrav); - writeline(s); - sprintf(s,FMT37a,Viscos/VISCOS); - writeline(s); - sprintf(s,FMT37b,Diffus/DIFFUS); - writeline(s); - sprintf(s,FMT38,Dmult); - writeline(s); - sprintf(s,FMT39,Dur*Ucf[TIME],Field[TIME].Units); - writeline(s); - if (Rptflag) - { - sprintf(s,FMT40); - writeline(s); - if (Nodeflag == 0) writeline(FMT41); - if (Nodeflag == 1) writeline(FMT42); - if (Nodeflag == 2) writeline(FMT43); - writelimits(DEMAND,QUALITY); - if (Linkflag == 0) writeline(FMT44); - if (Linkflag == 1) writeline(FMT45); - if (Linkflag == 2) writeline(FMT46); - writelimits(DIAM,HEADLOSS); - } - writeline(" "); -} /* End of writesummary */ - - -void writehydstat(int iter, double relerr) -/* -**-------------------------------------------------------------- -** Input: iter = # iterations to find hydraulic solution -** relerr = convergence error in hydraulic solution -** Output: none -** Purpose: writes hydraulic status report for solution found -** at current time period to report file -**-------------------------------------------------------------- -*/ -{ - int i,n; - char newstat; - char s1[MAXLINE+1]; - -/*** Updated 6/24/02 ***/ - char atime[13]; - - /* Display system status */ - strcpy(atime,clocktime(Atime,Htime)); - if (iter > 0) - { - if (relerr <= Hacc) - sprintf(s1,FMT58,atime,iter); - else - sprintf(s1,FMT59,atime,iter,relerr); - writeline(s1); - } - - /* - Display status changes for tanks. - D[n] is net inflow to tank at node n. - Old tank status is stored in OldStat[] - at indexes Nlinks+1 to Nlinks+Ntanks. - */ - for (i=1; i<=Ntanks; i++) - { - n = Tank[i].Node; - if (ABS(NodeDemand[n]) < 0.001) newstat = CLOSED; - else if (NodeDemand[n] > 0.0) newstat = FILLING; - else if (NodeDemand[n] < 0.0) newstat = EMPTYING; - else newstat = OldStat[Nlinks+i]; - if (newstat != OldStat[Nlinks+i]) - { - if (Tank[i].A > 0.0) - sprintf(s1,FMT50,atime,Node[n].ID,StatTxt[newstat], - (NodeHead[n]-Node[n].El)*Ucf[HEAD],Field[HEAD].Units); - else sprintf(s1,FMT51,atime,Node[n].ID,StatTxt[newstat]); - writeline(s1); - OldStat[Nlinks+i] = newstat; - } - } - - /* Display status changes for links */ - for (i=1; i<=Nlinks; i++) - { - if (LinkStatus[i] != OldStat[i]) - { - if (Htime == 0) - sprintf(s1,FMT52,atime,LinkTxt[Link[i].Type],Link[i].ID, - StatTxt[LinkStatus[i]]); - else sprintf(s1,FMT53,atime,LinkTxt[Link[i].Type],Link[i].ID, - StatTxt[OldStat[i]],StatTxt[LinkStatus[i]]); - writeline(s1); - OldStat[i] = LinkStatus[i]; - } - } - writeline(" "); -} /* End of writehydstat */ - - -void writeenergy() -/* -**------------------------------------------------------------- -** Input: none -** Output: none -** Purpose: writes energy usage report to report file -**------------------------------------------------------------- -*/ -{ - int j; - double csum; - char s[MAXLINE+1]; - if (Npumps == 0) return; - writeline(" "); - writeheader(ENERHDR,0); - csum = 0.0; - for (j=1; j<=Npumps; j++) - { - csum += Pump[j].Energy[5]; - if (LineNum == (long)PageSize) writeheader(ENERHDR,1); - sprintf(s,"%-8s %6.2f %6.2f %9.2f %9.2f %9.2f %9.2f", - Link[Pump[j].Link].ID,Pump[j].Energy[0],Pump[j].Energy[1], - Pump[j].Energy[2],Pump[j].Energy[3],Pump[j].Energy[4], - Pump[j].Energy[5]); - writeline(s); - } - fillstr(s,'-',63); - writeline(s); - -/*** Updated 6/24/02 ***/ - sprintf(s,FMT74,"",Emax*Dcost); - writeline(s); - sprintf(s,FMT75,"",csum+Emax*Dcost); -/*** End of update ***/ - - writeline(s); - writeline(" "); -} /* End of writeenergy */ - - -int writeresults() -/* -**-------------------------------------------------------------- -** Input: none -** Output: returns error code -** Purpose: writes simulation results to report file -**-------------------------------------------------------------- -*/ -{ - Pfloat *x; /* Array of pointers to floats */ - int j,m,n,np,nnv,nlv; - int errcode = 0; - - /* - **----------------------------------------------------------- - ** NOTE: The OutFile contains results for 4 node variables - ** (demand, head, pressure, & quality) and 8 link - ** variables (flow, velocity, headloss, quality, - ** status, setting, reaction rate & friction factor) - ** at each reporting time. - **----------------------------------------------------------- - */ - - /* Return if no output file */ - if (OutFile == NULL) return(106); - - /* Return if no nodes or links selected for reporting */ - /* or if no node or link report variables enabled. */ - if (!Nodeflag && !Linkflag) return(errcode); - nnv = 0; - for (j=ELEV; j<=QUALITY; j++) nnv += Field[j].Enabled; - nlv = 0; - for (j=LENGTH; j<=FRICTION; j++) nlv += Field[j].Enabled; - if (nnv == 0 && nlv == 0) return(errcode); - - /* Allocate memory for output variables. */ - /* m = larger of # node variables & # link variables */ - /* n = larger of # nodes & # links */ - m = MAX( (QUALITY-DEMAND+1), (FRICTION-FLOW+1) ); - n = MAX( (Nnodes+1), (Nlinks+1)); - x = (Pfloat *) calloc(m, sizeof(Pfloat)); - ERRCODE( MEMCHECK(x) ); - if (errcode) return(errcode); - for (j=0; j 0 && Nodeflag > 0) writenodetable(x); - - /* Read in link results & write link table. */ - for (j=FLOW; j<=FRICTION; j++) - fread((x[j-FLOW])+1,sizeof(REAL4),Nlinks,OutFile); - if (nlv > 0 && Linkflag > 0) writelinktable(x); - Htime += Rstep; - } - - /* Free allocated memory */ - for (j=0; j 1.e6) sprintf(s1, "%10.2e", y[j]); - else sprintf(s1, "%10.*f", Field[j].Precision, y[j]); -/*** End of update ***/ - - strcat(s, s1); - } - } - - /* Note if node is a reservoir/tank */ - if (i > Njuncs) - { - strcat(s, " "); - strcat(s, NodeTxt[getnodetype(i)]); - } - - /* Write results for node */ - writeline(s); - } - } - writeline(" "); -} - - -void writelinktable(Pfloat *x) -/* -**--------------------------------------------------------------- -** Input: x = pointer to link results for current time -** Output: none -** Purpose: writes link results for current time to report file -**--------------------------------------------------------------- -*/ -{ - int i,j,k; - char s[MAXLINE+1],s1[16]; - double y[MAXVAR]; - - /* Write table header */ - writeheader(LINKHDR,0); - - /* For each link: */ - for (i=1; i<=Nlinks; i++) - { - - /* Place results for each link variable in y */ - y[LENGTH] = Link[i].Len*Ucf[LENGTH]; - y[DIAM] = Link[i].Diam*Ucf[DIAM]; - for (j=FLOW; j<=FRICTION; j++) y[j] = *((x[j-FLOW])+i); - - /* Check if link gets reported on */ - if ((Linkflag == 1 || Link[i].Rpt) && checklimits(y,DIAM,FRICTION)) - { - - /* Check if new page needed */ - if (LineNum == (long)PageSize) writeheader(LINKHDR,1); - - /* Add link ID and each reported field to string s */ - sprintf(s,"%-15s",Link[i].ID); - for (j=LENGTH; j<=FRICTION; j++) - { - if (Field[j].Enabled == TRUE) - { - if (j == STATUS) - { - if (y[j] <= CLOSED) k = CLOSED; - else if (y[j] == ACTIVE) k = ACTIVE; - else k = OPEN; - sprintf(s1, "%10s", StatTxt[k]); - } - -/*** Updated 6/24/02 ***/ - else - { - if (fabs(y[j]) > 1.e6) sprintf(s1, "%10.2e", y[j]); - else sprintf(s1, "%10.*f", Field[j].Precision, y[j]); - } -/*** End of update ***/ - - strcat(s, s1); - } - } - - /* Note if link is a pump or valve */ - if ( (j = Link[i].Type) > PIPE) - { - strcat(s, " "); - strcat(s, LinkTxt[j]); - } - - /* Write results for link */ - writeline(s); - } - } - writeline(" "); -} - - -void writeheader(int type, int contin) -/* -**-------------------------------------------------------------- -** Input: type = table type -** contin = table continuation flag -** Output: none -** Purpose: writes column headings for output report tables -**-------------------------------------------------------------- -*/ -{ - char s[MAXLINE+1],s1[MAXLINE+1],s2[MAXLINE+1],s3[MAXLINE+1]; - int i,n; - - /* Move to next page if < 11 lines remain on current page. */ - if (Rptflag && LineNum+11 > (long)PageSize) - { - while (LineNum < (long)PageSize) writeline(" "); - } - writeline(" "); - - /* Hydraulic Status Table */ - if (type == STATHDR) - { - sprintf(s,FMT49); - if (contin) strcat(s,t_CONTINUED); - writeline(s); - fillstr(s,'-',70); - writeline(s); - } - - /* Energy Usage Table */ - if (type == ENERHDR) - { - if (Unitsflag == SI) strcpy(s1,t_perM3); - else strcpy(s1,t_perMGAL); - sprintf(s,FMT71); - if (contin) strcat(s,t_CONTINUED); - writeline(s); - fillstr(s,'-',63); - writeline(s); - sprintf(s,FMT72); - writeline(s); - sprintf(s,FMT73,s1); - writeline(s); - fillstr(s,'-',63); - writeline(s); - } - - /* Node Results Table */ - if (type == NODEHDR) - { - if (Tstatflag == RANGE) sprintf(s,FMT76,t_DIFFER); - else if (Tstatflag != SERIES) sprintf(s,FMT76,TstatTxt[Tstatflag]); - else if (Dur == 0) sprintf(s,FMT77); - else sprintf(s,FMT78,clocktime(Atime,Htime)); - if (contin) strcat(s,t_CONTINUED); - writeline(s); - n = 15; - sprintf(s2,"%15s",""); - strcpy(s,t_NODEID); - sprintf(s3,"%-15s",s); - for (i=ELEV; i MaxIter && relerr <= Hacc) - { - sprintf(Msg,WARN02,clocktime(Atime,Htime)); - if (Messageflag) writeline(Msg); - flag = 2; - } - - /* Check for negative pressures */ - for (i=1; i<=Njuncs; i++) - { - if (NodeHead[i] < Node[i].El && NodeDemand[i] > 0.0) - { - sprintf(Msg,WARN06,clocktime(Atime,Htime)); - if (Messageflag) writeline(Msg); - flag = 6; - break; - } - } - - /* Check for abnormal valve condition */ - for (i=1; i<=Nvalves; i++) - { - j = Valve[i].Link; - if (LinkStatus[j] >= XFCV) - { - sprintf(Msg,WARN05,LinkTxt[Link[j].Type],Link[j].ID, - StatTxt[LinkStatus[j]],clocktime(Atime,Htime)); - if (Messageflag) writeline(Msg); - flag = 5; - } - } - - /* Check for abnormal pump condition */ - for (i=1; i<=Npumps; i++) - { - j = Pump[i].Link; - s = LinkStatus[j]; //(2.00.11 - LR) - if (LinkStatus[j] >= OPEN) //(2.00.11 - LR) - { //(2.00.11 - LR) - if (Q[j] > LinkSetting[j]*Pump[i].Qmax) s = XFLOW; //(2.00.11 - LR) - if (Q[j] < 0.0) s = XHEAD; //(2.00.11 - LR) - } //(2.00.11 - LR) - if (s == XHEAD || s == XFLOW) //(2.00.11 - LR) - { - sprintf(Msg,WARN04,Link[j].ID,StatTxt[s], //(2.00.11 - LR) - clocktime(Atime,Htime)); - if (Messageflag) writeline(Msg); - flag = 4; - } - } - - /* Check if system is unbalanced */ - if (iter > MaxIter && relerr > Hacc) - { - sprintf(Msg,WARN01,clocktime(Atime,Htime)); - if (ExtraIter == -1) strcat(Msg,t_HALTED); - if (Messageflag) writeline(Msg); - flag = 1; - } - - /* Check for disconnected network */ - /* & update global warning flag */ - if (flag > 0) - { - disconnected(); - Warnflag = flag; - } - return(flag); -} /* End of writehydwarn */ - - -void writehyderr(int errnode) -/* -**----------------------------------------------------------- -** Input: none -** Output: none -** Purpose: outputs status & checks connectivity when -** network hydraulic equations cannot be solved. -**----------------------------------------------------------- -*/ -{ - sprintf(Msg,FMT62,clocktime(Atime,Htime),Node[errnode].ID); - if (Messageflag) writeline(Msg); - writehydstat(0,0); - disconnected(); -} /* End of writehyderr */ - - -int disconnected() -/* -**------------------------------------------------------------------- -** Input: None -** Output: Returns number of disconnected nodes -** Purpose: Tests current hydraulic solution to see if any closed -** links have caused the network to become disconnected. -**------------------------------------------------------------------- -*/ -{ - int i, j; - int count, mcount; - int errcode = 0; - int *nodelist; - char *marked; - - /* Allocate memory for node list & marked list */ - nodelist = (int *) calloc(Nnodes+1,sizeof(int)); - marked = (char *) calloc(Nnodes+1,sizeof(char)); - ERRCODE(MEMCHECK(nodelist)); - ERRCODE(MEMCHECK(marked)); - if (errcode) return(0); - - /* Place tanks on node list and marked list */ - for (i=1; i<=Ntanks; i++) - { - j = Njuncs + i; - nodelist[i] = j; - marked[j] = 1; - } - - /* Place junctions with negative demands on the lists */ - mcount = Ntanks; - for (i=1; i<=Njuncs; i++) - { - if (NodeDemand[i] < 0.0) - { - mcount++; - nodelist[mcount] = i; - marked[i] = 1; - } - } - - /* Mark all nodes that can be connected to tanks */ - /* and count number of nodes remaining unmarked. */ - marknodes(mcount,nodelist,marked); - j = 0; - count = 0; - for (i=1; i<=Njuncs; i++) - { - if (!marked[i] && NodeDemand[i] != 0.0) /* Skip if no demand */ - { - count++; - if (count <= MAXCOUNT && Messageflag) - { - sprintf(Msg,WARN03a,Node[i].ID,clocktime(Atime,Htime)); - writeline(Msg); - } - j = i; /* Last unmarked node */ - } - } - - /* Report number of unmarked nodes and find closed link */ - /* on path from node j back to a tank. */ - if (count > 0 && Messageflag) - { - if (count > MAXCOUNT) - { - sprintf(Msg, WARN03b, count-MAXCOUNT, clocktime(Atime,Htime)); - writeline(Msg); - } - getclosedlink(j,marked); - } - - /* Free allocated memory */ - free(nodelist); - free(marked); - return(count); -} /* End of disconnected() */ - - -void marknodes(int m, int *nodelist, char *marked) -/* -**---------------------------------------------------------------- -** Input: m = number of source nodes -** nodelist[] = list of nodes to be traced from -** marked[] = TRUE if node connected to source -** Output: None. -** Purpose: Marks all junction nodes connected to tanks. -**---------------------------------------------------------------- -*/ -{ - int i, j, k, n; - Padjlist alink; - - /* Scan each successive entry of node list */ - n = 1; - while (n <= m ) - { - - /* Scan all nodes connected to current node */ - i = nodelist[n]; - for (alink = Adjlist[i]; alink != NULL; alink = alink->next) - { - - /* Get indexes of connecting link and node */ - k = alink->link; - j = alink->node; - if (marked[j]) continue; - - /* Check if valve connection is in correct direction */ - switch (Link[k].Type) - { - case CV: - case PRV: - case PSV: if (j == Link[k].N1) continue; - } - - /* Mark connection node if link not closed */ - if (LinkStatus[k] > CLOSED) - { - marked[j] = 1; - m++; - nodelist[m] = j; - } - } - n++; - } -} /* End of marknodes() */ - - -void getclosedlink(int i, char *marked) -/* -**---------------------------------------------------------------- -** Input: i = junction index -** marked[] = marks nodes already examined -** Output: None. -** Purpose: Determines if a closed link connects to junction i. -**---------------------------------------------------------------- -*/ -{ - int j,k; - Padjlist alink; - marked[i] = 2; - for (alink = Adjlist[i]; alink != NULL; alink = alink->next) - { - k = alink->link; - j = alink->node; - if (marked[j] == 2) continue; - if (marked[j] == 1) - { - sprintf(Msg, WARN03c, Link[k].ID); - writeline(Msg); - return; - } - else getclosedlink(j,marked); - } -} - - -void writelimits(int j1, int j2) -/* -**-------------------------------------------------------------- -** Input: j1 = index of first output variable -** j2 = index of last output variable -** Output: none -** Purpose: writes reporting criteria to output report -**-------------------------------------------------------------- -*/ -{ - int j; - for (j=j1; j<=j2; j++) - { - if (Field[j].RptLim[LOW] < BIG) - { - sprintf(Msg,FMT47, - Field[j].Name,Field[j].RptLim[LOW],Field[j].Units); - writeline(Msg); - } - if (Field[j].RptLim[HI] > -BIG) - { - sprintf(Msg,FMT48, - Field[j].Name,Field[j].RptLim[HI],Field[j].Units); - writeline(Msg); - } - } -} /* End of writelimits */ - - -int checklimits(double *y, int j1, int j2) -/* -**-------------------------------------------------------------- -** Input: *y = array of output results -** j1 = index of first output variable -** j2 = index of last output variable -** Output: returns 1 if criteria met, 0 otherwise -** Purpose: checks if output reporting criteria is met -**-------------------------------------------------------------- -*/ -{ - int j; - for (j=j1; j<=j2; j++) - { - if (y[j] > Field[j].RptLim[LOW] - || y[j] < Field[j].RptLim[HI]) return(0); - } - return(1); -} /* End of checklim */ - - -void writetime(char *fmt) -/* -**---------------------------------------------------------------- -** Input: fmt = format string -** Output: none -** Purpose: writes starting/ending time of a run to report file -**---------------------------------------------------------------- -*/ -{ - time_t timer; - time(&timer); - sprintf(Msg, fmt, ctime(&timer)); - writeline(Msg); -} - - -char *clocktime(char *atime, long seconds) -/* -**-------------------------------------------------------------- -** Input: seconds = time in seconds -** Output: atime = time in hrs:min -** (returns pointer to atime) -** Purpose: converts time in seconds to hours:minutes format -**-------------------------------------------------------------- -*/ -{ -/*** Updated 6/24/02 ***/ - int h,m,s; - h = seconds/3600; - m = (seconds % 3600) / 60; - s = seconds - 3600*h - 60*m; - sprintf(atime, "%01d:%02d:%02d", h,m,s); - return(atime); -} /* End of clocktime */ - - -char *fillstr(char *s, char ch, int n) -/* -**--------------------------------------------------------- -** Fills n bytes of s to character ch. -** NOTE: does not check for overwriting s. -**--------------------------------------------------------- -*/ -{ - int i; - for (i=0; i<=n; i++) s[i] = ch; - s[n+1] = '\0'; - return(s); -} - - -int getnodetype(int i) -/* -**--------------------------------------------------------- -** Determines type of node with index i -** (junction = 0, reservoir = 1, tank = 2). -**--------------------------------------------------------- -*/ -{ - if (i <= Njuncs) return(0); - if (Tank[i-Njuncs].A == 0.0) return(1); - return(2); -} - -/********************* END OF REPORT.C ********************/ +/* +********************************************************************* + +REPORT.C -- Reporting Routines for EPANET Program + +VERSION: 2.00 +DATE: 5/30/00 + 6/24/02 + 8/15/07 (2.00.11) + 2/14/08 (2.00.12) +AUTHOR: L. Rossman + US EPA - NRMRL + +This module contains various procedures (all beginning with +'write') that are called from other modules to write formatted +output to a report file. + +It also contains function disconnected(), called from writehydwarn() +and writehyderr(), that checks if a hydraulic solution causes a +network to become disconnected. + +The function writeline(pr, S) is used throughout to write a +formatted string S to the report file. + +******************************************************************** +*/ + +#include +#include +#ifndef __APPLE__ +#include +#else +#include +#endif +#include "epanet2.h" +#include "funcs.h" +#include "hash.h" +#include "text.h" +#include "types.h" +#include +#include +#define EXTERN extern +#include "vars.h" + +#undef WINDOWS +#ifdef _WIN32 +#define WINDOWS +#define snprintf _snprintf +#endif + +#define MAXCOUNT 10 /* Max. # of disconnected nodes listed */ + +/* Defined in enumstxt.h in EPANET.C */ +extern char *NodeTxt[]; +extern char *LinkTxt[]; +extern char *StatTxt[]; +extern char *TstatTxt[]; +extern char *RptFormTxt[]; + +typedef REAL4 *Pfloat; +void writenodetable(EN_Project *pr, Pfloat *); +void writelinktable(EN_Project *pr, Pfloat *); + +int writereport(EN_Project *pr) +/* +**------------------------------------------------------ +** Input: none +** Output: returns error code +** Purpose: writes formatted output report to file +** +** Calls strcomp() from the EPANET.C module. +**------------------------------------------------------ +*/ +{ + report_options_t *rep = &pr->report; + parser_data_t *par = &pr->parser; + + char tflag; + FILE *tfile; + int errcode = 0; + + /* If no secondary report file specified then */ + /* write formatted output to primary report file. */ + rep->Fprinterr = FALSE; + if (rep->Rptflag && strlen(rep->Rpt2Fname) == 0 && rep->RptFile != NULL) { + writecon(FMT17); + writecon(rep->Rpt1Fname); + if (rep->Energyflag) + writeenergy(pr); + errcode = writeresults(pr); + } + + /* A secondary report file was specified */ + else if (strlen(rep->Rpt2Fname) > 0) { + + /* If secondary report file has same name as either input */ + /* or primary report file then use primary report file. */ + if (strcomp(rep->Rpt2Fname, par->InpFname) || strcomp(rep->Rpt2Fname, rep->Rpt1Fname)) { + writecon(FMT17); + writecon(rep->Rpt1Fname); + if (rep->Energyflag) + writeenergy(pr); + errcode = writeresults(pr); + } + + /* Otherwise write report to secondary report file. */ + else { + + /* Try to open file */ + tfile = rep->RptFile; + tflag = rep->Rptflag; + if ((rep->RptFile = fopen(rep->Rpt2Fname, "wt")) == NULL) { + rep->RptFile = tfile; + rep->Rptflag = tflag; + errcode = 303; + } + + /* Write full formatted report to file */ + else { + rep->Rptflag = 1; + writecon(FMT17); + writecon(rep->Rpt2Fname); + writelogo(pr); + if (rep->Summaryflag) + writesummary(pr); + if (rep->Energyflag) + writeenergy(pr); + errcode = writeresults(pr); + fclose(rep->RptFile); + rep->RptFile = tfile; + rep->Rptflag = tflag; + } + } + } + + /* Special error handler for write-to-file error */ + if (rep->Fprinterr) + errmsg(pr,309); + return (errcode); +} /* End of writereport */ + +void writelogo(EN_Project *pr) +/* +**-------------------------------------------------------------- +** Input: none +** Output: none +** Purpose: writes program logo to report file. +**-------------------------------------------------------------- +*/ +{ + report_options_t *rep = &pr->report; + + int version; + int major; + int minor; + char s[80]; + time_t timer; /* time_t structure & functions time() & */ + /* ctime() are defined in time.h */ + + version = CODEVERSION; + major = version / 10000; + minor = (version % 10000) / 100; + + time(&timer); + strcpy(rep->DateStamp, ctime(&timer)); + rep->PageNum = 1; + rep->LineNum = 2; + fprintf(rep->RptFile, FMT18); + fprintf(rep->RptFile, "%s", rep->DateStamp); + writeline(pr, LOGO1); + writeline(pr, LOGO2); + writeline(pr, LOGO3); + writeline(pr, LOGO4); + sprintf(s, LOGO5, major, minor); + writeline(pr, s); + writeline(pr, LOGO6); + writeline(pr, ""); +} /* End of writelogo */ + +void writesummary(EN_Project *pr) +/* +**-------------------------------------------------------------- +** Input: none +** Output: none +** Purpose: writes summary system information to report file +**-------------------------------------------------------------- +*/ +{ + EN_Network *net = &pr->network; + hydraulics_t *hyd = &pr->hydraulics; + report_options_t *rep = &pr->report; + quality_t *qu = &pr->quality; + parser_data_t *par = &pr->parser; + time_options_t *time = &pr->time_options; + time_options_t *ti = &pr->time_options; + + char s[MAXFNAME + 1]; + int i; + int nres = 0; + + for (i = 0; i < 3; i++) { + if (strlen(pr->Title[i]) > 0) { + sprintf(s, "%-.70s", pr->Title[i]); + writeline(pr, s); + } + } + writeline(pr, " "); + sprintf(s, FMT19, par->InpFname); + writeline(pr, s); + sprintf(s, FMT20, net->Njuncs); + writeline(pr, s); + for (i = 1; i <= net->Ntanks; i++) + if (net->Tank[i].A == 0.0) + nres++; + sprintf(s, FMT21a, nres); + writeline(pr, s); + sprintf(s, FMT21b, net->Ntanks - nres); + writeline(pr, s); + sprintf(s, FMT22, net->Npipes); + writeline(pr, s); + sprintf(s, FMT23, net->Npumps); + writeline(pr, s); + sprintf(s, FMT24, net->Nvalves); + writeline(pr, s); + sprintf(s, FMT25, RptFormTxt[hyd->Formflag]); + writeline(pr, s); + sprintf(s, FMT26, time->Hstep * pr->Ucf[TIME], rep->Field[TIME].Units); + writeline(pr, s); + sprintf(s, FMT27, hyd->Hacc); + writeline(pr, s); + + sprintf(s, FMT27a, hyd->CheckFreq); + writeline(pr, s); + sprintf(s, FMT27b, hyd->MaxCheck); + writeline(pr, s); + sprintf(s, FMT27c, hyd->DampLimit); + writeline(pr, s); + + sprintf(s, FMT28, hyd->MaxIter); + writeline(pr, s); + if (qu->Qualflag == NONE || time->Dur == 0.0) + sprintf(s, FMT29); + else if (qu->Qualflag == CHEM) + sprintf(s, FMT30, qu->ChemName); + else if (qu->Qualflag == TRACE) + sprintf(s, FMT31, net->Node[qu->TraceNode].ID); + else if (qu->Qualflag == AGE) + sprintf(s, FMT32); + writeline(pr, s); + if (qu->Qualflag != NONE && ti->Dur > 0) { + sprintf(s, FMT33, (float)qu->Qstep / 60.0); + writeline(pr, s); + sprintf(s, FMT34, qu->Ctol * pr->Ucf[QUALITY], rep->Field[QUALITY].Units); + writeline(pr, s); + } + sprintf(s, FMT36, hyd->SpGrav); + writeline(pr, s); + sprintf(s, FMT37a, hyd->Viscos / VISCOS); + writeline(pr, s); + sprintf(s, FMT37b, qu->Diffus / DIFFUS); + writeline(pr, s); + sprintf(s, FMT38, hyd->Dmult); + writeline(pr, s); + sprintf(s, FMT39, time->Dur * pr->Ucf[TIME], rep->Field[TIME].Units); + writeline(pr, s); + if (rep->Rptflag) { + sprintf(s, FMT40); + writeline(pr, s); + if (rep->Nodeflag == 0) + writeline(pr, FMT41); + if (rep->Nodeflag == 1) + writeline(pr, FMT42); + if (rep->Nodeflag == 2) + writeline(pr, FMT43); + writelimits(pr, DEMAND, QUALITY); + if (rep->Linkflag == 0) + writeline(pr, FMT44); + if (rep->Linkflag == 1) + writeline(pr, FMT45); + if (rep->Linkflag == 2) + writeline(pr, FMT46); + writelimits(pr, DIAM, HEADLOSS); + } + writeline(pr, " "); +} /* End of writesummary */ + +void writehydstat(EN_Project *pr, int iter, double relerr) +/* +**-------------------------------------------------------------- +** Input: iter = # iterations to find hydraulic solution +** relerr = convergence error in hydraulic solution +** Output: none +** Purpose: writes hydraulic status report for solution found +** at current time period to report file +**-------------------------------------------------------------- +*/ +{ + EN_Network *net = &pr->network; + hydraulics_t *hyd = &pr->hydraulics; + report_options_t *rep = &pr->report; + time_options_t *time = &pr->time_options; + + + double *NodeDemand = hyd->NodeDemand; + Stank *Tank = net->Tank; + Slink *Link = net->Link; + + int i, n; + StatType newstat; + char s1[MAXLINE + 1]; + + /*** Updated 6/24/02 ***/ + char atime[13]; + + /* Display system status */ + strcpy(atime, clocktime(rep->Atime, time->Htime)); + if (iter > 0) { + if (relerr <= hyd->Hacc) + sprintf(s1, FMT58, atime, iter); + else + sprintf(s1, FMT59, atime, iter, relerr); + writeline(pr, s1); + } + + /* + Display status changes for tanks. + D[n] is net inflow to tank at node n. + Old tank status is stored in OldStat[] + at indexes Nlinks+1 to Nlinks+Ntanks. + */ + for (i = 1; i <= net->Ntanks; i++) { + n = net->Tank[i].Node; + if (ABS(NodeDemand[n]) < 0.001) + newstat = CLOSED; + else if (NodeDemand[n] > 0.0) + newstat = FILLING; + else if (NodeDemand[n] < 0.0) + newstat = EMPTYING; + else + newstat = hyd->OldStat[net->Nlinks + i]; + if (newstat != hyd->OldStat[net->Nlinks + i]) { + if (Tank[i].A > 0.0) + snprintf(s1, MAXLINE, FMT50, atime, net->Node[n].ID, StatTxt[newstat], + (hyd->NodeHead[n] - net->Node[n].El) * pr->Ucf[HEAD], + rep->Field[HEAD].Units); + else + snprintf(s1, MAXLINE, FMT51, atime, net->Node[n].ID, StatTxt[newstat]); + writeline(pr, s1); + hyd->OldStat[net->Nlinks + i] = newstat; + } + } + + /* Display status changes for links */ + for (i = 1; i <= net->Nlinks; i++) { + if (hyd->LinkStatus[i] != hyd->OldStat[i]) { + if (time->Htime == 0) + sprintf(s1, FMT52, atime, LinkTxt[(int)net->Link[i].Type], net->Link[i].ID, + StatTxt[(int)hyd->LinkStatus[i]]); + else + sprintf(s1, FMT53, atime, LinkTxt[Link[i].Type], net->Link[i].ID, + StatTxt[hyd->OldStat[i]], StatTxt[hyd->LinkStatus[i]]); + writeline(pr, s1); + hyd->OldStat[i] = hyd->LinkStatus[i]; + } + } + writeline(pr, " "); +} /* End of writehydstat */ + +void writeenergy(EN_Project *pr) +/* +**------------------------------------------------------------- +** Input: none +** Output: none +** Purpose: writes energy usage report to report file +**------------------------------------------------------------- +*/ +{ + EN_Network *net = &pr->network; + hydraulics_t *hyd = &pr->hydraulics; + report_options_t *rep = &pr->report; + + int j; + double csum; + char s[MAXLINE + 1]; + if (net->Npumps == 0) + return; + writeline(pr, " "); + writeheader(pr,ENERHDR, 0); + csum = 0.0; + for (j = 1; j <= net->Npumps; j++) { + Spump *pump = &net->Pump[j]; + csum += pump->Energy[5]; + if (rep->LineNum == (long)rep->PageSize) + writeheader(pr, ENERHDR, 1); + sprintf(s, "%-8s %6.2f %6.2f %9.2f %9.2f %9.2f %9.2f", + net->Link[pump->Link].ID, pump->Energy[0], pump->Energy[1], + pump->Energy[2], pump->Energy[3], pump->Energy[4], + pump->Energy[5]); + writeline(pr, s); + } + fillstr(s, '-', 63); + writeline(pr, s); + + /*** Updated 6/24/02 ***/ + sprintf(s, FMT74, "", hyd->Emax * hyd->Dcost); + writeline(pr, s); + sprintf(s, FMT75, "", csum + hyd->Emax * hyd->Dcost); + /*** End of update ***/ + + writeline(pr, s); + writeline(pr, " "); +} /* End of writeenergy */ + +int writeresults(EN_Project *pr) +/* +**-------------------------------------------------------------- +** Input: none +** Output: returns error code +** Purpose: writes simulation results to report file +**-------------------------------------------------------------- +*/ +{ + EN_Network *net = &pr->network; + out_file_t *out = &pr->out_files; + report_options_t *rep = &pr->report; + time_options_t *time = &pr->time_options; + FILE *outFile = out->OutFile; + + + Pfloat *x; /* Array of pointers to floats */ + int j, m, n, np, nnv, nlv; + int errcode = 0; + + /* + **----------------------------------------------------------- + ** NOTE: The OutFile contains results for 4 node variables + ** (demand, head, pressure, & quality) and 8 link + ** variables (flow, velocity, headloss, quality, + ** status, setting, reaction rate & friction factor) + ** at each reporting time. + **----------------------------------------------------------- + */ + + /* Return if no output file */ + if (outFile == NULL) + return (106); + + /* Return if no nodes or links selected for reporting */ + /* or if no node or link report variables enabled. */ + if (!rep->Nodeflag && !rep->Linkflag) { + return (errcode); + } + nnv = 0; + for (j = ELEV; j <= QUALITY; j++) { + nnv += rep->Field[j].Enabled; + } + nlv = 0; + for (j = LENGTH; j <= FRICTION; j++) { + nlv += rep->Field[j].Enabled; + } + if (nnv == 0 && nlv == 0) { + return (errcode); + } + + /* Allocate memory for output variables. */ + /* m = larger of # node variables & # link variables */ + /* n = larger of # nodes & # links */ + m = MAX((QUALITY - DEMAND + 1), (FRICTION - FLOW + 1)); + n = MAX((net->Nnodes + 1), (net->Nlinks + 1)); + x = (Pfloat *)calloc(m, sizeof(Pfloat)); + ERRCODE(MEMCHECK(x)); + if (errcode) + return (errcode); + for (j = 0; j < m; j++) { + x[j] = (REAL4 *)calloc(n, sizeof(REAL4)); + ERRCODE(MEMCHECK(x[j])); + } + if (errcode) + return (errcode); + + /* Re-position output file & initialize report time. */ + fseek(outFile, out->OutOffset2, SEEK_SET); + time->Htime = time->Rstart; + + /* For each reporting time: */ + for (np = 1; np <= rep->Nperiods; np++) { + + /* Read in node results & write node table. */ + /* (Remember to offset x[j] by 1 because array is zero-based). */ + for (j = DEMAND; j <= QUALITY; j++) { + fread((x[j - DEMAND]) + 1, sizeof(REAL4), net->Nnodes, outFile); + } + if (nnv > 0 && rep->Nodeflag > 0) { + writenodetable(pr,x); + } + + /* Read in link results & write link table. */ + for (j = FLOW; j <= FRICTION; j++) { + fread((x[j - FLOW]) + 1, sizeof(REAL4), net->Nlinks, outFile); + } + if (nlv > 0 && rep->Linkflag > 0) { + writelinktable(pr,x); + } + time->Htime += time->Rstep; + } + + /* Free allocated memory */ + for (j = 0; j < m; j++) { + free(x[j]); + } + free(x); + return (errcode); +} /* End of writereport */ + +void writenodetable(EN_Project *pr, Pfloat *x) +/* +**--------------------------------------------------------------- +** Input: x = pointer to node results for current time +** Output: none +** Purpose: writes node results for current time to report file +**--------------------------------------------------------------- +*/ +{ + EN_Network *net = &pr->network; + report_options_t *rep = &pr->report; + + int i, j; + char s[MAXLINE + 1], s1[16]; + double y[MAXVAR]; + + /* Write table header */ + writeheader(pr, NODEHDR, 0); + + /* For each node: */ + for (i = 1; i <= net->Nnodes; i++) { + Snode *node = &net->Node[i]; + /* Place results for each node variable in y */ + y[ELEV] = node->El * pr->Ucf[ELEV]; + for (j = DEMAND; j <= QUALITY; j++) + y[j] = *((x[j - DEMAND]) + i); + + /* Check if node gets reported on */ + if ((rep->Nodeflag == 1 || node->Rpt) && checklimits(rep, y, ELEV, QUALITY)) { + + /* Check if new page needed */ + if (rep->LineNum == (long)rep->PageSize) + writeheader(pr, NODEHDR, 1); + + /* Add node ID and each reported field to string s */ + sprintf(s, "%-15s", node->ID); + for (j = ELEV; j <= QUALITY; j++) { + if (rep->Field[j].Enabled == TRUE) { + + /*** Updated 6/24/02 ***/ + if (fabs(y[j]) > 1.e6) + sprintf(s1, "%10.2e", y[j]); + else + sprintf(s1, "%10.*f", rep->Field[j].Precision, y[j]); + /*** End of update ***/ + + strcat(s, s1); + } + } + + /* Note if node is a reservoir/tank */ + if (i > net->Njuncs) { + strcat(s, " "); + strcat(s, NodeTxt[getnodetype(net,i)]); + } + + /* Write results for node */ + writeline(pr, s); + } + } + writeline(pr, " "); +} + +void writelinktable(EN_Project *pr, Pfloat *x) +/* +**--------------------------------------------------------------- +** Input: x = pointer to link results for current time +** Output: none +** Purpose: writes link results for current time to report file +**--------------------------------------------------------------- +*/ +{ + EN_Network *net = &pr->network; + report_options_t *rep = &pr->report; + Slink *Link = net->Link; + double *Ucf = pr->Ucf; + + + int i, j, k; + char s[MAXLINE + 1], s1[16]; + double y[MAXVAR]; + + /* Write table header */ + writeheader(pr,LINKHDR, 0); + + /* For each link: */ + for (i = 1; i <= net->Nlinks; i++) { + + /* Place results for each link variable in y */ + y[LENGTH] = Link[i].Len * Ucf[LENGTH]; + y[DIAM] = Link[i].Diam * Ucf[DIAM]; + for (j = FLOW; j <= FRICTION; j++) + y[j] = *((x[j - FLOW]) + i); + + /* Check if link gets reported on */ + if ((rep->Linkflag == 1 || Link[i].Rpt) && checklimits(rep, y, DIAM, FRICTION)) { + + /* Check if new page needed */ + if (rep->LineNum == (long)rep->PageSize) + writeheader(pr,LINKHDR, 1); + + /* Add link ID and each reported field to string s */ + sprintf(s, "%-15s", Link[i].ID); + for (j = LENGTH; j <= FRICTION; j++) { + if (rep->Field[j].Enabled == TRUE) { + if (j == STATUS) { + if (y[j] <= CLOSED) + k = CLOSED; + else if (y[j] == ACTIVE) + k = ACTIVE; + else + k = OPEN; + sprintf(s1, "%10s", StatTxt[k]); + } + + /*** Updated 6/24/02 ***/ + else { + if (fabs(y[j]) > 1.e6) + sprintf(s1, "%10.2e", y[j]); + else + sprintf(s1, "%10.*f", rep->Field[j].Precision, y[j]); + } + /*** End of update ***/ + + strcat(s, s1); + } + } + + /* Note if link is a pump or valve */ + if ((j = Link[i].Type) > EN_PIPE) { + strcat(s, " "); + strcat(s, LinkTxt[j]); + } + + /* Write results for link */ + writeline(pr, s); + } + } + writeline(pr, " "); +} + +void writeheader(EN_Project *pr, int type, int contin) +/* +**-------------------------------------------------------------- +** Input: type = table type +** contin = table continuation flag +** Output: none +** Purpose: writes column headings for output report tables +**-------------------------------------------------------------- +*/ +{ + report_options_t *rep = &pr->report; + quality_t *qu = &pr->quality; + parser_data_t *par = &pr->parser; + time_options_t *time = &pr->time_options; + + char s[MAXLINE + 1], s1[MAXLINE + 1], s2[MAXLINE + 1], s3[MAXLINE + 1]; + int i, n; + + /* Move to next page if < 11 lines remain on current page. */ + if (rep->Rptflag && rep->LineNum + 11 > (long)rep->PageSize) { + while (rep->LineNum < (long)rep->PageSize) + writeline(pr, " "); + } + writeline(pr, " "); + + /* Hydraulic Status Table */ + if (type == STATHDR) { + sprintf(s, FMT49); + if (contin) + strcat(s, t_CONTINUED); + writeline(pr, s); + fillstr(s, '-', 70); + writeline(pr, s); + } + + /* Energy Usage Table */ + if (type == ENERHDR) { + if (par->Unitsflag == SI) + strcpy(s1, t_perM3); + else + strcpy(s1, t_perMGAL); + sprintf(s, FMT71); + if (contin) + strcat(s, t_CONTINUED); + writeline(pr, s); + fillstr(s, '-', 63); + writeline(pr, s); + sprintf(s, FMT72); + writeline(pr, s); + sprintf(s, FMT73, s1); + writeline(pr, s); + fillstr(s, '-', 63); + writeline(pr, s); + } + + /* Node Results Table */ + if (type == NODEHDR) { + if (rep->Tstatflag == RANGE) + sprintf(s, FMT76, t_DIFFER); + else if (rep->Tstatflag != SERIES) + sprintf(s, FMT76, TstatTxt[rep->Tstatflag]); + else if (time->Dur == 0) + sprintf(s, FMT77); + else + sprintf(s, FMT78, clocktime(rep->Atime, time->Htime)); + if (contin) + strcat(s, t_CONTINUED); + writeline(pr, s); + n = 15; + sprintf(s2, "%15s", ""); + strcpy(s, t_NODEID); + sprintf(s3, "%-15s", s); + for (i = ELEV; i < QUALITY; i++) + if (rep->Field[i].Enabled == TRUE) { + n += 10; + sprintf(s, "%10s", rep->Field[i].Name); + strcat(s2, s); + sprintf(s, "%10s", rep->Field[i].Units); + strcat(s3, s); + } + if (rep->Field[QUALITY].Enabled == TRUE) { + n += 10; + sprintf(s, "%10s", qu->ChemName); + strcat(s2, s); + sprintf(s, "%10s", qu->ChemUnits); + strcat(s3, s); + } + fillstr(s1, '-', n); + writeline(pr, s1); + writeline(pr, s2); + writeline(pr, s3); + writeline(pr, s1); + } + + /* Link Results Table */ + if (type == LINKHDR) { + if (rep->Tstatflag == RANGE) + sprintf(s, FMT79, t_DIFFER); + else if (rep->Tstatflag != SERIES) + sprintf(s, FMT79, TstatTxt[rep->Tstatflag]); + else if (time->Dur == 0) + sprintf(s, FMT80); + else + sprintf(s, FMT81, clocktime(rep->Atime, time->Htime)); + if (contin) + strcat(s, t_CONTINUED); + writeline(pr, s); + n = 15; + sprintf(s2, "%15s", ""); + strcpy(s, t_LINKID); + sprintf(s3, "%-15s", s); + for (i = LENGTH; i <= FRICTION; i++) + if (rep->Field[i].Enabled == TRUE) { + n += 10; + sprintf(s, "%10s", rep->Field[i].Name); + strcat(s2, s); + sprintf(s, "%10s", rep->Field[i].Units); + strcat(s3, s); + } + fillstr(s1, '-', n); + writeline(pr, s1); + writeline(pr, s2); + writeline(pr, s3); + writeline(pr, s1); + } +} /* End of writeheader */ + +void writeline(EN_Project *pr, char *s) +/* +**-------------------------------------------------------------- +** Input: *s = text string +** Output: none +** Purpose: writes a line of output to report file +**-------------------------------------------------------------- +*/ +{ + report_options_t *rpt = &pr->report; + + if (rpt->RptFile == NULL) { + return; + } + if (rpt->Rptflag) { + if (rpt->LineNum == (long)rpt->PageSize) { + rpt->PageNum++; + if (fprintf(rpt->RptFile, FMT82, (int)rpt->PageNum, pr->Title[0]) == EOF) { + rpt->Fprinterr = TRUE; + } + rpt->LineNum = 3; + } + } + if (fprintf(rpt->RptFile, "\n %s", s) == EOF) { + rpt->Fprinterr = TRUE; + } + rpt->LineNum++; +} /* End of writeline */ + +void writerelerr(EN_Project *pr, int iter, double relerr) +/* +**----------------------------------------------------------------- +** Input: iter = current iteration of hydraulic solution +** relerr = current convergence error +** Output: none +** Purpose: writes out convergence status of hydraulic solution +**----------------------------------------------------------------- +*/ +{ + report_options_t *rep = &pr->report; + time_options_t *time = &pr->time_options; + + if (iter == 0) { + sprintf(pr->Msg, FMT64, clocktime(rep->Atime, time->Htime)); + writeline(pr, pr->Msg); + } else { + sprintf(pr->Msg, FMT65, iter, relerr); + writeline(pr, pr->Msg); + } +} /* End of writerelerr */ + +void writestatchange(EN_Project *pr, int k, char s1, char s2) +/* +**-------------------------------------------------------------- +** Input: k = link index +** s1 = old link status +** s2 = new link status +** Output: none +** Purpose: writes change in link status to output report +**-------------------------------------------------------------- +*/ +{ + EN_Network *net = &pr->network; + hydraulics_t *hyd = &pr->hydraulics; + Slink *Link = net->Link; + double *Ucf = pr->Ucf; + double *LinkSetting = hyd->LinkSetting; + + int j1, j2; + double setting; + + /* We have a pump/valve setting change instead of a status change */ + if (s1 == s2) { + + /*** Updated 10/25/00 ***/ + setting = LinkSetting[k]; // Link[k].Kc; + + switch (Link[k].Type) { + case EN_PRV: + case EN_PSV: + case EN_PBV: + setting *= Ucf[PRESSURE]; + break; + case EN_FCV: + setting *= Ucf[FLOW]; + default: + break; + } + sprintf(pr->Msg, FMT56, LinkTxt[Link[k].Type], Link[k].ID, setting); + writeline(pr, pr->Msg); + return; + } + + /* We have a status change. Write the old & new status types. */ + if (s1 == ACTIVE) + j1 = ACTIVE; + else if (s1 <= CLOSED) + j1 = CLOSED; + else + j1 = OPEN; + if (s2 == ACTIVE) + j2 = ACTIVE; + else if (s2 <= CLOSED) + j2 = CLOSED; + else + j2 = OPEN; + if (j1 != j2) { + sprintf(pr->Msg, FMT57, LinkTxt[Link[k].Type], Link[k].ID, StatTxt[j1], + StatTxt[j2]); + writeline(pr, pr->Msg); + } +} /* End of writestatchange */ + +void writecontrolaction(EN_Project *pr, int k, int i) +/* +---------------------------------------------------------------- +** Input: k = link index +** i = control index +** Output: none +** Purpose: writes control action taken to status report +**-------------------------------------------------------------- +*/ +{ + EN_Network *net = &pr->network; + report_options_t *rep = &pr->report; + time_options_t *time = &pr->time_options; + Snode *Node = net->Node; + Slink *Link = net->Link; + Scontrol *Control = net->Control; + + int n; + switch (Control[i].Type) { + case LOWLEVEL: + case HILEVEL: + n = Control[i].Node; + sprintf(pr->Msg, FMT54, clocktime(rep->Atime, time->Htime), LinkTxt[Link[k].Type], + Link[k].ID, NodeTxt[getnodetype(net,n)], Node[n].ID); + break; + case TIMER: + case TIMEOFDAY: + sprintf(pr->Msg, FMT55, clocktime(rep->Atime, time->Htime), LinkTxt[Link[k].Type], Link[k].ID); + break; + default: + return; + } + writeline(pr, pr->Msg); +} + +void writeruleaction(EN_Project *pr, int k, char *ruleID) +/* +**-------------------------------------------------------------- +** Input: k = link index +** *ruleID = rule ID +** Output: none +** Purpose: writes rule action taken to status report +**-------------------------------------------------------------- +*/ +{ + EN_Network *net = &pr->network; + report_options_t *rep = &pr->report; + time_options_t *time = &pr->time_options; + Slink *Link = net->Link; + + sprintf(pr->Msg, FMT63, clocktime(rep->Atime, time->Htime), LinkTxt[Link[k].Type], + Link[k].ID, ruleID); + writeline(pr, pr->Msg); +} + +int writehydwarn(EN_Project *pr, int iter, double relerr) +/* +**-------------------------------------------------------------- +** Input: iter = # iterations to find hydraulic solution +** Output: warning flag code +** Purpose: writes hydraulic warning message to report file +** +** Note: Warning conditions checked in following order: +** 1. System balanced but unstable +** 2. Negative pressures +** 3. FCV cannot supply flow or PRV/PSV cannot maintain pressure +** 4. Pump out of range +** 5. Network disconnected +** 6. System unbalanced +**-------------------------------------------------------------- +*/ +{ + EN_Network *net = &pr->network; + hydraulics_t *hyd = &pr->hydraulics; + report_options_t *rep = &pr->report; + time_options_t *time = &pr->time_options; + + Snode *Node = net->Node; + Slink *Link = net->Link; + Spump *Pump = net->Pump; + Svalve *Valve = net->Valve; + + const int Njuncs = net->Njuncs; + double *NodeDemand = hyd->NodeDemand; + double *LinkFlows = hyd->LinkFlows; + double *LinkSetting = hyd->LinkSetting; + + int i, j; + char flag = 0; + char s; + + /* Check if system unstable */ + if (iter > hyd->MaxIter && relerr <= hyd->Hacc) { + sprintf(pr->Msg, WARN02, clocktime(rep->Atime, time->Htime)); + if (rep->Messageflag) + writeline(pr, pr->Msg); + flag = 2; + } + + /* Check for negative pressures */ + for (i = 1; i <= Njuncs; i++) { + Snode *node = &Node[i]; + if (hyd->NodeHead[i] < node->El && NodeDemand[i] > 0.0) { + sprintf(pr->Msg, WARN06, clocktime(rep->Atime, time->Htime)); + if (rep->Messageflag) { + writeline(pr, pr->Msg); + } + flag = 6; + break; + } + } + + /* Check for abnormal valve condition */ + for (i = 1; i <= net->Nvalves; i++) { + j = Valve[i].Link; + if (hyd->LinkStatus[j] >= XFCV) { + sprintf(pr->Msg, WARN05, LinkTxt[Link[j].Type], Link[j].ID, + StatTxt[hyd->LinkStatus[j]], clocktime(rep->Atime, time->Htime)); + if (rep->Messageflag) + writeline(pr, pr->Msg); + flag = 5; + } + } + + /* Check for abnormal pump condition */ + for (i = 1; i <= net->Npumps; i++) { + j = Pump[i].Link; + s = hyd->LinkStatus[j]; + if (hyd->LinkStatus[j] >= OPEN) + { + if (LinkFlows[j] > LinkSetting[j] * Pump[i].Qmax) + s = XFLOW; + if (LinkFlows[j] < 0.0) + s = XHEAD; + } + if (s == XHEAD || s == XFLOW) + { + sprintf(pr->Msg, WARN04, Link[j].ID, StatTxt[s], + clocktime(rep->Atime, time->Htime)); + if (rep->Messageflag) + writeline(pr, pr->Msg); + flag = 4; + } + } + + /* Check if system is unbalanced */ + if (iter > hyd->MaxIter && relerr > hyd->Hacc) { + sprintf(pr->Msg, WARN01, clocktime(rep->Atime, time->Htime)); + if (hyd->ExtraIter == -1) + strcat(pr->Msg, t_HALTED); + if (rep->Messageflag) + writeline(pr, pr->Msg); + flag = 1; + } + + /* Check for disconnected network */ + /* & update global warning flag */ + if (flag > 0) { + disconnected(pr); + pr->Warnflag = flag; + } + return (flag); +} /* End of writehydwarn */ + +void writehyderr(EN_Project *pr, int errnode) +/* +**----------------------------------------------------------- +** Input: none +** Output: none +** Purpose: outputs status & checks connectivity when +** network hydraulic equations cannot be solved. +**----------------------------------------------------------- +*/ +{ + EN_Network *net = &pr->network; + report_options_t *rep = &pr->report; + time_options_t *time = &pr->time_options; + Snode *Node = net->Node; + + sprintf(pr->Msg, FMT62, clocktime(rep->Atime, time->Htime), Node[errnode].ID); + if (rep->Messageflag) + writeline(pr, pr->Msg); + writehydstat(pr, 0, 0); + disconnected(pr); +} /* End of writehyderr */ + +int disconnected(EN_Project *pr) +/* +**------------------------------------------------------------------- +** Input: None +** Output: Returns number of disconnected nodes +** Purpose: Tests current hydraulic solution to see if any closed +** links have caused the network to become disconnected. +**------------------------------------------------------------------- +*/ +{ + EN_Network *net = &pr->network; + hydraulics_t *hyd = &pr->hydraulics; + report_options_t *rep = &pr->report; + time_options_t *time = &pr->time_options; + + int i, j; + int count, mcount; + int errcode = 0; + int *nodelist; + char *marked; + + /* Allocate memory for node list & marked list */ + nodelist = (int *)calloc(net->Nnodes + 1, sizeof(int)); + marked = (char *)calloc(net->Nnodes + 1, sizeof(char)); + ERRCODE(MEMCHECK(nodelist)); + ERRCODE(MEMCHECK(marked)); + if (errcode) + return (0); + + /* Place tanks on node list and marked list */ + for (i = 1; i <= net->Ntanks; i++) { + j = net->Njuncs + i; + nodelist[i] = j; + marked[j] = 1; + } + + /* Place junctions with negative demands on the lists */ + mcount = net->Ntanks; + for (i = 1; i <= net->Njuncs; i++) { + if (hyd->NodeDemand[i] < 0.0) { + mcount++; + nodelist[mcount] = i; + marked[i] = 1; + } + } + + /* Mark all nodes that can be connected to tanks */ + /* and count number of nodes remaining unmarked. */ + marknodes(pr, mcount, nodelist, marked); + j = 0; + count = 0; + for (i = 1; i <= net->Njuncs; i++) { + Snode *node = &net->Node[i]; + if (!marked[i] && hyd->NodeDemand[i] != 0.0) { + count++; + if (count <= MAXCOUNT && rep->Messageflag) { + sprintf(pr->Msg, WARN03a, node->ID, clocktime(rep->Atime, time->Htime)); + writeline(pr, pr->Msg); + } + j = i; /* Last unmarked node */ + } + } + + /* Report number of unmarked nodes and find closed link */ + /* on path from node j back to a tank. */ + if (count > 0 && rep->Messageflag) { + if (count > MAXCOUNT) { + sprintf(pr->Msg, WARN03b, count - MAXCOUNT, clocktime(rep->Atime, time->Htime)); + writeline(pr, pr->Msg); + } + getclosedlink(pr, j, marked); + } + + /* Free allocated memory */ + free(nodelist); + free(marked); + return (count); +} /* End of disconnected() */ + +void marknodes(EN_Project *pr, int m, int *nodelist, char *marked) +/* +**---------------------------------------------------------------- +** Input: m = number of source nodes +** nodelist[] = list of nodes to be traced from +** marked[] = TRUE if node connected to source +** Output: None. +** Purpose: Marks all junction nodes connected to tanks. +**---------------------------------------------------------------- +*/ +{ + EN_Network *net = &pr->network; + hydraulics_t *hyd = &pr->hydraulics; + + int i, j, k, n; + Padjlist alink; + + /* Scan each successive entry of node list */ + n = 1; + while (n <= m) { + + /* Scan all nodes connected to current node */ + i = nodelist[n]; + for (alink = net->Adjlist[i]; alink != NULL; alink = alink->next) { + + /* Get indexes of connecting link and node */ + k = alink->link; + j = alink->node; + if (marked[j]) { + continue; + } + + /* Check if valve connection is in correct direction */ + switch (net->Link[k].Type) { + case EN_CVPIPE: + case EN_PRV: + case EN_PSV: + if (j == net->Link[k].N1) { + continue; + } + default: + break; + } + + /* Mark connection node if link not closed */ + if (hyd->LinkStatus[k] > CLOSED) { + marked[j] = 1; + m++; + nodelist[m] = j; + } + } + n++; + } +} /* End of marknodes() */ + +void getclosedlink(EN_Project *pr, int i, char *marked) +/* +**---------------------------------------------------------------- +** Input: i = junction index +** marked[] = marks nodes already examined +** Output: None. +** Purpose: Determines if a closed link connects to junction i. +**---------------------------------------------------------------- +*/ +{ + EN_Network *net = &pr->network; + + int j, k; + Padjlist alink; + marked[i] = 2; + for (alink = net->Adjlist[i]; alink != NULL; alink = alink->next) { + k = alink->link; + j = alink->node; + if (marked[j] == 2) + continue; + if (marked[j] == 1) { + sprintf(pr->Msg, WARN03c, net->Link[k].ID); + writeline(pr, pr->Msg); + return; + } else + getclosedlink(pr, j, marked); + } +} + +void writelimits(EN_Project *pr, int j1, int j2) +/* +**-------------------------------------------------------------- +** Input: j1 = index of first output variable +** j2 = index of last output variable +** Output: none +** Purpose: writes reporting criteria to output report +**-------------------------------------------------------------- +*/ +{ + report_options_t *rep = &pr->report; + + int j; + for (j = j1; j <= j2; j++) { + if (rep->Field[j].RptLim[LOW] < BIG) { + sprintf(pr->Msg, FMT47, rep->Field[j].Name, rep->Field[j].RptLim[LOW], + rep->Field[j].Units); + writeline(pr, pr->Msg); + } + if (rep->Field[j].RptLim[HI] > -BIG) { + sprintf(pr->Msg, FMT48, rep->Field[j].Name, rep->Field[j].RptLim[HI], + rep->Field[j].Units); + writeline(pr, pr->Msg); + } + } +} /* End of writelimits */ + +int checklimits(report_options_t *rep, double *y, int j1, int j2) +/* +**-------------------------------------------------------------- +** Input: *y = array of output results +** j1 = index of first output variable +** j2 = index of last output variable +** Output: returns 1 if criteria met, 0 otherwise +** Purpose: checks if output reporting criteria is met +**-------------------------------------------------------------- +*/ +{ + int j; + for (j = j1; j <= j2; j++) { + if (y[j] > rep->Field[j].RptLim[LOW] || y[j] < rep->Field[j].RptLim[HI]) + return (0); + } + return (1); +} /* End of checklim */ + +void writetime(EN_Project *pr, char *fmt) +/* +**---------------------------------------------------------------- +** Input: fmt = format string +** Output: none +** Purpose: writes starting/ending time of a run to report file +**---------------------------------------------------------------- +*/ +{ + time_t timer; + time(&timer); + sprintf(pr->Msg, fmt, ctime(&timer)); + writeline(pr, pr->Msg); +} + +char *clocktime(char *atime, long seconds) +/* +**-------------------------------------------------------------- +** Input: seconds = time in seconds +** Output: atime = time in hrs:min +** (returns pointer to atime) +** Purpose: converts time in seconds to hours:minutes format +**-------------------------------------------------------------- +*/ +{ + /*** Updated 6/24/02 ***/ + long h, m, s; + h = seconds / 3600; + m = seconds % 3600 / 60; + s = seconds - 3600 * h - 60 * m; + sprintf(atime, "%01d:%02d:%02d", (int)h, (int)m, (int)s); + return (atime); +} /* End of clocktime */ + +char *fillstr(char *s, char ch, int n) +/* +**--------------------------------------------------------- +** Fills n bytes of s to character ch. +** NOTE: does not check for overwriting s. +**--------------------------------------------------------- +*/ +{ + int i; + for (i = 0; i <= n; i++) + s[i] = ch; + s[n + 1] = '\0'; + return (s); +} + +int getnodetype(EN_Network *net, int i) +/* +**--------------------------------------------------------- +** Determines type of node with index i +** (junction = 0, reservoir = 1, tank = 2). +**--------------------------------------------------------- +*/ +{ + if (i <= net->Njuncs) + return (0); + if (net->Tank[i - net->Njuncs].A == 0.0) + return (1); + return (2); +} + +/********************* END OF REPORT.C ********************/ diff --git a/src/rules.c b/src/rules.c old mode 100755 new mode 100644 index 2d94533..39d6a6a --- a/src/rules.c +++ b/src/rules.c @@ -1,1154 +1,1419 @@ -/* -********************************************************************** - -RULES.C -- Rule processor module for EPANET - -VERSION: 2.00 -DATE: 5/8/00 - 9/7/00 - 10/25/00 - 3/1/01 - 8/15/07 (2.00.11) -AUTHOR: L. Rossman - US EPA - NRMRL - - The entry points for this module are: - initrules() -- called from ENopen() in EPANET.C - addrule() -- called from netsize() in INPUT2.C - allocrules() -- called from allocdata() in EPANET.C - ruledata() -- called from newline() in INPUT2.C - freerules() -- called from freedata() in EPANET.C - checkrules() -- called from ruletimestep() in HYDRAUL.C - -********************************************************************** -*/ -#include -#include -#ifndef __APPLE__ -#include -#else -#include -#endif -#include "hash.h" -#include "text.h" -#include "types.h" -#include "funcs.h" -#define EXTERN extern -#include "vars.h" - - -enum Rulewords {r_RULE,r_IF,r_AND,r_OR,r_THEN,r_ELSE,r_PRIORITY,r_ERROR}; -char *Ruleword[] = {w_RULE,w_IF,w_AND,w_OR,w_THEN,w_ELSE,w_PRIORITY,NULL}; - -enum Varwords {r_DEMAND, r_HEAD, r_GRADE, r_LEVEL, r_PRESSURE, - r_FLOW, r_STATUS, r_SETTING, r_POWER, r_TIME, - r_CLOCKTIME, r_FILLTIME, r_DRAINTIME}; -char *Varword[] = {w_DEMAND, w_HEAD, w_GRADE, w_LEVEL, w_PRESSURE, - w_FLOW, w_STATUS, w_SETTING, w_POWER,w_TIME, - w_CLOCKTIME,w_FILLTIME,w_DRAINTIME, NULL}; - -enum Objects {r_JUNC,r_RESERV,r_TANK,r_PIPE,r_PUMP,r_VALVE, - r_NODE,r_LINK,r_SYSTEM}; -char *Object[] = {w_JUNC,w_RESERV,w_TANK,w_PIPE,w_PUMP,w_VALVE, - w_NODE,w_LINK,w_SYSTEM,NULL}; - -/* NOTE: place "<=" & ">=" before "<" & ">" so that findmatch() works correctly. */ -enum Operators { EQ, NE, LE, GE, LT, GT, IS, NOT, BELOW, ABOVE}; -char *Operator[] = {"=","<>","<=",">=","<",">",w_IS,w_NOT,w_BELOW,w_ABOVE,NULL}; - -enum Values {IS_NUMBER,IS_OPEN,IS_CLOSED,IS_ACTIVE}; -char *Value[] = {"XXXX", w_OPEN, w_CLOSED, w_ACTIVE,NULL}; - -/* External variables declared in INPUT2.C */ -extern char *Tok[MAXTOKS]; -extern int Ntokens; - -extern char *StatTxt[]; - -/* -** Local function prototypes are defined here and not in FUNCS.H -** because some of them utilize the Premise and Action structures -** defined locally in this module. -*/ -void newrule(void); -int newpremise(int); -int newaction(void); -int newpriority(void); -int evalpremises(int); -void updateactlist(int, struct Action *); -int checkaction(int, struct Action *); -int checkpremise(struct Premise *); -int checktime(struct Premise *); -int checkstatus(struct Premise *); -int checkvalue(struct Premise *); -int takeactions(void); -void clearactlist(void); -void clearrules(void); -void ruleerrmsg(int); -int writeRuleinInp(FILE *f, int RuleIdx); - -void initrules() -/* -**-------------------------------------------------------------- -** Initializes rule base. -** Called by ENopen() in EPANET.C module -**-------------------------------------------------------------- -*/ -{ - RuleState = r_PRIORITY; - Rule = NULL; -} - - -void addrule(char *tok) -/* -**-------------------------------------------------------------- -** Updates rule count if RULE keyword found in line of input. -** Called by netsize() in INPUT2.C module. -**-------------------------------------------------------------- -*/ -{ - if (match(tok,w_RULE)) MaxRules++; -} - - -int allocrules() -/* -**-------------------------------------------------------------- -** Allocates memory for rule-based controls. -** Called by allocdata() in EPANET.C module. -**-------------------------------------------------------------- -*/ -{ - Rule = (struct aRule *) calloc(MaxRules+1,sizeof(struct aRule)); - if (Rule == NULL) return(101); - else return(0); -} - - -void freerules() -/* -**-------------------------------------------------------------- -** Frees memory used for rule-based controls. -** Called by freedata() in EPANET.C module. -**-------------------------------------------------------------- -*/ -{ - clearrules(); - free(Rule); -} - - -int checkrules(long dt) -/* -**----------------------------------------------------- -** Checks which rules should fire at current time. -** Called by ruletimestep() in HYDRAUL.C. -**----------------------------------------------------- -*/ -{ - int i, - r; /* Number of actions actually taken */ - - /* Start of rule evaluation time interval */ - Time1 = Htime - dt + 1; - - /* Iterate through each rule */ - ActList = NULL; - r = 0; - for (i=1; i<=Nrules; i++) - { - /* If premises true, add THEN clauses to action list. */ - if (evalpremises(i) == TRUE) updateactlist(i,Rule[i].Tchain); - - /* If premises false, add ELSE actions to list. */ - else - { - if (Rule[i].Fchain != NULL) updateactlist(i,Rule[i].Fchain); - } - } - - /* Execute actions then clear list. */ - if (ActList != NULL) r = takeactions(); - clearactlist(); - return(r); -} - - -int ruledata() -/* -**-------------------------------------------------------------- -** Parses a line from [RULES] section of input. -** Called by newline() in INPUT2.C module. -** Tok[] is global array of tokens parsed from input line. -**-------------------------------------------------------------- -*/ -{ - int key, /* Keyword code */ - err; - - /* Exit if current rule has an error */ - if (RuleState == r_ERROR) return(0); - - /* Find the key word that begins the rule statement */ - err = 0; - key = findmatch(Tok[0],Ruleword); - switch (key) - { - case -1: err = 201; /* Unrecognized keyword */ - break; - case r_RULE: Nrules++; - newrule(); - RuleState = r_RULE; - break; - case r_IF: if (RuleState != r_RULE) - { - err = 221; /* Mis-placed IF clause */ - break; - } - RuleState = r_IF; - err = newpremise(r_AND); - break; - case r_AND: if (RuleState == r_IF) err = newpremise(r_AND); - else if (RuleState == r_THEN || RuleState == r_ELSE) - err = newaction(); - else err = 221; - break; - case r_OR: if (RuleState == r_IF) err = newpremise(r_OR); - else err = 221; - break; - case r_THEN: if (RuleState != r_IF) - { - err = 221; /* Mis-placed THEN clause */ - break; - } - RuleState = r_THEN; - err = newaction(); - break; - case r_ELSE: if (RuleState != r_THEN) - { - err = 221; /* Mis-placed ELSE clause */ - break; - } - RuleState = r_ELSE; - err = newaction(); - break; - case r_PRIORITY: if (RuleState != r_THEN && RuleState != r_ELSE) - { - err = 221; - break; - } - RuleState = r_PRIORITY; - err = newpriority(); - break; - default: err = 201; - } - - /* Set RuleState to r_ERROR if errors found */ - if (err) - { - RuleState = r_ERROR; - ruleerrmsg(err); - err = 200; - } - return(err); -} - - -void clearactlist() -/* -**---------------------------------------------------------- -** Clears memory used for action list -**---------------------------------------------------------- -*/ -{ - struct ActItem *a; - struct ActItem *anext; - a = ActList; - while (a != NULL) - { - anext = a->next; - free(a); - a = anext; - } -} - - -void clearrules() -/* -**----------------------------------------------------------- -** Clears memory used for premises & actions for all rules -**----------------------------------------------------------- -*/ -{ - struct Premise *p; - struct Premise *pnext; - struct Action *a; - struct Action *anext; - int i; - for (i=1; i<=Nrules; i++) - { - p = Rule[i].Pchain; - while (p != NULL) - { - pnext = p->next; - free(p); - p = pnext; - } - a = Rule[i].Tchain; - while (a != NULL) - { - anext = a->next; - free(a); - a = anext; - } - a = Rule[i].Fchain; - while (a != NULL) - { - anext = a->next; - free(a); - a = anext; - } - } -} - - -void newrule() -/* -**---------------------------------------------------------- -** Adds new rule to rule base -**---------------------------------------------------------- -*/ -{ - strncpy(Rule[Nrules].label, Tok[1], MAXID); - Rule[Nrules].Pchain = NULL; - Rule[Nrules].Tchain = NULL; - Rule[Nrules].Fchain = NULL; - Rule[Nrules].priority = 0.0; - Plast = NULL; - Tlast = NULL; - Flast = NULL; -} - - -int newpremise(int logop) -/* -**-------------------------------------------------------------------- -** Adds new premise to current rule. -** Formats are: -** IF/AND/OR -** IF/AND/OR SYSTEM (units) -** -** Calls findmatch() and hour() in INPUT2.C. -** Calls findnode() and findlink() in EPANET.C. -**--------------------------------------------------------------------- -*/ -{ - int i,j,k,m,r,s,v; - double x; - struct Premise *p; - - /* Check for correct number of tokens */ - if (Ntokens != 5 && Ntokens != 6) return(201); - - /* Find network object & id if present */ - i = findmatch(Tok[1],Object); - if (i == r_SYSTEM) - { - j = 0; - v = findmatch(Tok[2],Varword); - if (v != r_DEMAND && v != r_TIME && v != r_CLOCKTIME) return(201); - } - else - { - v = findmatch(Tok[3],Varword); - if (v < 0) return(201); - switch (i) - { - case r_NODE: - case r_JUNC: - case r_RESERV: - case r_TANK: k = r_NODE; break; - case r_LINK: - case r_PIPE: - case r_PUMP: - case r_VALVE: k = r_LINK; break; - default: return(201); - } - i = k; - if (i == r_NODE) - { - j = findnode(Tok[2]); - if (j == 0) return(203); - switch (v) - { - case r_DEMAND: - case r_HEAD: - case r_GRADE: - case r_LEVEL: - case r_PRESSURE: break; - -/*** Updated 9/7/00 ***/ - case r_FILLTIME: - case r_DRAINTIME: if (j <= Njuncs) return(201); break; - - default: return(201); - } - } - else - { - j = findlink(Tok[2]); - if (j == 0) return(204); - switch (v) - { - case r_FLOW: - case r_STATUS: - case r_SETTING: break; - default: return(201); - } - } - } - - /* Parse relational operator (r) and check for synonyms */ - if (i == r_SYSTEM) m = 3; - else m = 4; - k = findmatch(Tok[m],Operator); - if (k < 0) return(201); - switch(k) - { - case IS: r = EQ; break; - case NOT: r = NE; break; - case BELOW: r = LT; break; - case ABOVE: r = GT; break; - default: r = k; - } - - /* Parse for status (s) or numerical value (x) */ - s = 0; - x = MISSING; - if (v == r_TIME || v == r_CLOCKTIME) - { - if (Ntokens == 6) - x = hour(Tok[4],Tok[5])*3600.; - else - x = hour(Tok[4],"")*3600.; - if (x < 0.0) return(202); - } - else if ((k = findmatch(Tok[Ntokens-1],Value)) > IS_NUMBER) s = k; - else - { - if (!getfloat(Tok[Ntokens-1],&x)) return(202); - if (v == r_FILLTIME || v == r_DRAINTIME) x = x*3600.0; //(2.00.11 - LR) - } - - - - /* Create new premise structure */ - p = (struct Premise *) malloc(sizeof(struct Premise)); - if (p == NULL) return(101); - p->object = i; - p->index = j; - p->variable = v; - p->relop = r; - p->logop = logop; - p->status = s; - p->value = x; - - /* Add premise to current rule's premise list */ - p->next = NULL; - if (Plast == NULL) Rule[Nrules].Pchain = p; - else Plast->next = p; - Plast = p; - return(0); -} - - -int newaction() -/* -**---------------------------------------------------------- -** Adds new action to current rule. -** Format is: -** THEN/ELSE/AND LINK IS -** -** Calls findlink() from EPANET.C. -** Calls getfloat() and findmatch() from INPUT2.C. -**---------------------------------------------------------- -*/ -{ - int j,k,s; - double x; - struct Action *a; - - /* Check for correct number of tokens */ - if (Ntokens != 6) return(201); - - /* Check that link exists */ - j = findlink(Tok[2]); - if (j == 0) return(204); - -/*** Updated 9/7/00 ***/ - /* Cannot control a CV */ - if (Link[j].Type == CV) return(207); - - /* Find value for status or setting */ - s = -1; - x = MISSING; - if ((k = findmatch(Tok[5],Value)) > IS_NUMBER) s = k; - else - { - if (!getfloat(Tok[5],&x)) return(202); - if (x < 0.0) return(202); - } - -/*** Updated 9/7/00 ***/ - /* Cannot change setting for a GPV ***/ - if (x != MISSING && Link[j].Type == GPV) return(202); - -/*** Updated 3/1/01 ***/ - /* Set status for pipe in case setting was specified */ - if (x != MISSING && Link[j].Type == PIPE) - { - if (x == 0.0) s = IS_CLOSED; - else s = IS_OPEN; - x = MISSING; - } - - /* Create a new action structure */ - a = (struct Action *) malloc(sizeof(struct Action)); - if (a == NULL) return(101); - a->link = j; - a->status = s; - a->setting = x; - - /* Add action to current rule's action list */ - if (RuleState == r_THEN) - { - a->next = NULL; - if (Tlast == NULL) Rule[Nrules].Tchain = a; - else Tlast->next = a; - Tlast = a; - } - else - { - a->next = NULL; - if (Flast == NULL) Rule[Nrules].Fchain = a; - else Flast->next = a; - Flast = a; - } - return(0); -} - - -int newpriority() -/* -**--------------------------------------------------- -** Adds priority rating to current rule -**--------------------------------------------------- -*/ -{ - double x; - if (!getfloat(Tok[1],&x)) return(202); - Rule[Nrules].priority = x; - return(0); -} - - -int evalpremises(int i) -/* -**---------------------------------------------------------- -** Checks if premises to rule i are true -**---------------------------------------------------------- -*/ -{ - int result; - struct Premise *p; - - result = TRUE; - p = Rule[i].Pchain; - while (p != NULL) - { - if (p->logop == r_OR) - { - if (result == FALSE) - { - result = checkpremise(p); - } - } - else - { - if (result == FALSE) return(FALSE); - result = checkpremise(p); - } - p = p->next; - } - return(result); -} - - -int checkpremise(struct Premise *p) -/* -**---------------------------------------------------------- -** Checks if a particular premise is true -**---------------------------------------------------------- -*/ -{ - if (p->variable == r_TIME || p->variable == r_CLOCKTIME) - return(checktime(p)); - else if (p->status > IS_NUMBER) - return(checkstatus(p)); - else - return(checkvalue(p)); -} - - -int checktime(struct Premise *p) -/* -**------------------------------------------------------------ -** Checks if condition on system time holds -**------------------------------------------------------------ -*/ -{ - char flag; - long t1,t2,x; - - /* Get start and end of rule evaluation time interval */ - if (p->variable == r_TIME) - { - t1 = Time1; - t2 = Htime; - } - else if (p->variable == r_CLOCKTIME) - { - t1 = (Time1 + Tstart) % SECperDAY; - t2 = (Htime + Tstart) % SECperDAY; - } - else return(0); - - /* Test premise's time */ - x = (long)(p->value); - switch (p->relop) - { - /* For inequality, test against current time */ - case LT: if (t2 >= x) return(0); break; - case LE: if (t2 > x) return(0); break; - case GT: if (t2 <= x) return(0); break; - case GE: if (t2 < x) return(0); break; - - /* For equality, test if within interval */ - case EQ: - case NE: - flag = FALSE; - if (t2 < t1) /* E.g., 11:00 am to 1:00 am */ - { - if (x >= t1 || x <= t2) flag = TRUE; - } - else - { - if (x >= t1 && x <= t2) flag = TRUE; - } - if (p->relop == EQ && flag == FALSE) return(0); - if (p->relop == NE && flag == TRUE) return(0); - break; - } - - /* If we get to here then premise was satisfied */ - return(1); -} - - -int checkstatus(struct Premise *p) -/* -**------------------------------------------------------------ -** Checks if condition on link status holds -**------------------------------------------------------------ -*/ -{ - char i; - int j; - switch (p->status) - { - case IS_OPEN: - case IS_CLOSED: - case IS_ACTIVE: - i = LinkStatus[p->index]; - if (i <= CLOSED) j = IS_CLOSED; - else if (i == ACTIVE) j = IS_ACTIVE; - else j = IS_OPEN; - if (j == p->status && - p->relop == EQ) return(1); - if (j != p->status && - p->relop == NE) return(1); - } - return(0); -} - - -int checkvalue(struct Premise *p) -/* -**---------------------------------------------------------- -** Checks if numerical condition on a variable is true. -** Uses tolerance of 0.001 when testing conditions. -**---------------------------------------------------------- -*/ -{ - int i,j,v; - double x, - tol = 1.e-3; - - i = p->index; - v = p->variable; - switch (v) - { - -/*** Updated 10/25/00 ***/ - case r_DEMAND: if (p->object == r_SYSTEM) x = Dsystem*Ucf[DEMAND]; - else x = NodeDemand[i]*Ucf[DEMAND]; - break; - - case r_HEAD: - case r_GRADE: x = NodeHead[i]*Ucf[HEAD]; - break; - case r_PRESSURE: x = (NodeHead[i]-Node[i].El)*Ucf[PRESSURE]; - break; - case r_LEVEL: x = (NodeHead[i]-Node[i].El)*Ucf[HEAD]; - break; - case r_FLOW: x = ABS(Q[i])*Ucf[FLOW]; - break; - case r_SETTING: if (LinkSetting[i] == MISSING) return(0); - x = LinkSetting[i]; - switch (Link[i].Type) - { - case PRV: - case PSV: - case PBV: x = x*Ucf[PRESSURE]; break; - case FCV: x = x*Ucf[FLOW]; break; - } - break; - case r_FILLTIME: if (i <= Njuncs) return(0); - j = i-Njuncs; - if (Tank[j].A == 0.0) return(0); - if (NodeDemand[i] <= TINY) return(0); - x = (Tank[j].Vmax - Tank[j].V)/NodeDemand[i]; - break; - case r_DRAINTIME: if (i <= Njuncs) return(0); - j = i-Njuncs; - if (Tank[j].A == 0.0) return(0); - if (NodeDemand[i] >= -TINY) return(0); - x = (Tank[j].Vmin - Tank[j].V)/NodeDemand[i]; - break; - default: return(0); - } - switch (p->relop) - { - case EQ: if (ABS(x - p->value) > tol) return(0); - break; - case NE: if (ABS(x - p->value) < tol) return(0); - break; - case LT: if (x > p->value + tol) return(0); break; - case LE: if (x > p->value - tol) return(0); break; - case GT: if (x < p->value - tol) return(0); break; - case GE: if (x < p->value + tol) return(0); break; - } - return(1); -} - - -void updateactlist(int i, struct Action *actions) -/* -**--------------------------------------------------- -** Adds rule's actions to action list -**--------------------------------------------------- -*/ -{ - struct ActItem *item; - struct Action *a; - - /* Iterate through each action of Rule i */ - a = actions; - while (a != NULL) - { - /* Add action to list if not already on it */ - if (!checkaction(i,a)) - { - item = (struct ActItem *) malloc(sizeof(struct ActItem)); - if (item != NULL) - { - item->action = a; - item->ruleindex = i; - item->next = ActList; - ActList = item; - } - } - a = a->next; - } -} - - -int checkaction(int i, struct Action *a) -/* -**----------------------------------------------------------- -** Checks if an action is already on the Action List -**----------------------------------------------------------- -*/ -{ - int i1,k,k1; - struct ActItem *item; - struct Action *a1; - - /* Search action list for link named in action */ - k = a->link; /* Action applies to link k */ - item = ActList; - while (item != NULL) - { - a1 = item->action; - i1 = item->ruleindex; - k1 = a1->link; - - /* If link on list then replace action if rule has higher priority. */ - if (k1 == k) - { - if (Rule[i].priority > Rule[i1].priority) - { - item->action = a; - item->ruleindex = i; - } - return(1); - } - item = item->next; - } - return(0); -} - - -int takeactions() -/* -**----------------------------------------------------------- -** Implements actions on action list -**----------------------------------------------------------- -*/ -{ - struct Action *a; - struct ActItem *item; - char flag; - int k, s, n; - double tol = 1.e-3, - v, x; - - n = 0; - item = ActList; - while (item != NULL) - { - flag = FALSE; - a = item->action; - k = a->link; - s = LinkStatus[k]; - v = LinkSetting[k]; - x = a->setting; - - /* Switch link from closed to open */ - if (a->status == IS_OPEN && s <= CLOSED) - { - setlinkstatus(k, 1, &LinkStatus[k], &LinkSetting[k]); - flag = TRUE; - } - - /* Switch link from not closed to closed */ - else if (a->status == IS_CLOSED && s > CLOSED) - { - setlinkstatus(k, 0, &LinkStatus[k], &LinkSetting[k]); - flag = TRUE; - } - - /* Change link's setting */ - else if (x != MISSING) - { - switch(Link[k].Type) - { - case PRV: - case PSV: - case PBV: x = x/Ucf[PRESSURE]; break; - case FCV: x = x/Ucf[FLOW]; break; - } - if (ABS(x-v) > tol) - { - setlinksetting(k, x, &LinkStatus[k], &LinkSetting[k]); - flag = TRUE; - } - } - - /* Report rule action */ - if (flag == TRUE) - { - n++; - if (Statflag) writeruleaction(k,Rule[item->ruleindex].label); - } - - /* Move to next action on list */ - item = item->next; - } - return(n); -} - - -void ruleerrmsg(int err) -/* -**----------------------------------------------------------- -** Reports error message -**----------------------------------------------------------- -*/ -{ - int i; - char label[81]; - char fmt[256]; - switch (err) - { - case 201: strcpy(fmt,R_ERR201); break; - case 202: strcpy(fmt,R_ERR202); break; - case 203: strcpy(fmt,R_ERR203); break; - case 204: strcpy(fmt,R_ERR204); break; - -/*** Updated on 9/7/00 ***/ - case 207: strcpy(fmt,R_ERR207); break; - - case 221: strcpy(fmt,R_ERR221); break; - default: return; - } - if (Nrules > 0) - { - strcpy(label,t_RULE); - strcat(label," "); - strcat(label,Rule[Nrules].label); - } - else strcpy(label,t_RULES_SECT); - sprintf(Msg,fmt); - strcat(Msg,label); - strcat(Msg,":"); - writeline(Msg); - strcpy(fmt,Tok[0]); - for (i=1; ivalue==MISSING) - { - fprintf(f, "\nIF "); - if ((strncmp(Object[p->object], "NODE", 4)==0) || (strncmp(Object[p->object], "Junc", 4)==0) || (strncmp(Object[p->object], "Reser", 5)==0) || (strncmp(Object[p->object], "Tank", 4)==0) ) - { - if (p->index <= Njuncs) fprintf(f,"JUNCTION %s %s %s %s", Node[p->index].ID, Varword[p->variable], Operator[p->relop], Value[p->status]); - else if (Tank[p->index-Njuncs].A == 0.0) fprintf(f,"RESERVOIR %s %s %s %s", Node[p->index].ID, Varword[p->variable], Operator[p->relop], Value[p->status]); - else fprintf(f,"TANK %s %s %s %s", Node[p->index].ID, Varword[p->variable], Operator[p->relop], Value[p->status]); - } - else - { //it is a link - if (Link[p->index].Type == PIPE || Link[p->index].Type == CV) fprintf(f,"PIPE %s %s %s %s", Link[p->index].ID, Varword[p->variable], Operator[p->relop], Value[p->status]); - else if (Link[p->index].Type == PUMP) fprintf(f,"PUMP %s %s %s %s", Link[p->index].ID, Varword[p->variable], Operator[p->relop], Value[p->status]); - else fprintf(f,"VALVE %s %s %s %s", Link[p->index].ID, Varword[p->variable], Operator[p->relop], Value[p->status]); - } - } - else - { - if (p->variable == r_TIME) - { - hours = (int) p->value / 3600; - minutes = (int) ((p->value - 3600*hours)/60); - seconds = (int) (p->value - 3600*hours - minutes*60); - fprintf(f, "\nIF %s %s %s %d:%02d:%02d", Object[p->object], Varword[p->variable], Operator[p->relop], hours, minutes, seconds); - } - else - { - if (p->variable == r_CLOCKTIME) - { - hours = (int) p->value / 3600; - minutes = (int) ((p->value - 3600*hours)/60); - seconds = (int) (p->value - 3600*hours - minutes*60); - - if (hours < 12) fprintf(f, "\nIF %s %s %s %d:%02d:%02d AM", Object[p->object], Varword[p->variable], Operator[p->relop], hours, minutes, seconds); - else fprintf(f, "\nIF %s %s %s %d:%02d:%02d PM", Object[p->object], Varword[p->variable], Operator[p->relop], hours-12, minutes, seconds); - } - else - { - if (p->variable == r_FILLTIME || p->variable == r_DRAINTIME) fprintf(f, "\nIF TANK %s %s %s %.4lf", Node[p->index].ID, Varword[p->variable], Operator[p->relop], p->value/3600.0); - else - { - fprintf(f, "\nIF "); - if ((strncmp(Object[p->object], "NODE", 4)==0) || (strncmp(Object[p->object], "Junc", 4)==0) || (strncmp(Object[p->object], "Reser", 5)==0) || (strncmp(Object[p->object], "Tank", 4)==0)) - { - if (p->index <= Njuncs) fprintf(f,"JUNCTION %s %s %s %.4lf", Node[p->index].ID, Varword[p->variable], Operator[p->relop], p->value); - else if (Tank[p->index-Njuncs].A == 0.0) fprintf(f,"RESERVOIR %s %s %s %.4lf", Node[p->index].ID, Varword[p->variable], Operator[p->relop], p->value); - else fprintf(f,"TANK %s %s %s %.4lf", Node[p->index].ID, Varword[p->variable], Operator[p->relop], p->value); - } - else - { //it is a link - if (Link[p->index].Type == PIPE || Link[p->index].Type == CV) fprintf(f,"PIPE %s %s %s %.4lf", Link[p->index].ID, Varword[p->variable], Operator[p->relop], p->value); - else if (Link[p->index].Type == PUMP) fprintf(f,"PUMP %s %s %s %.4lf", Link[p->index].ID, Varword[p->variable], Operator[p->relop], p->value); - else fprintf(f,"VALVE %s %s %s %.4lf", Link[p->index].ID, Varword[p->variable], Operator[p->relop], p->value); - } - } - } - } - } - - p = p->next; - while (p != NULL) //for all other premises/conditions write the corresponding logicOperator - { - if (p->value==MISSING) - { - fprintf(f, "\n%s ", Ruleword[p->logop]); - if ((strncmp(Object[p->object], "NODE", 4)==0) || (strncmp(Object[p->object], "Junc", 4)==0) || (strncmp(Object[p->object], "Reser", 5)==0) || (strncmp(Object[p->object], "Tank", 4)==0)) - { - if (p->index <= Njuncs) fprintf(f,"JUNCTION %s %s %s %s", Node[p->index].ID, Varword[p->variable], Operator[p->relop], Value[p->status]); - else if (Tank[p->index-Njuncs].A == 0.0) fprintf(f,"RESERVOIR %s %s %s %s", Node[p->index].ID, Varword[p->variable], Operator[p->relop], Value[p->status]); - else fprintf(f,"TANK %s %s %s %s", Node[p->index].ID, Varword[p->variable], Operator[p->relop], Value[p->status]); - } - else - { //it is a link - if (Link[p->index].Type == PIPE || Link[p->index].Type == CV) fprintf(f,"PIPE %s %s %s %s", Link[p->index].ID, Varword[p->variable], Operator[p->relop], Value[p->status]); - else if (Link[p->index].Type == PUMP) fprintf(f,"PUMP %s %s %s %s", Link[p->index].ID, Varword[p->variable], Operator[p->relop], Value[p->status]); - else fprintf(f,"VALVE %s %s %s %s", Link[p->index].ID, Varword[p->variable], Operator[p->relop], Value[p->status]); - } - } - else - { - if (p->variable == r_TIME) - { - hours = (int) p->value / 3600; - minutes = (int) ((p->value - 3600*hours)/60); - seconds = (int) (p->value - 3600*hours - minutes*60); - fprintf(f, "\n%s %s %s %s %d:%02d:%02d", Ruleword[p->logop], Object[p->object], Varword[p->variable], Operator[p->relop], hours, minutes, seconds); - } - else - { - if (p->variable == r_CLOCKTIME) - { - hours = (int) p->value / 3600; - minutes = (int) ((p->value - 3600*hours)/60); - seconds = (int) (p->value - 3600*hours - minutes*60); - - if (hours < 12) fprintf(f, "\n%s %s %s %s %d:%02d:%02d AM", Ruleword[p->logop], Object[p->object], Varword[p->variable], Operator[p->relop], hours, minutes, seconds); - else fprintf(f, "\n%s %s %s %s %d:%02d:%02d PM", Ruleword[p->logop], Object[p->object], Varword[p->variable], Operator[p->relop], hours-12, minutes, seconds); - } - else - { - if (p->variable == r_FILLTIME || p->variable == r_DRAINTIME) fprintf(f, "\n%s TANK %s %s %s %.4lf", Ruleword[p->logop], Object[p->object], Node[p->index].ID, Varword[p->variable], Operator[p->relop], p->value/3600.0); - else - { - fprintf(f, "\n%s ", Ruleword[p->logop]); - if ((strncmp(Object[p->object], "NODE", 4)==0) || (strncmp(Object[p->object], "Junc", 4)==0) || (strncmp(Object[p->object], "Reser", 5)==0) || (strncmp(Object[p->object], "Tank", 4)==0)) { - if (p->index <= Njuncs) fprintf(f,"JUNCTION %s %s %s %.4lf", Node[p->index].ID, Varword[p->variable], Operator[p->relop], p->value); - else if (Tank[p->index-Njuncs].A == 0.0) fprintf(f,"RESERVOIR %s %s %s %.4lf", Node[p->index].ID, Varword[p->variable], Operator[p->relop], p->value); - else fprintf(f,"TANK %s %s %s %.4lf", Node[p->index].ID, Varword[p->variable], Operator[p->relop], p->value); - } - else - { //it is a link - if (Link[p->index].Type == PIPE || Link[p->index].Type == CV) fprintf(f,"PIPE %s %s %s %.4lf", Link[p->index].ID, Varword[p->variable], Operator[p->relop], p->value); - else if (Link[p->index].Type == PUMP) fprintf(f,"PUMP %s %s %s %.4lf", Link[p->index].ID, Varword[p->variable], Operator[p->relop], p->value); - else fprintf(f,"VALVE %s %s %s %.4lf", Link[p->index].ID, Varword[p->variable], Operator[p->relop], p->value); - } - } - } - } - } - p = p->next; - } - - a = Rule[RuleIdx].Tchain; //The first action in hte list of true actions starts with THEN - if (a->setting==MISSING) - { - if (Link[a->link].Type == PIPE || Link[a->link].Type == CV) fprintf(f, "\nTHEN PIPE %s STATUS IS %s", Link[a->link].ID, Value[a->status]); - else if (Link[a->link].Type == PUMP) fprintf(f, "\nTHEN PUMP %s STATUS IS %s", Link[a->link].ID, Value[a->status]); - else fprintf(f, "\nTHEN VALVE %s STATUS IS %s", Link[a->link].ID, Value[a->status]); - } - else - { - if (Link[a->link].Type == PIPE || Link[a->link].Type == CV) fprintf(f, "\nTHEN PIPE %s SETTING IS %.4f", Link[a->link].ID, a->setting); - else if (Link[a->link].Type == PUMP) fprintf(f, "\nTHEN PUMP %s SETTING IS %.4f", Link[a->link].ID, a->setting); - else fprintf(f, "\nTHEN VALVE %s SETTING IS %.4f", Link[a->link].ID, a->setting); - } - - a = a->next; //The other actions in the list of true actions start with AND - while (a != NULL) - { - if (a->setting==MISSING) - { - if (Link[a->link].Type == PIPE || Link[a->link].Type == CV) fprintf(f, "\nAND PIPE %s STATUS IS %s", Link[a->link].ID, Value[a->status]); - else if (Link[a->link].Type == PUMP) fprintf(f, "\nAND PUMP %s STATUS IS %s", Link[a->link].ID, Value[a->status]); - else fprintf(f, "\nAND VALVE %s STATUS IS %s", Link[a->link].ID, Value[a->status]); - } - else - { - if (Link[a->link].Type == PIPE || Link[a->link].Type == CV) fprintf(f, "\nAND PIPE %s SETTING IS %.4f", Link[a->link].ID, a->setting); - else if (Link[a->link].Type == PUMP) fprintf(f, "\nAND PUMP %s SETTING IS %.4f", Link[a->link].ID, a->setting); - else fprintf(f, "\nAND VALVE %s SETTING IS %.4f", Link[a->link].ID, a->setting); - } - - a = a->next; - } - - - a = Rule[RuleIdx].Fchain; //The first action in the list of false actions starts with ELSE - if (a != NULL) - { - if (a->setting==MISSING) - { - if (Link[a->link].Type == PIPE || Link[a->link].Type == CV) fprintf(f, "\nELSE PIPE %s STATUS IS %s", Link[a->link].ID, Value[a->status]); - else if (Link[a->link].Type == PUMP) fprintf(f, "\nELSE PUMP %s STATUS IS %s", Link[a->link].ID, Value[a->status]); - else fprintf(f, "\nELSE VALVE %s STATUS IS %s", Link[a->link].ID, Value[a->status]); - } - else - { - if (Link[a->link].Type == PIPE || Link[a->link].Type == CV) fprintf(f, "\nELSE PIPE %s SETTING IS %.4f", Link[a->link].ID, a->setting); - else if (Link[a->link].Type == PUMP) fprintf(f, "\nELSE PUMP %s SETTING IS %.4f", Link[a->link].ID, a->setting); - else fprintf(f, "\nELSE VALVE %s SETTING IS %.4f", Link[a->link].ID, a->setting); - } - - a = a->next; //The other actions in the list of false actions start with AND - while (a != NULL) - { - if (a->setting==MISSING) - { - if (Link[a->link].Type == PIPE || Link[a->link].Type == CV) fprintf(f, "\nAND PIPE %s STATUS IS %s", Link[a->link].ID, Value[a->status]); - else if (Link[a->link].Type == PUMP) fprintf(f, "\nAND PUMP %s STATUS IS %s", Link[a->link].ID, Value[a->status]); - else fprintf(f, "\nAND VALVE %s STATUS IS %s", Link[a->link].ID, Value[a->status]); - } - else - { - if (Link[a->link].Type == PIPE || Link[a->link].Type == CV) fprintf(f, "\nAND PIPE %s SETTING IS %.4f", Link[a->link].ID, a->setting); - else if (Link[a->link].Type == PUMP) fprintf(f, "\nAND PUMP %s SETTING IS %.4f", Link[a->link].ID, a->setting); - else fprintf(f, "\nAND VALVE %s SETTING IS %.4f", Link[a->link].ID, a->setting); - } - - a = a->next; - } - } - if (Rule[RuleIdx].priority != 0) fprintf(f, "\nPRIORITY %.4f", Rule[RuleIdx].priority); - - return(0); -} - -/***************** END OF RULES.C ******************/ - +/* +********************************************************************** + +RULES.C -- Rule processor module for EPANET + +VERSION: 2.00 +DATE: 5/8/00 + 9/7/00 + 10/25/00 + 3/1/01 + 8/15/07 (2.00.11) +AUTHOR: L. Rossman + US EPA - NRMRL + + The entry points for this module are: + initrules() -- called from ENopen() in EPANET.C + addrule() -- called from netsize() in INPUT2.C + allocrules() -- called from allocdata() in EPANET.C + ruledata() -- called from newline() in INPUT2.C + freerules() -- called from freedata() in EPANET.C + checkrules() -- called from ruletimestep() in HYDRAUL.C + +********************************************************************** +*/ +#include +#include +#ifndef __APPLE__ +#include +#else +#include +#endif +#include "epanet2.h" +#include "funcs.h" +#include "hash.h" +#include "text.h" +#include "types.h" +#define EXTERN extern +#include "vars.h" + +enum Rulewords { + r_RULE, + r_IF, + r_AND, + r_OR, + r_THEN, + r_ELSE, + r_PRIORITY, + r_ERROR +}; +char *Ruleword[] = {w_RULE, w_IF, w_AND, w_OR, + w_THEN, w_ELSE, w_PRIORITY, NULL}; + +enum Varwords { + r_DEMAND, + r_HEAD, + r_GRADE, + r_LEVEL, + r_PRESSURE, + r_FLOW, + r_STATUS, + r_SETTING, + r_POWER, + r_TIME, + r_CLOCKTIME, + r_FILLTIME, + r_DRAINTIME +}; +char *Varword[] = {w_DEMAND, w_HEAD, w_GRADE, w_LEVEL, w_PRESSURE, + w_FLOW, w_STATUS, w_SETTING, w_POWER, w_TIME, + w_CLOCKTIME, w_FILLTIME, w_DRAINTIME, NULL}; + +enum Objects { + r_JUNC, + r_RESERV, + r_TANK, + r_PIPE, + r_PUMP, + r_VALVE, + r_NODE, + r_LINK, + r_SYSTEM +}; +char *Object[] = {w_JUNC, w_RESERV, w_TANK, w_PIPE, w_PUMP, + w_VALVE, w_NODE, w_LINK, w_SYSTEM, NULL}; + +/* NOTE: place "<=" & ">=" before "<" & ">" so that findmatch() works correctly. + */ +enum Operators { EQ, NE, LE, GE, LT, GT, IS, NOT, BELOW, ABOVE }; +char *Operator[] = {"=", "<>", "<=", ">=", "<", ">", + w_IS, w_NOT, w_BELOW, w_ABOVE, NULL}; + +enum Values { IS_NUMBER, IS_OPEN, IS_CLOSED, IS_ACTIVE }; +char *Value[] = {"XXXX", w_OPEN, w_CLOSED, w_ACTIVE, NULL}; + +/* External variables declared in INPUT2.C */ + +/* +** Local function prototypes are defined here and not in FUNCS.H +** because some of them utilize the Premise and Action structures +** defined locally in this module. +*/ +void newrule(EN_Project *pr); +int newpremise(EN_Project *pr, int); +int newaction(EN_Project *pr); +int newpriority(EN_Project *pr); +int evalpremises(EN_Project *pr, int); +void updateactlist(rules_t *rules, int, Action *); +int checkaction(rules_t *rules, int, Action *); +int checkpremise(EN_Project *pr, Premise *); +int checktime(EN_Project *pr, Premise *); +int checkstatus(EN_Project *pr, Premise *); +int checkvalue(EN_Project *pr, Premise *); +int takeactions(EN_Project *pr); +void clearactlist(rules_t *rules); +void clearrules(EN_Project *pr); +void ruleerrmsg(EN_Project *pr, int); +int writeRuleinInp(EN_Project *pr, FILE *f, int RuleIdx); + +void initrules(rules_t *rules) +/* +**-------------------------------------------------------------- +** Initializes rule base. +** Called by ENopen() in EPANET.C module +**-------------------------------------------------------------- +*/ +{ + rules->RuleState = r_PRIORITY; + rules->Rule = NULL; +} + +void addrule(parser_data_t *par, char *tok) +/* +**-------------------------------------------------------------- +** Updates rule count if RULE keyword found in line of input. +** Called by netsize() in INPUT2.C module. +**-------------------------------------------------------------- +*/ +{ + if (match(tok, w_RULE)) { + par->MaxRules++; + } +} + +int allocrules(EN_Project *pr) +/* +**-------------------------------------------------------------- +** Allocates memory for rule-based controls. +** Called by allocdata() in EPANET.C module. +**-------------------------------------------------------------- +*/ +{ + rules_t *rules = &pr->rules; + parser_data_t *par = &pr->parser; + + rules->Rule = (aRule *)calloc(par->MaxRules + 1, sizeof(aRule)); + if (rules->Rule == NULL) { + return (101); + } else { + return (0); + } +} + +void freerules(EN_Project *pr) +/* +**-------------------------------------------------------------- +** Frees memory used for rule-based controls. +** Called by freedata() in EPANET.C module. +**-------------------------------------------------------------- +*/ +{ + clearrules(pr); + free(pr->rules.Rule); +} + +int checkrules(EN_Project *pr, long dt) +/* +**----------------------------------------------------- +** Checks which rules should fire at current time. +** Called by ruletimestep() in HYDRAUL.C. +**----------------------------------------------------- +*/ +{ + EN_Network *net = &pr->network; + time_options_t *time = &pr->time_options; + rules_t *rules = &pr->rules; + + int i, r; /* Number of actions actually taken */ + + /* Start of rule evaluation time interval */ + rules->Time1 = time->Htime - dt + 1; + + /* Iterate through each rule */ + rules->ActList = NULL; + r = 0; + for (i = 1; i <= net->Nrules; i++) { + /* If premises true, add THEN clauses to action list. */ + if (evalpremises(pr, i) == TRUE) { + updateactlist(rules, i, rules->Rule[i].Tchain); + } + + /* If premises false, add ELSE actions to list. */ + else { + if (rules->Rule[i].Fchain != NULL) { + updateactlist(rules, i, rules->Rule[i].Fchain); + } + } + } + + /* Execute actions then clear list. */ + if (rules->ActList != NULL) { + r = takeactions(pr); + } + clearactlist(rules); + return (r); +} + +int ruledata(EN_Project *pr) +/* +**-------------------------------------------------------------- +** Parses a line from [RULES] section of input. +** Called by newline() in INPUT2.C module. +** Tok[] is global array of tokens parsed from input line. +**-------------------------------------------------------------- +*/ +{ + EN_Network *net = &pr->network; + parser_data_t *par = &pr->parser; + + rules_t *rules = &pr->rules; + char **Tok = par->Tok; + + int key, /* Keyword code */ + err; + + /* Exit if current rule has an error */ + if (rules->RuleState == r_ERROR) + return (0); + + /* Find the key word that begins the rule statement */ + err = 0; + key = findmatch(Tok[0], Ruleword); + switch (key) { + case -1: + err = 201; /* Unrecognized keyword */ + break; + case r_RULE: + net->Nrules++; + newrule(pr); + rules->RuleState = r_RULE; + break; + case r_IF: + if (rules->RuleState != r_RULE) { + err = 221; /* Mis-placed IF clause */ + break; + } + rules->RuleState = r_IF; + err = newpremise(pr,r_AND); + break; + case r_AND: + if (rules->RuleState == r_IF) + err = newpremise(pr, r_AND); + else if (rules->RuleState == r_THEN || rules->RuleState == r_ELSE) + err = newaction(pr); + else + err = 221; + break; + case r_OR: + if (rules->RuleState == r_IF) + err = newpremise(pr, r_OR); + else + err = 221; + break; + case r_THEN: + if (rules->RuleState != r_IF) { + err = 221; /* Mis-placed THEN clause */ + break; + } + rules->RuleState = r_THEN; + err = newaction(pr); + break; + case r_ELSE: + if (rules->RuleState != r_THEN) { + err = 221; /* Mis-placed ELSE clause */ + break; + } + rules->RuleState = r_ELSE; + err = newaction(pr); + break; + case r_PRIORITY: + if (rules->RuleState != r_THEN && rules->RuleState != r_ELSE) { + err = 221; + break; + } + rules->RuleState = r_PRIORITY; + err = newpriority(pr); + break; + default: + err = 201; + } + + /* Set RuleState to r_ERROR if errors found */ + if (err) { + rules->RuleState = r_ERROR; + ruleerrmsg(pr,err); + err = 200; + } + return (err); +} + +void clearactlist(rules_t *rules) +/* +**---------------------------------------------------------- +** Clears memory used for action list +**---------------------------------------------------------- +*/ +{ + ActItem *a; + ActItem *anext; + a = rules->ActList; + while (a != NULL) { + anext = a->next; + free(a); + a = anext; + } +} + +void clearrules(EN_Project *pr) +/* +**----------------------------------------------------------- +** Clears memory used for premises & actions for all rules +**----------------------------------------------------------- +*/ +{ + EN_Network *net = &pr->network; + + rules_t *rules = &pr->rules; + + Premise *p; + Premise *pnext; + Action *a; + Action *anext; + int i; + for (i = 1; i <= net->Nrules; i++) { + p = rules->Rule[i].Pchain; + while (p != NULL) { + pnext = p->next; + free(p); + p = pnext; + } + a = rules->Rule[i].Tchain; + while (a != NULL) { + anext = a->next; + free(a); + a = anext; + } + a = rules->Rule[i].Fchain; + while (a != NULL) { + anext = a->next; + free(a); + a = anext; + } + } +} + +void newrule(EN_Project *pr) +/* +**---------------------------------------------------------- +** Adds new rule to rule base +**---------------------------------------------------------- +*/ +{ + EN_Network *net = &pr->network; + parser_data_t *par = &pr->parser; + rules_t *rules = &pr->rules; + char **Tok = par->Tok; + strncpy(rules->Rule[net->Nrules].label, Tok[1], MAXID); + rules->Rule[net->Nrules].Pchain = NULL; + rules->Rule[net->Nrules].Tchain = NULL; + rules->Rule[net->Nrules].Fchain = NULL; + rules->Rule[net->Nrules].priority = 0.0; + rules->Plast = NULL; + rules->Tlast = NULL; + rules->Flast = NULL; +} + +int newpremise(EN_Project *pr, int logop) +/* +**-------------------------------------------------------------------- +** Adds new premise to current rule. +** Formats are: +** IF/AND/OR +** IF/AND/OR SYSTEM (units) +** +** Calls findmatch() and hour() in INPUT2.C. +** Calls findnode() and findlink() in EPANET.C. +**--------------------------------------------------------------------- +*/ +{ + EN_Network *net = &pr->network; + parser_data_t *par = &pr->parser; + + rules_t *rules = &pr->rules; + char **Tok = par->Tok; + + int i, j, k, m, r, s, v; + double x; + Premise *p; + + /* Check for correct number of tokens */ + if (par->Ntokens != 5 && par->Ntokens != 6) + return (201); + + /* Find network object & id if present */ + i = findmatch(Tok[1], Object); + if (i == r_SYSTEM) { + j = 0; + v = findmatch(Tok[2], Varword); + if (v != r_DEMAND && v != r_TIME && v != r_CLOCKTIME) { + return (201); + } + } else { + v = findmatch(Tok[3], Varword); + if (v < 0) { + return (201); + } + switch (i) { + case r_NODE: + case r_JUNC: + case r_RESERV: + case r_TANK: + k = r_NODE; + break; + case r_LINK: + case r_PIPE: + case r_PUMP: + case r_VALVE: + k = r_LINK; + break; + default: + return (201); + } + i = k; + if (i == r_NODE) { + j = findnode(net, Tok[2]); + if (j == 0) + return (203); + switch (v) { + case r_DEMAND: + case r_HEAD: + case r_GRADE: + case r_LEVEL: + case r_PRESSURE: + break; + case r_FILLTIME: + case r_DRAINTIME: + if (j <= net->Njuncs) { + return (201); + } + break; + + default: + return (201); + } + } else { + j = findlink(net, Tok[2]); + if (j == 0) { + return (204); + } + switch (v) { + case r_FLOW: + case r_STATUS: + case r_SETTING: + break; + default: + return (201); + } + } + } + + /* Parse relational operator (r) and check for synonyms */ + if (i == r_SYSTEM) { + m = 3; + } else { + m = 4; + } + k = findmatch(Tok[m], Operator); + if (k < 0) + return (201); + switch (k) { + case IS: + r = EQ; + break; + case NOT: + r = NE; + break; + case BELOW: + r = LT; + break; + case ABOVE: + r = GT; + break; + default: + r = k; + } + + /* Parse for status (s) or numerical value (x) */ + s = 0; + x = MISSING; + if (v == r_TIME || v == r_CLOCKTIME) { + if (par->Ntokens == 6) + x = hour(Tok[4], Tok[5]) * 3600.; + else + x = hour(Tok[4], "") * 3600.; + if (x < 0.0) + return (202); + } else if ((k = findmatch(Tok[par->Ntokens - 1], Value)) > IS_NUMBER) + s = k; + else { + if (!getfloat(Tok[par->Ntokens - 1], &x)) + return (202); + if (v == r_FILLTIME || v == r_DRAINTIME) + x = x * 3600.0; + } + + /* Create new premise structure */ + p = (Premise *)malloc(sizeof(Premise)); + if (p == NULL) + return (101); + p->object = i; + p->index = j; + p->variable = v; + p->relop = r; + p->logop = logop; + p->status = s; + p->value = x; + + /* Add premise to current rule's premise list */ + p->next = NULL; + if (rules->Plast == NULL) + rules->Rule[net->Nrules].Pchain = p; + else + rules->Plast->next = p; + rules->Plast = p; + return (0); +} + +int newaction(EN_Project *pr) +/* +**---------------------------------------------------------- +** Adds new action to current rule. +** Format is: +** THEN/ELSE/AND LINK IS +** +** Calls findlink() from EPANET.C. +** Calls getfloat() and findmatch() from INPUT2.C. +**---------------------------------------------------------- +*/ +{ + EN_Network *net = &pr->network; + parser_data_t *par = &pr->parser; + rules_t *rules = &pr->rules; + char **Tok = par->Tok; + + int j, k, s; + double x; + Action *a; + + /* Check for correct number of tokens */ + if (par->Ntokens != 6) + return (201); + + /* Check that link exists */ + j = findlink(net, Tok[2]); + if (j == 0) + return (204); + + /*** Updated 9/7/00 ***/ + /* Cannot control a CV */ + if (net->Link[j].Type == EN_CVPIPE) + return (207); + + /* Find value for status or setting */ + s = -1; + x = MISSING; + if ((k = findmatch(Tok[5], Value)) > IS_NUMBER) { + s = k; + } else { + if (!getfloat(Tok[5], &x)) { + return (202); + } + if (x < 0.0) { + return (202); + } + } + + /*** Updated 9/7/00 ***/ + /* Cannot change setting for a GPV ***/ + if (x != MISSING && net->Link[j].Type == EN_GPV) + return (202); + + /*** Updated 3/1/01 ***/ + /* Set status for pipe in case setting was specified */ + if (x != MISSING && net->Link[j].Type == EN_PIPE) { + if (x == 0.0) + s = IS_CLOSED; + else + s = IS_OPEN; + x = MISSING; + } + + /* Create a new action structure */ + a = (Action *)malloc(sizeof(Action)); + if (a == NULL) + return (101); + a->link = j; + a->status = s; + a->setting = x; + + /* Add action to current rule's action list */ + if (rules->RuleState == r_THEN) { + a->next = NULL; + if (rules->Tlast == NULL) + rules->Rule[net->Nrules].Tchain = a; + else + rules->Tlast->next = a; + rules->Tlast = a; + } else { + a->next = NULL; + if (rules->Flast == NULL) + rules->Rule[net->Nrules].Fchain = a; + else + rules->Flast->next = a; + rules->Flast = a; + } + return (0); +} + +int newpriority(EN_Project *pr) +/* +**--------------------------------------------------- +** Adds priority rating to current rule +**--------------------------------------------------- +*/ +{ + EN_Network *net = &pr->network; + parser_data_t *par = &pr->parser; + rules_t *rules = &pr->rules; + char **Tok = par->Tok; + + double x; + if (!getfloat(Tok[1], &x)) + return (202); + rules->Rule[net->Nrules].priority = x; + return (0); +} + +int evalpremises(EN_Project *pr, int i) +/* +**---------------------------------------------------------- +** Checks if premises to rule i are true +**---------------------------------------------------------- +*/ +{ + rules_t *rules = &pr->rules; + + int result; + Premise *p; + + result = TRUE; + p = rules->Rule[i].Pchain; + while (p != NULL) { + if (p->logop == r_OR) { + if (result == FALSE) { + result = checkpremise(pr,p); + } + } else { + if (result == FALSE) + return (FALSE); + result = checkpremise(pr,p); + } + p = p->next; + } + return (result); +} + +int checkpremise(EN_Project *pr, Premise *p) +/* +**---------------------------------------------------------- +** Checks if a particular premise is true +**---------------------------------------------------------- +*/ +{ + if (p->variable == r_TIME || p->variable == r_CLOCKTIME) + return (checktime(pr,p)); + else if (p->status > IS_NUMBER) + return (checkstatus(pr,p)); + else + return (checkvalue(pr,p)); +} + +int checktime(EN_Project *pr, Premise *p) +/* +**------------------------------------------------------------ +** Checks if condition on system time holds +**------------------------------------------------------------ +*/ +{ + time_options_t *time = &pr->time_options; + rules_t *rules = &pr->rules; + + char flag; + long t1, t2, x; + + /* Get start and end of rule evaluation time interval */ + if (p->variable == r_TIME) { + t1 = rules->Time1; + t2 = time->Htime; + } + else if (p->variable == r_CLOCKTIME) { + t1 = (rules->Time1 + time->Tstart) % SECperDAY; + t2 = (time->Htime + time->Tstart) % SECperDAY; + } else + return (0); + + /* Test premise's time */ + x = (long)(p->value); + switch (p->relop) { + /* For inequality, test against current time */ + case LT: + if (t2 >= x) + return (0); + break; + case LE: + if (t2 > x) + return (0); + break; + case GT: + if (t2 <= x) + return (0); + break; + case GE: + if (t2 < x) + return (0); + break; + + /* For equality, test if within interval */ + case EQ: + case NE: + flag = FALSE; + if (t2 < t1) /* E.g., 11:00 am to 1:00 am */ + { + if (x >= t1 || x <= t2) + flag = TRUE; + } else { + if (x >= t1 && x <= t2) + flag = TRUE; + } + if (p->relop == EQ && flag == FALSE) + return (0); + if (p->relop == NE && flag == TRUE) + return (0); + break; + } + + /* If we get to here then premise was satisfied */ + return (1); +} + +int checkstatus(EN_Project *pr, Premise *p) +/* +**------------------------------------------------------------ +** Checks if condition on link status holds +**------------------------------------------------------------ +*/ +{ + hydraulics_t *hyd = &pr->hydraulics; + + char i; + int j; + switch (p->status) { + case IS_OPEN: + case IS_CLOSED: + case IS_ACTIVE: + i = hyd->LinkStatus[p->index]; + if (i <= CLOSED) + j = IS_CLOSED; + else if (i == ACTIVE) + j = IS_ACTIVE; + else + j = IS_OPEN; + if (j == p->status && p->relop == EQ) + return (1); + if (j != p->status && p->relop == NE) + return (1); + } + return (0); +} + +int checkvalue(EN_Project *pr, Premise *p) +/* +**---------------------------------------------------------- +** Checks if numerical condition on a variable is true. +** Uses tolerance of 0.001 when testing conditions. +**---------------------------------------------------------- +*/ +{ + EN_Network *net = &pr->network; + hydraulics_t *hyd = &pr->hydraulics; + + Snode *Node = net->Node; + Slink *Link = net->Link; + Stank *Tank = net->Tank; + + const int Njuncs = net->Njuncs; + double *Ucf = pr->Ucf; + double *NodeDemand = hyd->NodeDemand; + double *LinkFlows = hyd->LinkFlows; + double *LinkSetting = hyd->LinkSetting; + + int i, j, v; + double x, tol = 1.e-3; + + i = p->index; + v = p->variable; + switch (v) { + case r_DEMAND: + if (p->object == r_SYSTEM) + x = hyd->Dsystem * Ucf[DEMAND]; + else + x = NodeDemand[i] * Ucf[DEMAND]; + break; + + case r_HEAD: + case r_GRADE: + x = hyd->NodeHead[i] * Ucf[HEAD]; + break; + case r_PRESSURE: + x = (hyd->NodeHead[i] - Node[i].El) * Ucf[PRESSURE]; + break; + case r_LEVEL: + x = (hyd->NodeHead[i] - Node[i].El) * Ucf[HEAD]; + break; + case r_FLOW: + x = ABS(LinkFlows[i]) * Ucf[FLOW]; + break; + case r_SETTING: + if (LinkSetting[i] == MISSING) + return (0); + x = LinkSetting[i]; + switch (Link[i].Type) { + case EN_PRV: + case EN_PSV: + case EN_PBV: + x = x * Ucf[PRESSURE]; + break; + case EN_FCV: + x = x * Ucf[FLOW]; + break; + default: + break; + } + break; + case r_FILLTIME: + if (i <= Njuncs) + return (0); + j = i - Njuncs; + if (Tank[j].A == 0.0) + return (0); + if (NodeDemand[i] <= TINY) + return (0); + x = (Tank[j].Vmax - Tank[j].V) / NodeDemand[i]; + break; + case r_DRAINTIME: + if (i <= Njuncs) + return (0); + j = i - Njuncs; + if (Tank[j].A == 0.0) + return (0); + if (NodeDemand[i] >= -TINY) + return (0); + x = (Tank[j].Vmin - Tank[j].V) / NodeDemand[i]; + break; + default: + return (0); + } + switch (p->relop) { + case EQ: + if (ABS(x - p->value) > tol) + return (0); + break; + case NE: + if (ABS(x - p->value) < tol) + return (0); + break; + case LT: + if (x > p->value + tol) + return (0); + break; + case LE: + if (x > p->value - tol) + return (0); + break; + case GT: + if (x < p->value - tol) + return (0); + break; + case GE: + if (x < p->value + tol) + return (0); + break; + } + return (1); +} + +void updateactlist(rules_t *rules, int i, Action *actions) +/* +**--------------------------------------------------- +** Adds rule's actions to action list +**--------------------------------------------------- +*/ +{ + ActItem *item; + Action *a; + + /* Iterate through each action of Rule i */ + a = actions; + while (a != NULL) { + /* Add action to list if not already on it */ + if (!checkaction(rules, i, a)) { + item = (ActItem *)malloc(sizeof(ActItem)); + if (item != NULL) { + item->action = a; + item->ruleindex = i; + item->next = rules->ActList; + rules->ActList = item; + } + } + a = a->next; + } +} + +int checkaction(rules_t *rules, int i, Action *a) +/* +**----------------------------------------------------------- +** Checks if an action is already on the Action List +**----------------------------------------------------------- +*/ +{ + int i1, k, k1; + ActItem *item; + Action *a1; + + /* Search action list for link named in action */ + k = a->link; /* Action applies to link k */ + item = rules->ActList; + while (item != NULL) { + a1 = item->action; + i1 = item->ruleindex; + k1 = a1->link; + + /* If link on list then replace action if rule has higher priority. */ + if (k1 == k) { + if (rules->Rule[i].priority > rules->Rule[i1].priority) { + item->action = a; + item->ruleindex = i; + } + return (1); + } + item = item->next; + } + return (0); +} + +int takeactions(EN_Project *pr) +/* +**----------------------------------------------------------- +** Implements actions on action list +**----------------------------------------------------------- +*/ +{ + + EN_Network *net = &pr->network; + hydraulics_t *hyd = &pr->hydraulics; + report_options_t *rep = &pr->report; + rules_t *rules = &pr->rules; + + Action *a; + ActItem *item; + char flag; + int k, s, n; + double tol = 1.e-3, v, x; + + n = 0; + item = rules->ActList; + while (item != NULL) { + flag = FALSE; + a = item->action; + k = a->link; + s = hyd->LinkStatus[k]; + v = hyd->LinkSetting[k]; + x = a->setting; + + /* Switch link from closed to open */ + if (a->status == IS_OPEN && s <= CLOSED) { + setlinkstatus(pr, k, 1, &hyd->LinkStatus[k], &hyd->LinkSetting[k]); + flag = TRUE; + } + + /* Switch link from not closed to closed */ + else if (a->status == IS_CLOSED && s > CLOSED) { + setlinkstatus(pr, k, 0, &hyd->LinkStatus[k], &hyd->LinkSetting[k]); + flag = TRUE; + } + + /* Change link's setting */ + else if (x != MISSING) { + switch (net->Link[k].Type) { + case EN_PRV: + case EN_PSV: + case EN_PBV: + x = x / pr->Ucf[PRESSURE]; + break; + case EN_FCV: + x = x / pr->Ucf[FLOW]; + break; + default: + break; + } + if (ABS(x - v) > tol) { + setlinksetting(pr, k, x, &hyd->LinkStatus[k], &hyd->LinkSetting[k]); + flag = TRUE; + } + } + + /* Report rule action */ + if (flag == TRUE) { + n++; + if (rep->Statflag) + writeruleaction(pr, k, rules->Rule[item->ruleindex].label); + } + + /* Move to next action on list */ + item = item->next; + } + return (n); +} + +void ruleerrmsg(EN_Project *pr, int err) +/* +**----------------------------------------------------------- +** Reports error message +**----------------------------------------------------------- +*/ +{ + EN_Network *net = &pr->network; + parser_data_t *par = &pr->parser; + rules_t *rules = &pr->rules; + char **Tok = par->Tok; + + int i; + char label[81]; + char fmt[256]; + switch (err) { + case 201: + strcpy(fmt, R_ERR201); + break; + case 202: + strcpy(fmt, R_ERR202); + break; + case 203: + strcpy(fmt, R_ERR203); + break; + case 204: + strcpy(fmt, R_ERR204); + break; + + /*** Updated on 9/7/00 ***/ + case 207: + strcpy(fmt, R_ERR207); + break; + + case 221: + strcpy(fmt, R_ERR221); + break; + default: + return; + } + if (net->Nrules > 0) { + strcpy(label, t_RULE); + strcat(label, " "); + strcat(label, rules->Rule[net->Nrules].label); + } else + strcpy(label, t_RULES_SECT); + sprintf(pr->Msg, "%s", fmt); + strcat(pr->Msg, label); + strcat(pr->Msg, ":"); + writeline(pr, pr->Msg); + strcpy(fmt, Tok[0]); + for (i = 1; i < par->Ntokens; i++) { + strcat(fmt, " "); + strcat(fmt, Tok[i]); + } + writeline(pr, fmt); +} + +int writeRuleinInp(EN_Project *pr, FILE *f, int RuleIdx) { + /* + **----------------------------------------------------------- + ** This function writes a specific rule (rule ID, + ** premises, true and false actions and prioriry in the + ** text input file. + ** INPUT: + ** - FILE *f : pointer to the .inp file to be written + ** - int RuleIdx : index of the rule that needs to be written + ** OUTPUT: error code + **----------------------------------------------------------- + */ + + EN_Network *net = &pr->network; + rules_t *rules = &pr->rules; + + Slink *Link = net->Link; + Stank *Tank = net->Tank; + Snode *Node = net->Node; + + // int i,j; + Premise *p; + Action *a; + int hours = 0, minutes = 0, seconds = 0; + + // the first condition/premises is different from the others because it starts + // with IF (but it is kept in memory as AND) + p = rules->Rule[RuleIdx].Pchain; + if (p->value == MISSING) { + fprintf(f, "\nIF "); + if ((strncmp(Object[p->object], "NODE", 4) == 0) || + (strncmp(Object[p->object], "Junc", 4) == 0) || + (strncmp(Object[p->object], "Reser", 5) == 0) || + (strncmp(Object[p->object], "Tank", 4) == 0)) { + if (p->index <= net->Njuncs) + fprintf(f, "JUNCTION %s %s %s %s", Node[p->index].ID, + Varword[p->variable], Operator[p->relop], Value[p->status]); + else if (Tank[p->index - net->Njuncs].A == 0.0) + fprintf(f, "RESERVOIR %s %s %s %s", Node[p->index].ID, + Varword[p->variable], Operator[p->relop], Value[p->status]); + else + fprintf(f, "TANK %s %s %s %s", Node[p->index].ID, Varword[p->variable], + Operator[p->relop], Value[p->status]); + } else { // it is a link + if (Link[p->index].Type == EN_PIPE || Link[p->index].Type == EN_CVPIPE) + fprintf(f, "PIPE %s %s %s %s", Link[p->index].ID, Varword[p->variable], + Operator[p->relop], Value[p->status]); + else if (Link[p->index].Type == EN_PUMP) + fprintf(f, "PUMP %s %s %s %s", Link[p->index].ID, Varword[p->variable], + Operator[p->relop], Value[p->status]); + else + fprintf(f, "VALVE %s %s %s %s", Link[p->index].ID, Varword[p->variable], + Operator[p->relop], Value[p->status]); + } + } else { + if (p->variable == r_TIME) { + hours = (int)p->value / 3600; + minutes = (int)((p->value - 3600 * hours) / 60); + seconds = (int)(p->value - 3600 * hours - minutes * 60); + fprintf(f, "\nIF %s %s %s %d:%02d:%02d", Object[p->object], + Varword[p->variable], Operator[p->relop], hours, minutes, + seconds); + } else { + if (p->variable == r_CLOCKTIME) { + hours = (int)p->value / 3600; + minutes = (int)((p->value - 3600 * hours) / 60); + seconds = (int)(p->value - 3600 * hours - minutes * 60); + + if (hours < 12) + fprintf(f, "\nIF %s %s %s %d:%02d:%02d AM", Object[p->object], + Varword[p->variable], Operator[p->relop], hours, minutes, + seconds); + else + fprintf(f, "\nIF %s %s %s %d:%02d:%02d PM", Object[p->object], + Varword[p->variable], Operator[p->relop], hours - 12, minutes, + seconds); + } else { + if (p->variable == r_FILLTIME || p->variable == r_DRAINTIME) + fprintf(f, "\nIF TANK %s %s %s %.4lf", Node[p->index].ID, + Varword[p->variable], Operator[p->relop], p->value / 3600.0); + else { + fprintf(f, "\nIF "); + if ((strncmp(Object[p->object], "NODE", 4) == 0) || + (strncmp(Object[p->object], "Junc", 4) == 0) || + (strncmp(Object[p->object], "Reser", 5) == 0) || + (strncmp(Object[p->object], "Tank", 4) == 0)) { + if (p->index <= net->Njuncs) + fprintf(f, "JUNCTION %s %s %s %.4lf", Node[p->index].ID, + Varword[p->variable], Operator[p->relop], p->value); + else if (Tank[p->index - net->Njuncs].A == 0.0) + fprintf(f, "RESERVOIR %s %s %s %.4lf", Node[p->index].ID, + Varword[p->variable], Operator[p->relop], p->value); + else + fprintf(f, "TANK %s %s %s %.4lf", Node[p->index].ID, + Varword[p->variable], Operator[p->relop], p->value); + } else { // it is a link + if (Link[p->index].Type == EN_PIPE || + Link[p->index].Type == EN_CVPIPE) + fprintf(f, "PIPE %s %s %s %.4lf", Link[p->index].ID, + Varword[p->variable], Operator[p->relop], p->value); + else if (Link[p->index].Type == EN_PUMP) + fprintf(f, "PUMP %s %s %s %.4lf", Link[p->index].ID, + Varword[p->variable], Operator[p->relop], p->value); + else + fprintf(f, "VALVE %s %s %s %.4lf", Link[p->index].ID, + Varword[p->variable], Operator[p->relop], p->value); + } + } + } + } + } + + p = p->next; + while (p != NULL) // for all other premises/conditions write the corresponding + // logicOperator + { + if (p->value == MISSING) { + fprintf(f, "\n%s ", Ruleword[p->logop]); + if ((strncmp(Object[p->object], "NODE", 4) == 0) || + (strncmp(Object[p->object], "Junc", 4) == 0) || + (strncmp(Object[p->object], "Reser", 5) == 0) || + (strncmp(Object[p->object], "Tank", 4) == 0)) { + if (p->index <= net->Njuncs) + fprintf(f, "JUNCTION %s %s %s %s", Node[p->index].ID, + Varword[p->variable], Operator[p->relop], Value[p->status]); + else if (Tank[p->index - net->Njuncs].A == 0.0) + fprintf(f, "RESERVOIR %s %s %s %s", Node[p->index].ID, + Varword[p->variable], Operator[p->relop], Value[p->status]); + else + fprintf(f, "TANK %s %s %s %s", Node[p->index].ID, + Varword[p->variable], Operator[p->relop], Value[p->status]); + } else { // it is a link + if (Link[p->index].Type == EN_PIPE || Link[p->index].Type == EN_CVPIPE) + fprintf(f, "PIPE %s %s %s %s", Link[p->index].ID, + Varword[p->variable], Operator[p->relop], Value[p->status]); + else if (Link[p->index].Type == EN_PUMP) + fprintf(f, "PUMP %s %s %s %s", Link[p->index].ID, + Varword[p->variable], Operator[p->relop], Value[p->status]); + else + fprintf(f, "VALVE %s %s %s %s", Link[p->index].ID, + Varword[p->variable], Operator[p->relop], Value[p->status]); + } + } else { + if (p->variable == r_TIME) { + hours = (int)p->value / 3600; + minutes = (int)((p->value - 3600 * hours) / 60); + seconds = (int)(p->value - 3600 * hours - minutes * 60); + fprintf(f, "\n%s %s %s %s %d:%02d:%02d", Ruleword[p->logop], + Object[p->object], Varword[p->variable], Operator[p->relop], + hours, minutes, seconds); + } else { + if (p->variable == r_CLOCKTIME) { + hours = (int)p->value / 3600; + minutes = (int)((p->value - 3600 * hours) / 60); + seconds = (int)(p->value - 3600 * hours - minutes * 60); + + if (hours < 12) + fprintf(f, "\n%s %s %s %s %d:%02d:%02d AM", Ruleword[p->logop], + Object[p->object], Varword[p->variable], Operator[p->relop], + hours, minutes, seconds); + else + fprintf(f, "\n%s %s %s %s %d:%02d:%02d PM", Ruleword[p->logop], + Object[p->object], Varword[p->variable], Operator[p->relop], + hours - 12, minutes, seconds); + } else { + if (p->variable == r_FILLTIME || p->variable == r_DRAINTIME) + fprintf(f, "\n%s TANK %s %s %s %s %.4lf", Ruleword[p->logop], + Object[p->object], Node[p->index].ID, Varword[p->variable], + Operator[p->relop], p->value / 3600.0); + else { + fprintf(f, "\n%s ", Ruleword[p->logop]); + if ((strncmp(Object[p->object], "NODE", 4) == 0) || + (strncmp(Object[p->object], "Junc", 4) == 0) || + (strncmp(Object[p->object], "Reser", 5) == 0) || + (strncmp(Object[p->object], "Tank", 4) == 0)) { + if (p->index <= net->Njuncs) + fprintf(f, "JUNCTION %s %s %s %.4lf", Node[p->index].ID, + Varword[p->variable], Operator[p->relop], p->value); + else if (Tank[p->index - net->Njuncs].A == 0.0) + fprintf(f, "RESERVOIR %s %s %s %.4lf", Node[p->index].ID, + Varword[p->variable], Operator[p->relop], p->value); + else + fprintf(f, "TANK %s %s %s %.4lf", Node[p->index].ID, + Varword[p->variable], Operator[p->relop], p->value); + } else { // it is a link + if (Link[p->index].Type == EN_PIPE || + Link[p->index].Type == EN_CVPIPE) + fprintf(f, "PIPE %s %s %s %.4lf", Link[p->index].ID, + Varword[p->variable], Operator[p->relop], p->value); + else if (Link[p->index].Type == EN_PUMP) + fprintf(f, "PUMP %s %s %s %.4lf", Link[p->index].ID, + Varword[p->variable], Operator[p->relop], p->value); + else + fprintf(f, "VALVE %s %s %s %.4lf", Link[p->index].ID, + Varword[p->variable], Operator[p->relop], p->value); + } + } + } + } + } + p = p->next; + } + + a = rules->Rule[RuleIdx].Tchain; // The first action in hte list of true actions + // starts with THEN + if (a->setting == MISSING) { + if (Link[a->link].Type == EN_PIPE || Link[a->link].Type == EN_CVPIPE) + fprintf(f, "\nTHEN PIPE %s STATUS IS %s", Link[a->link].ID, + Value[a->status]); + else if (Link[a->link].Type == EN_PUMP) + fprintf(f, "\nTHEN PUMP %s STATUS IS %s", Link[a->link].ID, + Value[a->status]); + else + fprintf(f, "\nTHEN VALVE %s STATUS IS %s", Link[a->link].ID, + Value[a->status]); + } else { + if (Link[a->link].Type == EN_PIPE || Link[a->link].Type == EN_CVPIPE) + fprintf(f, "\nTHEN PIPE %s SETTING IS %.4f", Link[a->link].ID, + a->setting); + else if (Link[a->link].Type == EN_PUMP) + fprintf(f, "\nTHEN PUMP %s SETTING IS %.4f", Link[a->link].ID, + a->setting); + else + fprintf(f, "\nTHEN VALVE %s SETTING IS %.4f", Link[a->link].ID, + a->setting); + } + + a = a->next; // The other actions in the list of true actions start with AND + while (a != NULL) { + if (a->setting == MISSING) { + if (Link[a->link].Type == EN_PIPE || Link[a->link].Type == EN_CVPIPE) + fprintf(f, "\nAND PIPE %s STATUS IS %s", Link[a->link].ID, + Value[a->status]); + else if (Link[a->link].Type == EN_PUMP) + fprintf(f, "\nAND PUMP %s STATUS IS %s", Link[a->link].ID, + Value[a->status]); + else + fprintf(f, "\nAND VALVE %s STATUS IS %s", Link[a->link].ID, + Value[a->status]); + } else { + if (Link[a->link].Type == EN_PIPE || Link[a->link].Type == EN_CVPIPE) + fprintf(f, "\nAND PIPE %s SETTING IS %.4f", Link[a->link].ID, + a->setting); + else if (Link[a->link].Type == EN_PUMP) + fprintf(f, "\nAND PUMP %s SETTING IS %.4f", Link[a->link].ID, + a->setting); + else + fprintf(f, "\nAND VALVE %s SETTING IS %.4f", Link[a->link].ID, + a->setting); + } + + a = a->next; + } + + a = rules->Rule[RuleIdx].Fchain; // The first action in the list of false actions + // starts with ELSE + if (a != NULL) { + if (a->setting == MISSING) { + if (Link[a->link].Type == EN_PIPE || Link[a->link].Type == EN_CVPIPE) + fprintf(f, "\nELSE PIPE %s STATUS IS %s", Link[a->link].ID, + Value[a->status]); + else if (Link[a->link].Type == EN_PUMP) + fprintf(f, "\nELSE PUMP %s STATUS IS %s", Link[a->link].ID, + Value[a->status]); + else + fprintf(f, "\nELSE VALVE %s STATUS IS %s", Link[a->link].ID, + Value[a->status]); + } else { + if (Link[a->link].Type == EN_PIPE || Link[a->link].Type == EN_CVPIPE) + fprintf(f, "\nELSE PIPE %s SETTING IS %.4f", Link[a->link].ID, + a->setting); + else if (Link[a->link].Type == EN_PUMP) + fprintf(f, "\nELSE PUMP %s SETTING IS %.4f", Link[a->link].ID, + a->setting); + else + fprintf(f, "\nELSE VALVE %s SETTING IS %.4f", Link[a->link].ID, + a->setting); + } + + a = a->next; // The other actions in the list of false actions start with + // AND + while (a != NULL) { + if (a->setting == MISSING) { + if (Link[a->link].Type == EN_PIPE || Link[a->link].Type == EN_CVPIPE) + fprintf(f, "\nAND PIPE %s STATUS IS %s", Link[a->link].ID, + Value[a->status]); + else if (Link[a->link].Type == EN_PUMP) + fprintf(f, "\nAND PUMP %s STATUS IS %s", Link[a->link].ID, + Value[a->status]); + else + fprintf(f, "\nAND VALVE %s STATUS IS %s", Link[a->link].ID, + Value[a->status]); + } else { + if (Link[a->link].Type == EN_PIPE || Link[a->link].Type == EN_CVPIPE) + fprintf(f, "\nAND PIPE %s SETTING IS %.4f", Link[a->link].ID, + a->setting); + else if (Link[a->link].Type == EN_PUMP) + fprintf(f, "\nAND PUMP %s SETTING IS %.4f", Link[a->link].ID, + a->setting); + else + fprintf(f, "\nAND VALVE %s SETTING IS %.4f", Link[a->link].ID, + a->setting); + } + + a = a->next; + } + } + if (rules->Rule[RuleIdx].priority != 0) + fprintf(f, "\nPRIORITY %.4f", rules->Rule[RuleIdx].priority); + + return (0); +} + +/***************** END OF RULES.C ******************/ diff --git a/src/smatrix.c b/src/smatrix.c index 7b0e7f3..70ec801 100755 --- a/src/smatrix.c +++ b/src/smatrix.c @@ -1,754 +1,807 @@ -/* -******************************************************************* - -SMATRIX.C -- Sparse matrix routines for EPANET program. - -VERSION: 2.00 -DATE: 5/8/00 -AUTHOR: L. Rossman - US EPA - NRMRL - -This module contains the sparse matrix routines used to solve -a network's hydraulic equations. The entry points into this -module are: - createsparse() -- called from openhyd() in HYDRAUL.C - freesparse() -- called from closehyd() in HYDRAUL.C - linsolve() -- called from netsolve() in HYDRAUL.C - -Createsparse() does the following: - 1. for each node, builds an adjacency list that identifies - all links connected to the node (see buildlists()) - 2. re-orders the network's nodes to minimize the number - of non-zero entries in the hydraulic solution matrix - (see reorder()) - 3. converts the adjacency lists into a compact scheme - for storing the non-zero coeffs. in the lower diagonal - portion of the solution matrix (see storesparse()) -Freesparse() frees the memory used for the sparse matrix. -Linsolve() solves the linearized system of hydraulic equations. - -******************************************************************** -*/ - -#include -#include -#ifndef __APPLE__ -#include -#else -#include -#endif -#include -#include "hash.h" -#include "text.h" -#include "types.h" -#include "funcs.h" -#define EXTERN extern -#include "vars.h" - -int *Degree; /* Number of links adjacent to each node */ - - -int createsparse() -/* -**-------------------------------------------------------------- -** Input: none -** Output: returns error code -** Purpose: creates sparse representation of coeff. matrix -**-------------------------------------------------------------- -*/ -{ - int errcode = 0; - - /* Allocate data structures */ - ERRCODE(allocsparse()); - if (errcode) return(errcode); - - /* Build node-link adjacency lists with parallel links removed. */ - Degree = (int *) calloc(Nnodes+1, sizeof(int)); - ERRCODE(MEMCHECK(Degree)); - ERRCODE(buildlists(TRUE)); - if (!errcode) - { - xparalinks(); /* Remove parallel links */ - countdegree(); /* Find degree of each junction */ - } /* (= # of adjacent links) */ - - /* Re-order nodes to minimize number of non-zero coeffs. */ - /* in factorized solution matrix. At same time, adjacency */ - /* list is updated with links representing non-zero coeffs. */ - Ncoeffs = Nlinks; - ERRCODE(reordernodes()); - - /* Allocate memory for sparse storage of positions of non-zero */ - /* coeffs. and store these positions in vector NZSUB. */ - ERRCODE(storesparse(Njuncs)); - - /* Free memory used for adjacency lists and sort */ - /* row indexes in NZSUB to optimize linsolve(). */ - if (!errcode) freelists(); - ERRCODE(ordersparse(Njuncs)); - - /* Re-build adjacency lists without removing parallel */ - /* links for use in future connectivity checking. */ - ERRCODE(buildlists(FALSE)); - - /* Free allocated memory */ - free(Degree); - return(errcode); -} /* End of createsparse */ - - -int allocsparse() -/* -**-------------------------------------------------------------- -** Input: none -** Output: returns error code -** Purpose: allocates memory for indexing the solution matrix -**-------------------------------------------------------------- -*/ -{ - int errcode = 0; - Adjlist = (Padjlist *) calloc(Nnodes+1, sizeof(Padjlist)); - Order = (int *) calloc(Nnodes+1, sizeof(int)); - Row = (int *) calloc(Nnodes+1, sizeof(int)); - Ndx = (int *) calloc(Nlinks+1, sizeof(int)); - ERRCODE(MEMCHECK(Adjlist)); - ERRCODE(MEMCHECK(Order)); - ERRCODE(MEMCHECK(Row)); - ERRCODE(MEMCHECK(Ndx)); - return(errcode); -} - - -void freesparse() -/* -**---------------------------------------------------------------- -** Input: None -** Output: None -** Purpose: Frees memory used for sparse matrix storage -**---------------------------------------------------------------- -*/ -{ - freelists(); - free(Adjlist); - free(Order); - free(Row); - free(Ndx); - free(XLNZ); - free(NZSUB); - free(LNZ); -} /* End of freesparse */ - - -int buildlists(int paraflag) -/* -**-------------------------------------------------------------- -** Input: paraflag = TRUE if list marks parallel links -** Output: returns error code -** Purpose: builds linked list of links adjacent to each node -**-------------------------------------------------------------- -*/ -{ - int i,j,k; - int pmark = 0; - int errcode = 0; - Padjlist alink; - - /* For each link, update adjacency lists of its end nodes */ - for (k=1; k<=Nlinks; k++) - { - i = Link[k].N1; - j = Link[k].N2; - if (paraflag) pmark = paralink(i,j,k); /* Parallel link check */ - - /* Include link in start node i's list */ - alink = (struct Sadjlist *) malloc(sizeof(struct Sadjlist)); - if (alink == NULL) return(101); - if (!pmark) alink->node = j; - else alink->node = 0; /* Parallel link marker */ - alink->link = k; - alink->next = Adjlist[i]; - Adjlist[i] = alink; - - /* Include link in end node j's list */ - alink = (struct Sadjlist *) malloc(sizeof(struct Sadjlist)); - if (alink == NULL) return(101); - if (!pmark) alink->node = i; - else alink->node = 0; /* Parallel link marker */ - alink->link = k; - alink->next = Adjlist[j]; - Adjlist[j] = alink; - } - return(errcode); -} /* End of buildlists */ - - -int paralink(int i, int j, int k) -/* -**-------------------------------------------------------------- -** Input: i = index of start node of link -** j = index of end node of link -** k = link index -** Output: returns 1 if link k parallels another link, else 0 -** Purpose: checks for parallel links between nodes i and j -** -**-------------------------------------------------------------- -*/ -{ - Padjlist alink; - for (alink = Adjlist[i]; alink != NULL; alink = alink->next) - { - if (alink->node == j) /* Link || to k (same end nodes) */ - { - Ndx[k] = alink->link; /* Assign Ndx entry to this link */ - return(1); - } - } - Ndx[k] = k; /* Ndx entry if link not parallel */ - return(0); -} /* End of paralink */ - - -void xparalinks() -/* -**-------------------------------------------------------------- -** Input: none -** Output: none -** Purpose: removes parallel links from nodal adjacency lists -**-------------------------------------------------------------- -*/ -{ - int i; - Padjlist alink, /* Current item in adjacency list */ - blink; /* Previous item in adjacency list */ - - /* Scan adjacency list of each node */ - for (i=1; i<=Nnodes; i++) - { - alink = Adjlist[i]; /* First item in list */ - blink = NULL; - while (alink != NULL) - { - if (alink->node == 0) /* Parallel link marker found */ - { - if (blink == NULL) /* This holds at start of list */ - { - Adjlist[i] = alink->next; - free(alink); /* Remove item from list */ - alink = Adjlist[i]; - } - else /* This holds for interior of list */ - { - blink->next = alink->next; - free(alink); /* Remove item from list */ - alink = blink->next; - } - } - else - { - blink = alink; /* Move to next item in list */ - alink = alink->next; - } - } - } -} /* End of xparalinks */ - - -void freelists() -/* -**-------------------------------------------------------------- -** Input: none -** Output: none -** Purpose: frees memory used for nodal adjacency lists -**-------------------------------------------------------------- -*/ -{ - int i; - Padjlist alink; - - for (i=0; i<=Nnodes; i++) - { - for (alink = Adjlist[i]; alink != NULL; alink = Adjlist[i]) - { - Adjlist[i] = alink->next; - free(alink); - } - } -} /* End of freelists */ - - -void countdegree() -/* -**---------------------------------------------------------------- -** Input: none -** Output: none -** Purpose: counts number of nodes directly connected to each node -**---------------------------------------------------------------- -*/ -{ - int i; - Padjlist alink; - memset(Degree,0,(Nnodes+1)*sizeof(int)); - - /* NOTE: For purposes of node re-ordering, Tanks (nodes with */ - /* indexes above Njuncs) have zero degree of adjacency. */ - - for (i=1; i<=Njuncs; i++) - for (alink = Adjlist[i]; alink != NULL; alink = alink->next) - if (alink->node > 0) Degree[i]++; -} - - -int reordernodes() -/* -**-------------------------------------------------------------- -** Input: none -** Output: returns 1 if successful, 0 if not -** Purpose: re-orders nodes to minimize # of non-zeros that -** will appear in factorized solution matrix -**-------------------------------------------------------------- -*/ -{ - int k, knode, m, n; - for (k=1; k<=Nnodes; k++) - { - Row[k] = k; - Order[k] = k; - } - n = Njuncs; - for (k=1; k<=n; k++) /* Examine each junction */ - { - m = mindegree(k,n); /* Node with lowest degree */ - knode = Order[m]; /* Node's index */ - if (!growlist(knode)) return(101); /* Augment adjacency list */ - Order[m] = Order[k]; /* Switch order of nodes */ - Order[k] = knode; - Degree[knode] = 0; /* In-activate node */ - } - for (k=1; k<=n; k++) /* Assign nodes to rows of */ - Row[Order[k]] = k; /* coeff. matrix */ - return(0); -} /* End of reordernodes */ - - -int mindegree(int k, int n) -/* -**-------------------------------------------------------------- -** Input: k = first node in list of active nodes -** n = total number of junction nodes -** Output: returns node index with fewest direct connections -** Purpose: finds active node with fewest direct connections -**-------------------------------------------------------------- -*/ -{ - int i, m; - int min = n, - imin = n; - - for (i=k; i<=n; i++) - { - m = Degree[Order[i]]; - if (m < min) - { - min = m; - imin = i; - } - } - return(imin); -} /* End of mindegree */ - - -int growlist(int knode) -/* -**-------------------------------------------------------------- -** Input: knode = node index -** Output: returns 1 if successful, 0 if not -** Purpose: creates new entries in knode's adjacency list for -** all unlinked pairs of active nodes that are -** adjacent to knode -**-------------------------------------------------------------- -*/ -{ - int node; - Padjlist alink; - - /* Iterate through all nodes connected to knode */ - for (alink = Adjlist[knode]; alink != NULL; alink = alink -> next) - { - node = alink->node; /* End node of connecting link */ - if (Degree[node] > 0) /* End node is active */ - { - Degree[node]--; /* Reduce degree of adjacency */ - if (!newlink(alink)) /* Add to adjacency list */ - return(0); - } - } - return(1); -} /* End of growlist */ - - -int newlink(Padjlist alink) -/* -**-------------------------------------------------------------- -** Input: alink = element of node's adjacency list -** Output: returns 1 if successful, 0 if not -** Purpose: links end of current adjacent link to end nodes of -** all links that follow it on adjacency list -**-------------------------------------------------------------- -*/ -{ - int inode, jnode; - Padjlist blink; - - /* Scan all entries in adjacency list that follow anode. */ - inode = alink->node; /* End node of connection to anode */ - for (blink = alink->next; blink != NULL; blink = blink->next) - { - jnode = blink->node; /* End node of next connection */ - - /* If jnode still active, and inode not connected to jnode, */ - /* then add a new connection between inode and jnode. */ - if (Degree[jnode] > 0) /* jnode still active */ - { - if (!linked(inode,jnode)) /* inode not linked to jnode */ - { - - /* Since new connection represents a non-zero coeff. */ - /* in the solution matrix, update the coeff. count. */ - Ncoeffs++; - - /* Update adjacency lists for inode & jnode to */ - /* reflect the new connection. */ - if (!addlink(inode,jnode,Ncoeffs)) return(0); - if (!addlink(jnode,inode,Ncoeffs)) return(0); - Degree[inode]++; - Degree[jnode]++; - } - } - } - return(1); -} /* End of newlink */ - - -int linked(int i, int j) -/* -**-------------------------------------------------------------- -** Input: i = node index -** j = node index -** Output: returns 1 if nodes i and j are linked, 0 if not -** Purpose: checks if nodes i and j are already linked. -**-------------------------------------------------------------- -*/ -{ - Padjlist alink; - for (alink = Adjlist[i]; alink != NULL; alink = alink->next) - if (alink->node == j) return(1); - return(0); -} /* End of linked */ - - -int addlink(int i, int j, int n) -/* -**-------------------------------------------------------------- -** Input: i = node index -** j = node index -** n = link index -** Output: returns 1 if successful, 0 if not -** Purpose: augments node i's adjacency list with node j -**-------------------------------------------------------------- -*/ -{ - Padjlist alink; - alink = (struct Sadjlist *) malloc(sizeof(struct Sadjlist)); - if (alink == NULL) return(0); - alink->node = j; - alink->link = n; - alink->next = Adjlist[i]; - Adjlist[i] = alink; - return(1); -} /* End of addlink */ - - -int storesparse(int n) -/* -**-------------------------------------------------------------- -** Input: n = number of rows in solution matrix -** Output: returns error code -** Purpose: stores row indexes of non-zeros of each column of -** lower triangular portion of factorized matrix -**-------------------------------------------------------------- -*/ -{ - Padjlist alink; - int i, ii, j, k, l, m; - int errcode = 0; - - /* Allocate sparse matrix storage */ - XLNZ = (int *) calloc(n+2, sizeof(int)); - NZSUB = (int *) calloc(Ncoeffs+2, sizeof(int)); - LNZ = (int *) calloc(Ncoeffs+2, sizeof(int)); - ERRCODE(MEMCHECK(XLNZ)); - ERRCODE(MEMCHECK(NZSUB)); - ERRCODE(MEMCHECK(LNZ)); - if (errcode) return(errcode); - - /* Generate row index pointers for each column of matrix */ - k = 0; - XLNZ[1] = 1; - for (i=1; i<=n; i++) /* column */ - { - m = 0; - ii = Order[i]; - for (alink = Adjlist[ii]; alink != NULL; alink = alink->next) - { - j = Row[alink->node]; /* row */ - l = alink->link; - if (j > i && j <= n) - { - m++; - k++; - NZSUB[k] = j; - LNZ[k] = l; - } - } - XLNZ[i+1] = XLNZ[i] + m; - } - return(errcode); -} /* End of storesparse */ - - -int ordersparse(int n) -/* -**-------------------------------------------------------------- -** Input: n = number of rows in solution matrix -** Output: returns eror code -** Purpose: puts row indexes in ascending order in NZSUB -**-------------------------------------------------------------- -*/ -{ - int i, k; - int *xlnzt, *nzsubt, *lnzt, *nzt; - int errcode = 0; - - xlnzt = (int *) calloc(n+2, sizeof(int)); - nzsubt = (int *) calloc(Ncoeffs+2, sizeof(int)); - lnzt = (int *) calloc(Ncoeffs+2, sizeof(int)); - nzt = (int *) calloc(n+2, sizeof(int)); - ERRCODE(MEMCHECK(xlnzt)); - ERRCODE(MEMCHECK(nzsubt)); - ERRCODE(MEMCHECK(lnzt)); - ERRCODE(MEMCHECK(nzt)); - if (!errcode) - { - - /* Count # non-zeros in each row */ - for (i=1; i<=n; i++) nzt[i] = 0; - for (i=1; i<=n; i++) - { - for (k=XLNZ[i]; k= istrt) - { - - /* Before modification, update vectors 'first' */ - /* and 'link' for future modification steps. */ - first[k] = istrt; - isub = NZSUB[istrt]; - link[k] = link[isub]; - link[isub] = k; - - /* The actual mod is saved in vector 'temp'. */ - for (i=istrt; i<=istop; i++) - { - isub = NZSUB[i]; - temp[isub] += Aij[LNZ[i]]*ljk; - } - } - k = newk; - } - - /* Apply the modifications accumulated */ - /* in 'temp' to column L(*,j). */ - diagj = Aii[j] - diagj; - if (diagj <= 0.0) /* Check for ill-conditioning */ - { - errcode = j; - goto ENDLINSOLVE; - } - diagj = sqrt(diagj); - Aii[j] = diagj; - istrt = XLNZ[j]; - istop = XLNZ[j+1] - 1; - if (istop >= istrt) - { - first[j] = istrt; - isub = NZSUB[istrt]; - link[j] = link[isub]; - link[isub] = j; - for (i=istrt; i<=istop; i++) - { - isub = NZSUB[i]; - bj = (Aij[LNZ[i]] - temp[isub])/diagj; - Aij[LNZ[i]] = bj; - temp[isub] = 0.0; - } - } - } /* next j */ - - /* Foward substitution */ - for (j=1; j<=n; j++) - { - bj = B[j]/Aii[j]; - B[j] = bj; - istrt = XLNZ[j]; - istop = XLNZ[j+1] - 1; - if (istop >= istrt) - { - for (i=istrt; i<=istop; i++) - { - isub = NZSUB[i]; - B[isub] -= Aij[LNZ[i]]*bj; - } - } - } - - /* Backward substitution */ - for (j=n; j>=1; j--) - { - bj = B[j]; - istrt = XLNZ[j]; - istop = XLNZ[j+1] - 1; - if (istop >= istrt) - { - for (i=istrt; i<=istop; i++) - { - isub = NZSUB[i]; - bj -= Aij[LNZ[i]]*B[isub]; - } - } - B[j] = bj/Aii[j]; - } - -ENDLINSOLVE: - free(temp); - free(link); - free(first); - return(errcode); -} /* End of linsolve */ - - -/************************ END OF SMATRIX.C ************************/ - +/* +******************************************************************* + +SMATRIX.C -- Sparse matrix routines for EPANET program. + +VERSION: 2.00 +DATE: 5/8/00 +AUTHOR: L. Rossman + US EPA - NRMRL + +This module contains the sparse matrix routines used to solve +a network's hydraulic equations. The entry points into this +module are: + createsparse() -- called from openhyd() in HYDRAUL.C + freesparse() -- called from closehyd() in HYDRAUL.C + linsolve() -- called from netsolve() in HYDRAUL.C + +Createsparse() does the following: + 1. for each node, builds an adjacency list that identifies + all links connected to the node (see buildlists()) + 2. re-orders the network's nodes to minimize the number + of non-zero entries in the hydraulic solution matrix + (see reorder()) + 3. converts the adjacency lists into a compact scheme + for storing the non-zero coeffs. in the lower diagonal + portion of the solution matrix (see storesparse()) +Freesparse() frees the memory used for the sparse matrix. +Linsolve() solves the linearized system of hydraulic equations. + +******************************************************************** +*/ + +#include +#include +#ifndef __APPLE__ +#include +#else +#include +#endif +#include +#include "hash.h" +#include "text.h" +#include "types.h" +#include "epanet2.h" +#include "funcs.h" +#define EXTERN extern +#include "vars.h" + +int createsparse(EN_Project *pr) +/* +**-------------------------------------------------------------- +** Input: none +** Output: returns error code +** Purpose: creates sparse representation of coeff. matrix +**-------------------------------------------------------------- +*/ +{ + int errcode = 0; + EN_Network *n = &pr->network; + solver_t *s = &pr->hydraulics.solver; + + EN_Network *net = &pr->network; + hydraulics_t *hyd = &pr->hydraulics; + + /* Allocate data structures */ + ERRCODE(allocsparse(pr)); + + if (errcode) { + return(errcode); + } + + /* Build node-link adjacency lists with parallel links removed. */ + s->Degree = (int *) calloc(n->Nnodes+1, sizeof(int)); + ERRCODE(MEMCHECK(s->Degree)); + ERRCODE(buildlists(pr,TRUE)); + if (!errcode){ + xparalinks(pr); /* Remove parallel links */ + countdegree(pr); /* Find degree of each junction */ + } /* (= # of adjacent links) */ + + /* Re-order nodes to minimize number of non-zero coeffs. */ + /* in factorized solution matrix. At same time, adjacency */ + /* list is updated with links representing non-zero coeffs. */ + hyd->Ncoeffs = n->Nlinks; + ERRCODE(reordernodes(pr)); + + /* Allocate memory for sparse storage of positions of non-zero */ + /* coeffs. and store these positions in vector NZSUB. */ + ERRCODE(storesparse(pr,net->Njuncs)); + + /* Free memory used for adjacency lists and sort */ + /* row indexes in NZSUB to optimize linsolve(). */ + if (!errcode) { + freelists(pr); + } + ERRCODE(ordersparse(hyd,net->Njuncs)); + + /* Re-build adjacency lists without removing parallel */ + /* links for use in future connectivity checking. */ + ERRCODE(buildlists(pr,FALSE)); + + /* Free allocated memory */ + free(s->Degree); + return(errcode); +} /* End of createsparse */ + + +int allocsparse(EN_Project *pr) +/* +**-------------------------------------------------------------- +** Input: none +** Output: returns error code +** Purpose: allocates memory for indexing the solution matrix +**-------------------------------------------------------------- +*/ +{ + EN_Network *n = &pr->network; + solver_t *s = &pr->hydraulics.solver; + + int errcode = 0; + n->Adjlist = (Padjlist *) calloc(n->Nnodes+1, sizeof(Padjlist)); + s->Order = (int *) calloc(n->Nnodes+1, sizeof(int)); + s->Row = (int *) calloc(n->Nnodes+1, sizeof(int)); + s->Ndx = (int *) calloc(n->Nlinks+1, sizeof(int)); + ERRCODE(MEMCHECK(n->Adjlist)); + ERRCODE(MEMCHECK(s->Order)); + ERRCODE(MEMCHECK(s->Row)); + ERRCODE(MEMCHECK(s->Ndx)); + return(errcode); +} + + +void freesparse(EN_Project *pr) +/* +**---------------------------------------------------------------- +** Input: None +** Output: None +** Purpose: Frees memory used for sparse matrix storage +**---------------------------------------------------------------- +*/ +{ + EN_Network *n = &pr->network; + solver_t *s = &pr->hydraulics.solver; + + freelists(pr); + free(n->Adjlist); + free(s->Order); + free(s->Row); + free(s->Ndx); + free(s->XLNZ); + free(s->NZSUB); + free(s->LNZ); +} /* End of freesparse */ + + +int buildlists(EN_Project *pr, int paraflag) +/* +**-------------------------------------------------------------- +** Input: paraflag = TRUE if list marks parallel links +** Output: returns error code +** Purpose: builds linked list of links adjacent to each node +**-------------------------------------------------------------- +*/ +{ + int i,j,k; + int pmark = 0; + int errcode = 0; + Padjlist alink; + + EN_Network *n = &pr->network; + + /* For each link, update adjacency lists of its end nodes */ + for (k=1; k <= n->Nlinks; k++) + { + i = n->Link[k].N1; + j = n->Link[k].N2; + if (paraflag) { + pmark = paralink(pr,i,j,k); /* Parallel link check */ + } + + /* Include link in start node i's list */ + alink = (struct Sadjlist *) malloc(sizeof(struct Sadjlist)); + if (alink == NULL) return(101); + if (!pmark) alink->node = j; + else alink->node = 0; /* Parallel link marker */ + alink->link = k; + alink->next = n->Adjlist[i]; + n->Adjlist[i] = alink; + + /* Include link in end node j's list */ + alink = (struct Sadjlist *) malloc(sizeof(struct Sadjlist)); + if (alink == NULL) return(101); + if (!pmark) alink->node = i; + else alink->node = 0; /* Parallel link marker */ + alink->link = k; + alink->next = n->Adjlist[j]; + n->Adjlist[j] = alink; + } + return(errcode); +} /* End of buildlists */ + + +int paralink(EN_Project *pr, int i, int j, int k) +/* +**-------------------------------------------------------------- +** Input: i = index of start node of link +** j = index of end node of link +** k = link index +** Output: returns 1 if link k parallels another link, else 0 +** Purpose: checks for parallel links between nodes i and j +** +**-------------------------------------------------------------- +*/ +{ + Padjlist alink; + for (alink = pr->network.Adjlist[i]; alink != NULL; alink = alink->next) + { + if (alink->node == j) /* Link || to k (same end nodes) */ + { + pr->hydraulics.solver.Ndx[k] = alink->link; /* Assign Ndx entry to this link */ + return(1); + } + } + pr->hydraulics.solver.Ndx[k] = k; /* Ndx entry if link not parallel */ + return(0); +} /* End of paralink */ + + +void xparalinks(EN_Project *pr) +/* +**-------------------------------------------------------------- +** Input: none +** Output: none +** Purpose: removes parallel links from nodal adjacency lists +**-------------------------------------------------------------- +*/ +{ + int i; + Padjlist alink, /* Current item in adjacency list */ + blink; /* Previous item in adjacency list */ + EN_Network *n = &pr->network; + + /* Scan adjacency list of each node */ + for (i=1; i <= n->Nnodes; i++) + { + alink = n->Adjlist[i]; /* First item in list */ + blink = NULL; + while (alink != NULL) + { + if (alink->node == 0) /* Parallel link marker found */ + { + if (blink == NULL) /* This holds at start of list */ + { + n->Adjlist[i] = alink->next; + free(alink); /* Remove item from list */ + alink = n->Adjlist[i]; + } + else /* This holds for interior of list */ + { + blink->next = alink->next; + free(alink); /* Remove item from list */ + alink = blink->next; + } + } + else + { + blink = alink; /* Move to next item in list */ + alink = alink->next; + } + } + } +} /* End of xparalinks */ + + +void freelists(EN_Project *pr) +/* +**-------------------------------------------------------------- +** Input: none +** Output: none +** Purpose: frees memory used for nodal adjacency lists +**-------------------------------------------------------------- +*/ +{ + int i; + Padjlist alink; + EN_Network *n = &pr->network; + + + for (i=0; i <= n->Nnodes; i++) + { + for (alink = n->Adjlist[i]; alink != NULL; alink = n->Adjlist[i]) + { + n->Adjlist[i] = alink->next; + free(alink); + } + } +} /* End of freelists */ + + +void countdegree(EN_Project *pr) +/* +**---------------------------------------------------------------- +** Input: none +** Output: none +** Purpose: counts number of nodes directly connected to each node +**---------------------------------------------------------------- +*/ +{ + int i; + Padjlist alink; + EN_Network *n = &pr->network; + memset(pr->hydraulics.solver.Degree,0,(n->Nnodes+1) * sizeof(int)); + + /* NOTE: For purposes of node re-ordering, Tanks (nodes with */ + /* indexes above Njuncs) have zero degree of adjacency. */ + + for (i=1; i <= n->Njuncs; i++) { + for (alink = n->Adjlist[i]; alink != NULL; alink = alink->next) { + if (alink->node > 0) { + pr->hydraulics.solver.Degree[i]++; + } + } + } +} + + +int reordernodes(EN_Project *pr) +/* +**-------------------------------------------------------------- +** Input: none +** Output: returns 1 if successful, 0 if not +** Purpose: re-orders nodes to minimize # of non-zeros that +** will appear in factorized solution matrix +**-------------------------------------------------------------- +*/ +{ + int k, knode, m, n; + EN_Network *net = &pr->network; + solver_t *s = &pr->hydraulics.solver; + + for (k=1; k <= net->Nnodes; k++) + { + s->Row[k] = k; + s->Order[k] = k; + } + n = net->Njuncs; + for (k=1; k<=n; k++) /* Examine each junction */ + { + m = mindegree(s,k,n); /* Node with lowest degree */ + knode = s->Order[m]; /* Node's index */ + if (!growlist(pr,knode)) { + return(101); /* Augment adjacency list */ + } + s->Order[m] = s->Order[k]; /* Switch order of nodes */ + s->Order[k] = knode; + s->Degree[knode] = 0; /* In-activate node */ + } + for (k=1; k<=n; k++) { /* Assign nodes to rows of */ + s->Row[s->Order[k]] = k; /* coeff. matrix */ + } + return(0); +} /* End of reordernodes */ + + +int mindegree(solver_t *s, int k, int n) +/* +**-------------------------------------------------------------- +** Input: k = first node in list of active nodes +** n = total number of junction nodes +** Output: returns node index with fewest direct connections +** Purpose: finds active node with fewest direct connections +**-------------------------------------------------------------- +*/ +{ + int i, m; + int min = n, + imin = n; + + for (i=k; i<=n; i++) + { + m = s->Degree[s->Order[i]]; + if (m < min) + { + min = m; + imin = i; + } + } + return(imin); +} /* End of mindegree */ + + +int growlist(EN_Project *pr, int knode) +/* +**-------------------------------------------------------------- +** Input: knode = node index +** Output: returns 1 if successful, 0 if not +** Purpose: creates new entries in knode's adjacency list for +** all unlinked pairs of active nodes that are +** adjacent to knode +**-------------------------------------------------------------- +*/ +{ + int node; + Padjlist alink; + EN_Network *n = &pr->network; + solver_t *s = &pr->hydraulics.solver; + + /* Iterate through all nodes connected to knode */ + for (alink = n->Adjlist[knode]; alink != NULL; alink = alink -> next) + { + node = alink->node; /* End node of connecting link */ + if (s->Degree[node] > 0) /* End node is active */ + { + s->Degree[node]--; /* Reduce degree of adjacency */ + if (!newlink(pr,alink)) { /* Add to adjacency list */ + return(0); + } + } + } + return(1); +} /* End of growlist */ + + +int newlink(EN_Project *pr, Padjlist alink) +/* +**-------------------------------------------------------------- +** Input: alink = element of node's adjacency list +** Output: returns 1 if successful, 0 if not +** Purpose: links end of current adjacent link to end nodes of +** all links that follow it on adjacency list +**-------------------------------------------------------------- +*/ +{ + int inode, jnode; + Padjlist blink; + EN_Network *n = &pr->network; + hydraulics_t *hyd = &pr->hydraulics; + solver_t *s = &pr->hydraulics.solver; + + /* Scan all entries in adjacency list that follow anode. */ + inode = alink->node; /* End node of connection to anode */ + for (blink = alink->next; blink != NULL; blink = blink->next) + { + jnode = blink->node; /* End node of next connection */ + + /* If jnode still active, and inode not connected to jnode, */ + /* then add a new connection between inode and jnode. */ + if (s->Degree[jnode] > 0) /* jnode still active */ + { + if (!linked(n, inode,jnode)) { /* inode not linked to jnode */ + /* Since new connection represents a non-zero coeff. */ + /* in the solution matrix, update the coeff. count. */ + hyd->Ncoeffs++; + + /* Update adjacency lists for inode & jnode to */ + /* reflect the new connection. */ + if (!addlink(n,inode,jnode,hyd->Ncoeffs)) { + return(0); + } + if (!addlink(n,jnode,inode,hyd->Ncoeffs)) { + return(0); + } + s->Degree[inode]++; + s->Degree[jnode]++; + } + } + } + return(1); +} /* End of newlink */ + + +int linked(EN_Network *n, int i, int j) +/* +**-------------------------------------------------------------- +** Input: i = node index +** j = node index +** Output: returns 1 if nodes i and j are linked, 0 if not +** Purpose: checks if nodes i and j are already linked. +**-------------------------------------------------------------- +*/ +{ + Padjlist alink; + for (alink = n->Adjlist[i]; alink != NULL; alink = alink->next) { + if (alink->node == j) { + return(1); + } + } + return(0); +} /* End of linked */ + + +int addlink(EN_Network *net, int i, int j, int n) +/* +**-------------------------------------------------------------- +** Input: i = node index +** j = node index +** n = link index +** Output: returns 1 if successful, 0 if not +** Purpose: augments node i's adjacency list with node j +**-------------------------------------------------------------- +*/ +{ + Padjlist alink; + alink = (struct Sadjlist *) malloc(sizeof(struct Sadjlist)); + if (alink == NULL) return(0); + alink->node = j; + alink->link = n; + alink->next = net->Adjlist[i]; + net->Adjlist[i] = alink; + return(1); +} /* End of addlink */ + + +int storesparse(EN_Project *pr, int n) +/* +**-------------------------------------------------------------- +** Input: n = number of rows in solution matrix +** Output: returns error code +** Purpose: stores row indexes of non-zeros of each column of +** lower triangular portion of factorized matrix +**-------------------------------------------------------------- +*/ +{ + Padjlist alink; + int i, ii, j, k, l, m; + int errcode = 0; + + EN_Network *net = &pr->network; + hydraulics_t *hyd = &pr->hydraulics; + solver_t *s = &pr->hydraulics.solver; + + /* Allocate sparse matrix storage */ + s->XLNZ = (int *) calloc(n+2, sizeof(int)); + s->NZSUB = (int *) calloc(hyd->Ncoeffs+2, sizeof(int)); + s->LNZ = (int *) calloc(hyd->Ncoeffs+2, sizeof(int)); + ERRCODE(MEMCHECK(s->XLNZ)); + ERRCODE(MEMCHECK(s->NZSUB)); + ERRCODE(MEMCHECK(s->LNZ)); + if (errcode) { + return(errcode); + } + + /* Generate row index pointers for each column of matrix */ + k = 0; + s->XLNZ[1] = 1; + for (i=1; i<=n; i++) { /* column */ + m = 0; + ii = s->Order[i]; + for (alink = net->Adjlist[ii]; alink != NULL; alink = alink->next) + { + j = s->Row[alink->node]; /* row */ + l = alink->link; + if (j > i && j <= n) { + m++; + k++; + s->NZSUB[k] = j; + s->LNZ[k] = l; + } + } + s->XLNZ[i+1] = s->XLNZ[i] + m; + } + return(errcode); +} /* End of storesparse */ + + +int ordersparse(hydraulics_t *h, int n) +/* +**-------------------------------------------------------------- +** Input: n = number of rows in solution matrix +** Output: returns eror code +** Purpose: puts row indexes in ascending order in NZSUB +**-------------------------------------------------------------- +*/ +{ + int i, k; + int *xlnzt, *nzsubt, *lnzt, *nzt; + int errcode = 0; + solver_t *s = &h->solver; + + xlnzt = (int *) calloc(n+2, sizeof(int)); + nzsubt = (int *) calloc(h->Ncoeffs+2, sizeof(int)); + lnzt = (int *) calloc(h->Ncoeffs+2, sizeof(int)); + nzt = (int *) calloc(n+2, sizeof(int)); + ERRCODE(MEMCHECK(xlnzt)); + ERRCODE(MEMCHECK(nzsubt)); + ERRCODE(MEMCHECK(lnzt)); + ERRCODE(MEMCHECK(nzt)); + if (!errcode) { + + /* Count # non-zeros in each row */ + for (i=1; i<=n; i++) { + nzt[i] = 0; + } + for (i=1; i<=n; i++) { + for (k = s->XLNZ[i]; k < s->XLNZ[i+1]; k++) nzt[s->NZSUB[k]]++; + } + xlnzt[1] = 1; + for (i=1; i<=n; i++) xlnzt[i+1] = xlnzt[i] + nzt[i]; + + /* Transpose matrix twice to order column indexes */ + transpose(n,s->XLNZ,s->NZSUB,s->LNZ,xlnzt,nzsubt,lnzt,nzt); + transpose(n,xlnzt,nzsubt,lnzt,s->XLNZ,s->NZSUB,s->LNZ,nzt); + } + + /* Reclaim memory */ + free(xlnzt); + free(nzsubt); + free(lnzt); + free(nzt); + return(errcode); +} /* End of ordersparse */ + + +void transpose(int n, int *il, int *jl, int *xl, int *ilt, int *jlt, + int *xlt, int *nzt) +/* +**--------------------------------------------------------------------- +** Input: n = matrix order +** il,jl,xl = sparse storage scheme for original matrix +** nzt = work array +** Output: ilt,jlt,xlt = sparse storage scheme for transposed matrix +** Purpose: Determines sparse storage scheme for transpose of a matrix +**--------------------------------------------------------------------- +*/ +{ + int i, j, k, kk; + + for (i=1; i<=n; i++) nzt[i] = 0; + for (i=1; i<=n; i++) + { + for (k=il[i]; kF = solution values +** returns 0 if solution found, or index of +** equation causing system to be ill-conditioned +** Purpose: solves sparse symmetric system of linear +** equations using Cholesky factorization +** +** NOTE: This procedure assumes that the solution matrix has +** been symbolically factorized with the positions of +** the lower triangular, off-diagonal, non-zero coeffs. +** stored in the following integer arrays: +** XLNZ (start position of each column in NZSUB) +** NZSUB (row index of each non-zero in each column) +** LNZ (position of each NZSUB entry in Aij array) +** +** This procedure has been adapted from subroutines GSFCT and +** GSSLV in the book "Computer Solution of Large Sparse +** Positive Definite Systems" by A. George and J. W-H Liu +** (Prentice-Hall, 1981). +**-------------------------------------------------------------- +*/ +{ + + double *Aii = s->Aii; + double *Aij = s->Aij; + double *B = s->F; + int *LNZ = s->LNZ; + int *XLNZ = s->XLNZ; + int *NZSUB = s->NZSUB; + + int *link, *first; + int i, istop, istrt, isub, j, k, kfirst, newk; + int errcode = 0; + double bj, diagj, ljk; + double *temp; + + temp = (double *) calloc(n+1, sizeof(double)); + link = (int *) calloc(n+1,sizeof(int)); + first = (int *) calloc(n+1,sizeof(int)); + ERRCODE(MEMCHECK(temp)); + ERRCODE(MEMCHECK(link)); + ERRCODE(MEMCHECK(first)); + if (errcode) + { + errcode = -errcode; + goto ENDLINSOLVE; + } + memset(temp,0,(n+1)*sizeof(double)); + memset(link,0,(n+1)*sizeof(int)); + + /* Begin numerical factorization of matrix A into L */ + /* Compute column L(*,j) for j = 1,...n */ + for (j=1; j<=n; j++) + { + /* For each column L(*,k) that affects L(*,j): */ + diagj = 0.0; + newk = link[j]; + k = newk; + while (k != 0) + { + + /* Outer product modification of L(*,j) by */ + /* L(*,k) starting at first[k] of L(*,k). */ + newk = link[k]; + kfirst = first[k]; + ljk = Aij[LNZ[kfirst]]; + diagj += ljk*ljk; + istrt = kfirst + 1; + istop = XLNZ[k+1] - 1; + if (istop >= istrt) + { + + /* Before modification, update vectors 'first' */ + /* and 'link' for future modification steps. */ + first[k] = istrt; + isub = NZSUB[istrt]; + link[k] = link[isub]; + link[isub] = k; + + /* The actual mod is saved in vector 'temp'. */ + for (i=istrt; i<=istop; i++) + { + isub = NZSUB[i]; + temp[isub] += Aij[LNZ[i]]*ljk; + } + } + k = newk; + } + + /* Apply the modifications accumulated */ + /* in 'temp' to column L(*,j). */ + diagj = Aii[j] - diagj; + if (diagj <= 0.0) /* Check for ill-conditioning */ + { + errcode = j; + goto ENDLINSOLVE; + } + diagj = sqrt(diagj); + Aii[j] = diagj; + istrt = XLNZ[j]; + istop = XLNZ[j+1] - 1; + if (istop >= istrt) + { + first[j] = istrt; + isub = NZSUB[istrt]; + link[j] = link[isub]; + link[isub] = j; + for (i=istrt; i<=istop; i++) + { + isub = NZSUB[i]; + bj = (Aij[LNZ[i]] - temp[isub])/diagj; + Aij[LNZ[i]] = bj; + temp[isub] = 0.0; + } + } + } /* next j */ + + /* Foward substitution */ + for (j=1; j<=n; j++) + { + bj = B[j]/Aii[j]; + B[j] = bj; + istrt = XLNZ[j]; + istop = XLNZ[j+1] - 1; + if (istop >= istrt) + { + for (i=istrt; i<=istop; i++) + { + isub = NZSUB[i]; + B[isub] -= Aij[LNZ[i]]*bj; + } + } + } + + /* Backward substitution */ + for (j=n; j>=1; j--) + { + bj = B[j]; + istrt = XLNZ[j]; + istop = XLNZ[j+1] - 1; + if (istop >= istrt) + { + for (i=istrt; i<=istop; i++) + { + isub = NZSUB[i]; + bj -= Aij[LNZ[i]]*B[isub]; + } + } + B[j] = bj/Aii[j]; + } + +ENDLINSOLVE: + free(temp); + free(link); + free(first); + return(errcode); +} /* End of linsolve */ + + +/************************ END OF SMATRIX.C ************************/ + diff --git a/src/text.h b/src/text.h index caaf552..be47e39 100755 --- a/src/text.h +++ b/src/text.h @@ -1,543 +1,477 @@ -/* -**************************************************** - - String Constants for EPANET Program - -VERSION: 2.00 -DATE: 5/8/00 - 10/25/00 - 8/15/07 (2.00.11) - 2/14/08 (2.00.12) -AUTHOR: L. Rossman - US EPA - NRMRL - -**************************************************** -*/ -/* ------------ Keyword Dictionary ---------- */ -#ifndef TEXT_H -#define TEXT_H - -#define w_USE "USE" -#define w_SAVE "SAVE" - -#define w_NONE "NONE" -#define w_ALL "ALL" - -#define w_CHEM "CHEM" -#define w_AGE "AGE" -#define w_TRACE "TRACE" - -//#define w_SYSTEM "SYST" -#define w_SYSTEM "SYSTEM" -#define w_JUNC "Junc" -#define w_RESERV "Reser" -#define w_TANK "Tank" -#define w_CV "CV" -#define w_PIPE "Pipe" -#define w_PUMP "Pump" -#define w_VALVE "Valve" -#define w_PRV "PRV" -#define w_PSV "PSV" -#define w_PBV "PBV" -#define w_FCV "FCV" -#define w_TCV "TCV" -#define w_GPV "GPV" - -#define w_OPEN "OPEN" -#define w_CLOSED "CLOSED" -#define w_ACTIVE "ACTIVE" -#define w_TIME "TIME" -#define w_ABOVE "ABOVE" -#define w_BELOW "BELOW" -#define w_PRECISION "PREC" -#define w_IS "IS" -#define w_NOT "NOT" - -#define w_ADD "ADD" -#define w_MULTIPLY "MULT" - -#define w_LIMITING "LIMIT" -#define w_ORDER "ORDER" -#define w_GLOBAL "GLOB" -#define w_BULK "BULK" -#define w_WALL "WALL" - -#define w_PAGE "PAGE" -#define w_STATUS "STATUS" -#define w_SUMMARY "SUMM" -#define w_MESSAGES "MESS" -#define w_ENERGY "ENER" -#define w_NODE "NODE" -#define w_LINK "LINK" -#define w_FILE "FILE" -#define w_YES "YES" -#define w_NO "NO" -#define w_FULL "FULL" - -#define w_HW "H-W" -#define w_DW "D-W" -#define w_CM "C-M" - -#define w_CFS "CFS" -#define w_GPM "GPM" -#define w_MGD "MGD" -#define w_IMGD "IMGD" -#define w_AFD "AFD" -#define w_LPS "LPS" -#define w_LPM "LPM" -#define w_MLD "MLD" -#define w_CMH "CMH" -#define w_CMD "CMD" -#define w_SI "SI" - -#define w_PSI "PSI" -#define w_KPA "KPA" -#define w_METERS "METERS" - -#define w_ELEV "ELEV" -#define w_DEMAND "DEMAND" -#define w_HEAD "HEAD" -#define w_PRESSURE "PRESSURE" -#define w_QUALITY "QUAL" - -#define w_DIAM "DIAM" -#define w_FLOW "FLOW" -#define w_ROUGHNESS "ROUG" -#define w_VELOCITY "VELO" -#define w_HEADLOSS "HEADL" -#define w_SETTING "SETTING" -#define w_POWER "POWER" -#define w_VOLUME "VOLU" -#define w_CLOCKTIME "CLOCKTIME" -#define w_FILLTIME "FILLTIME" -#define w_DRAINTIME "DRAINTIME" -#define w_GRADE "GRADE" -#define w_LEVEL "LEVEL" - -#define w_DURATION "DURA" -#define w_HYDRAULIC "HYDR" -#define w_MINIMUM "MINI" -#define w_PATTERN "PATT" -#define w_REPORT "REPO" -#define w_START "STAR" - -#define w_UNITS "UNIT" -#define w_MAP "MAP" -#define w_VERIFY "VERI" -#define w_VISCOSITY "VISC" -#define w_DIFFUSIVITY "DIFF" -#define w_SPECGRAV "SPEC" -#define w_TRIALS "TRIAL" -#define w_ACCURACY "ACCU" -#define w_SEGMENTS "SEGM" -#define w_TOLERANCE "TOLER" -#define w_EMITTER "EMIT" - -#define w_PRICE "PRICE" -#define w_DMNDCHARGE "DEMAN" - -#define w_HTOL "HTOL" -#define w_QTOL "QTOL" -#define w_RQTOL "RQTOL" -#define w_CHECKFREQ "CHECKFREQ" -#define w_MAXCHECK "MAXCHECK" -#define w_DAMPLIMIT "DAMPLIMIT" //(2.00.12 - LR) - -#define w_SECONDS "SEC" -#define w_MINUTES "MIN" -#define w_HOURS "HOU" -#define w_DAYS "DAY" -#define w_AM "AM" -#define w_PM "PM" - -#define w_CONCEN "CONCEN" -#define w_MASS "MASS" -#define w_SETPOINT "SETPOINT" -#define w_FLOWPACED "FLOWPACED" - -#define w_PATTERN "PATT" -#define w_CURVE "CURV" - -#define w_EFFIC "EFFI" -#define w_HEAD "HEAD" -#define w_POWER "POWER" -#define w_SPEED "SPEE" - -#define w_MIXED "MIXED" -#define w_2COMP "2COMP" -#define w_FIFO "FIFO" -#define w_LIFO "LIFO" - -#define w_STATISTIC "STAT" -#define w_AVG "AVERAGE" -#define w_MIN "MINIMUM" -#define w_MAX "MAXIMUM" -#define w_RANGE "RANGE" - -#define w_UNBALANCED "UNBA" -#define w_STOP "STOP" -#define w_CONTINUE "CONT" - -#define w_RULE "RULE" -#define w_IF "IF" -#define w_AND "AND" -#define w_OR "OR" -#define w_THEN "THEN" -#define w_ELSE "ELSE" -#define w_PRIORITY "PRIO" - -/* ---------Input Section Names ---------- */ -#define s_TITLE "[TITL" -#define s_JUNCTIONS "[JUNC" -#define s_RESERVOIRS "[RESE" -#define s_TANKS "[TANK" -#define s_PIPES "[PIPE" -#define s_PUMPS "[PUMP" -#define s_VALVES "[VALV" -#define s_CONTROLS "[CONT" -#define s_RULES "[RULE" -#define s_DEMANDS "[DEMA" -#define s_SOURCES "[SOUR" -#define s_EMITTERS "[EMIT" -#define s_PATTERNS "[PATT" -#define s_CURVES "[CURV" -#define s_QUALITY "[QUAL" -#define s_STATUS "[STAT" -#define s_ROUGHNESS "[ROUG" -#define s_ENERGY "[ENER" -#define s_REACTIONS "[REAC" -#define s_MIXING "[MIXI" -#define s_REPORT "[REPO" -#define s_TIMES "[TIME" -#define s_OPTIONS "[OPTI" -#define s_COORDS "[COOR" -#define s_VERTICES "[VERT" -#define s_LABELS "[LABE" -#define s_BACKDROP "[BACK" -#define s_TAGS "[TAGS" -#define s_END "[END" - -/* ---------------- Units ---------------- */ -/*** Limit units to MAXID or less characters ***/ -#define u_CFS "cfs" -#define u_GPM "gpm" -#define u_AFD "a-f/d" -#define u_MGD "mgd" -#define u_IMGD "Imgd" -#define u_LPS "L/s" -#define u_LPM "Lpm" -#define u_CMH "m3/h" -#define u_CMD "m3/d" -#define u_MLD "ML/d" -#define u_MGperL "mg/L" -#define u_UGperL "ug/L" -#define u_HOURS "hrs" -#define u_MINUTES "min" -#define u_PERCENT "% from" -#define u_METERS "m" -#define u_MMETERS "mm" -#define u_MperSEC "m/s" -#define u_SQMperSEC "sq m/sec" -#define u_per1000M "/1000m" -#define u_KW "kw" -#define u_FEET "ft" -#define u_INCHES "in" -#define u_PSI "psi" -#define u_KPA "kPa" -#define u_FTperSEC "fps" -#define u_SQFTperSEC "sq ft/sec" -#define u_per1000FT "/1000ft" -#define u_HP "hp" - -/* -------------- Curve Types ----------------- */ -#define c_HEADLOSS "HEADLOSS" -#define c_PUMP "PUMP" -#define c_EFFIC "EFFIC" -#define c_VOLUME "VOLUME" - -/* ------------------ Text Phrases ------------------- */ -#define t_ABOVE "above" -#define t_BELOW "below" -#define t_HW "Hazen-Williams" -#define t_DW "Darcy-Weisbach" -#define t_CM "Chezy-Manning" -#define t_CHEMICAL "Chemical" -#define t_XHEAD "closed because cannot deliver head" -#define t_TEMPCLOSED "temporarily closed" -#define t_CLOSED "closed" -#define t_OPEN "open" -#define t_ACTIVE "active" -#define t_XFLOW "open but exceeds maximum flow" -#define t_XFCV "open but cannot deliver flow" -#define t_XPRESSURE "open but cannot deliver pressure" -#define t_FILLING "filling" -#define t_EMPTYING "emptying" - -#define t_ELEV "Elevation" -#define t_DEMAND "Demand" -#define t_HEAD "Head" -#define t_PRESSURE "Pressure" -#define t_QUALITY "Quality" -#define t_LENGTH "Length" -#define t_DIAM "Diameter" -#define t_FLOW "Flow" -#define t_VELOCITY "Velocity" -#define t_HEADLOSS "Headloss" -#define t_LINKQUAL "Quality" -#define t_LINKSTATUS "State" -#define t_SETTING "Setting" -#define t_REACTRATE "Reaction" -#define t_FRICTION "F-Factor" - -#define t_NODEID "Node" -#define t_LINKID "Link" -#define t_PERDAY "/day" - -#define t_JUNCTION "Junction" -#define t_RESERVOIR "Reservoir" -#define t_TANK "Tank" -#define t_PIPE "Pipe" -#define t_PUMP "Pump" -#define t_VALVE "Valve" -#define t_CONTROL "Control" -#define t_RULE "Rule" -#define t_DEMANDFOR "Demand for Node" -#define t_SOURCE "Source" -#define t_EMITTER "Emitter" -#define t_PATTERN "Pattern" -#define t_CURVE "Curve" -#define t_STATUS "Status" -#define t_ROUGHNESS "Roughness" -#define t_ENERGY "Energy" -#define t_REACTION "Reaction" -#define t_MIXING "Mixing" -#define t_REPORT "Report" -#define t_TIME "Times" -#define t_OPTION "Options" -#define t_RULES_SECT "[RULES] section" -#define t_HALTED " EXECUTION HALTED." -#define t_FUNCCALL "function call" -#define t_CONTINUED " (continued)" -#define t_perM3 " /m3" -#define t_perMGAL "/Mgal" -#define t_DIFFER "DIFFERENTIAL" - - -/* ------------------ Format Messages ------------------*/ -#define LOGO1 \ -"******************************************************************" -#define LOGO2 \ -"* E P A N E T *" -#define LOGO3 \ -"* Hydraulic and Water Quality *" -#define LOGO4 \ -"* Analysis for Pipe Networks *" -#define LOGO5 \ -"* Version %d.%d *" -#define LOGO6 \ -"******************************************************************" -#define FMT01 "\n... EPANET Version %d.%d\n" -#define FMT02 "\n o Retrieving network data" -#define FMT03 "\n Correct syntax is:\n epanet \n" -#define FMT04 "\n Cannot use duplicate file names." -#define FMT05 "\n Cannot open input file " -#define FMT06 "\n Cannot open report file " -#define FMT07 "\n Cannot open output file " -#define FMT08 "\n Cannot open temporary output file" -#define FMT09 "\n\n... EPANET completed.\n" -#define FMT10 "\n\n... EPANET completed. There are warnings.\n" -#define FMT11 "\n\n... EPANET completed. There are errors.\n" -#define FMT14 "\n o Computing hydraulics at hour " -#define FMT15 "\n o Computing water quality at hour " -#define FMT16 "\n o Transferring results to file" -#define FMT17 "\n o Writing output report to " -#define FMT18 " Page 1 " -#define FMT19 " Input Data File ................... %s" -#define FMT20 " Number of Junctions................ %-d" -#define FMT21a " Number of Reservoirs............... %-d" -#define FMT21b " Number of Tanks ................... %-d" -#define FMT22 " Number of Pipes ................... %-d" -#define FMT23 " Number of Pumps ................... %-d" -#define FMT24 " Number of Valves .................. %-d" -#define FMT25 " Headloss Formula .................. %s" -#define FMT26 " Hydraulic Timestep ................ %-.2f %s" -#define FMT27 " Hydraulic Accuracy ................ %-.6f" - -#define FMT27a " Status Check Frequency ............ %-d" //(2.00.12 - LR) -#define FMT27b " Maximum Trials Checked ............ %-d" //(2.00.12 - LR) -#define FMT27c " Damping Limit Threshold ........... %-.6f" //(2.00.12 - LR) - -#define FMT28 " Maximum Trials .................... %-d" -#define FMT29 " Quality Analysis .................. None" -#define FMT30 " Quality Analysis .................. %s" -#define FMT31 " Quality Analysis .................. Trace From Node %s" -#define FMT32 " Quality Analysis .................. Age" -#define FMT33 " Water Quality Time Step ........... %-.2f min" -#define FMT34 " Water Quality Tolerance ........... %-.2f %s" -#define FMT36 " Specific Gravity .................. %-.2f" -#define FMT37a " Relative Kinematic Viscosity ...... %-.2f" -#define FMT37b " Relative Chemical Diffusivity ..... %-.2f" -#define FMT38 " Demand Multiplier ................. %-.2f" -#define FMT39 " Total Duration .................... %-.2f %s" -#define FMT40 " Reporting Criteria:" -#define FMT41 " No Nodes" -#define FMT42 " All Nodes" -#define FMT43 " Selected Nodes" -#define FMT44 " No Links" -#define FMT45 " All Links" -#define FMT46 " Selected Links" -#define FMT47 " with %s below %-.2f %s" -#define FMT48 " with %s above %-.2f %s" - -/* ---------- Status Report Format Strings ------------ */ -#define FMT49 "Hydraulic Status:" - -/*** Updated 6/24/02 ***/ -#define FMT50 "%10s: Tank %s is %s at %-.2f %s" -#define FMT51 "%10s: Reservoir %s is %s" -#define FMT52 "%10s: %s %s %s" -#define FMT53 "%10s: %s %s changed from %s to %s" -#define FMT54 "%10s: %s %s changed by %s %s control" -#define FMT55 "%10s: %s %s changed by timer control" -#define FMT56 " %s %s setting changed to %-.2f" -#define FMT57 " %s %s switched from %s to %s" -#define FMT58 "%10s: Balanced after %-d trials" -#define FMT59 "%10s: Unbalanced after %-d trials (flow change = %-.6f)" - -#define FMT60a " Max. flow imbalance is %.4f %s at Node %s" //(2.00.12 - LR) -#define FMT60b " Max. head imbalance is %.4f %s at Link %s" //(2.00.12 - LR) - -#define FMT61 "%10s: Valve %s caused ill-conditioning" -#define FMT62 "%10s: System ill-conditioned at node %s" -#define FMT63 "%10s: %s %s changed by rule %s" -#define FMT64 "%10s: Balancing the network:" -#define FMT65 " Trial %2d: relative flow change = %-.6f" -/*** End of update ***/ - -/* -------------------- Energy Report Table ------------------- */ -#define FMT71 "Energy Usage:" -#define FMT72 \ - " Usage Avg. Kw-hr Avg. Peak Cost" -#define FMT73 \ - "Pump Factor Effic. %s Kw Kw /day" -#define FMT74 "%38s Demand Charge: %9.2f" -#define FMT75 "%38s Total Cost: %9.2f" - -/* -------------------- Node Report Table --------------------- */ -#define FMT76 "%s Node Results:" -#define FMT77 "Node Results:" -#define FMT78 "Node Results at %s hrs:" - -/* -------------------- Link Report Table --------------------- */ -#define FMT79 "%s Link Results:" -#define FMT80 "Link Results:" -#define FMT81 "Link Results at %s hrs:" -#define FMT82 "\n\f\n Page %-d %60.60s\n" - -/* ------------------- Progress Messages ---------------------- */ -#define FMT100 "Retrieving network data..." -#define FMT101 "Computing hydraulics at hour %s" -#define FMT102 "Computing water quality at hour %s" -#define FMT103 "Saving results to file..." -#define FMT104 "Analysis begun %s" -#define FMT105 "Analysis ended %s" - -/*------------------- Error Messages --------------------*/ -#define ERR101 "System Error 101: insufficient memory available." -#define ERR102 "System Error 102: no network data available." -#define ERR103 "System Error 103: hydraulics not initialized." -#define ERR104 "System Error 104: no hydraulics for water quality analysis." -#define ERR105 "System Error 105: water quality not initialized." -#define ERR106 "System Error 106: no results saved to report on." -#define ERR107 "System Error 107: hydraulics supplied from external file." -#define ERR108 "System Error 108: cannot use external file while hydraulics solver is active." -#define ERR109 "System Error 109: cannot change time parameter when solver is active." -#define ERR110 "System Error 110: cannot solve network hydraulic equations." -#define ERR120 "System Error 120: cannot solve water quality transport equations." - -#define ERR200 "Input Error 200: one or more errors in input file." -#define ERR201 \ - "Input Error 201: syntax error in following line of [%s] section:" -#define ERR202 "Input Error 202: %s %s contains illegal numeric value." -#define ERR203 "Input Error 203: %s %s refers to undefined node." -#define ERR204 "Input Error 204: %s %s refers to undefined link." -#define ERR205 "Input Error 205: %s %s refers to undefined time pattern." -#define ERR206 "Input Error 206: %s %s refers to undefined curve." -#define ERR207 "Input Error 207: %s %s attempts to control a CV." - -#define ERR208 "Input Error 208: %s specified for undefined Node %s." -#define ERR209 "Input Error 209: illegal %s value for Node %s." -#define ERR210 "Input Error 210: %s specified for undefined Link %s." -#define ERR211 "Input Error 211: illegal %s value for Link %s." -#define ERR212 "Input Error 212: trace node %.0s %s is undefined." -#define ERR213 "Input Error 213: illegal option value in [%s] section:" -#define ERR214 \ - "Input Error 214: following line of [%s] section contains too many characters:" -#define ERR215 "Input Error 215: %s %s is a duplicate ID." -#define ERR216 "Input Error 216: %s data specified for undefined Pump %s." -#define ERR217 "Input Error 217: invalid %s data for Pump %s." -#define ERR219 "Input Error 219: %s %s illegally connected to a tank." -#define ERR220 "Input Error 220: %s %s illegally connected to another valve." - -/*** Updated on 10/25/00 ***/ -#define ERR222 "Input Error 222: %s %s has same start and end nodes." - -#define ERR223 "Input Error 223: not enough nodes in network" -#define ERR224 "Input Error 224: no tanks or reservoirs in network." -#define ERR225 "Input Error 225: invalid lower/upper levels for Tank %s." -#define ERR226 "Input Error 226: no head curve supplied for Pump %s." -#define ERR227 "Input Error 227: invalid head curve for Pump %s." -#define ERR230 "Input Error 230: Curve %s has nonincreasing x-values." -#define ERR233 "Input Error 233: Node %s is unconnected." -#define ERR240 "Input Error 240: %s %s refers to undefined source." -#define ERR241 "Input Error 241: %s %s refers to undefined control." -#define ERR250 "Input Error 250: function call contains invalid format." -#define ERR251 "Input Error 251: function call contains invalid parameter code." - -#define ERR253 "Input Error 253: Function call error - No such demand category index." -#define ERR254 "Input Error 254: Function call error - Node have no coordinates." -#define ERR255 "Input Error 255: Function call error - Coordinates were not loaded." - -#define ERR257 "Input Error 257: rule does not exist." -#define ERR258 "Input Error 258: condition or action index specified in rule does not exist." - -#define ERR301 "File Error 301: identical file names." -#define ERR302 "File Error 302: cannot open input file." -#define ERR303 "File Error 303: cannot open report file." -#define ERR304 "File Error 304: cannot open binary output file." -#define ERR305 "File Error 305: cannot open hydraulics file." -#define ERR306 "File Error 306: hydraulics file does not match network data." -#define ERR307 "File Error 307: cannot read hydraulics file." -#define ERR308 "File Error 308: cannot save results to file." -#define ERR309 "File Error 309: cannot save results to report file." - -#define ERR401 "Sync Error 401: Qstep is not dividable by Hstep. Can't sync." - -#define R_ERR201 "Input Error 201: syntax error in following line of " -#define R_ERR202 "Input Error 202: illegal numeric value in following line of " -#define R_ERR203 "Input Error 203: undefined node in following line of " -#define R_ERR204 "Input Error 204: undefined link in following line of " -#define R_ERR207 "Input Error 207: attempt to control a CV in following line of " - -#define R_ERR221 "Input Error 221: mis-placed clause in following line of " - -/*-------------------- Specific Warning Messages -------------------------*/ -#define WARN01 "WARNING: System unbalanced at %s hrs." -#define WARN02 \ -"WARNING: Maximum trials exceeded at %s hrs. System may be unstable." -#define WARN03a "WARNING: Node %s disconnected at %s hrs" -#define WARN03b "WARNING: %d additional nodes disconnected at %s hrs" -#define WARN03c "WARNING: System disconnected because of Link %s" -#define WARN04 "WARNING: Pump %s %s at %s hrs." -#define WARN05 "WARNING: %s %s %s at %s hrs." -#define WARN06 "WARNING: Negative pressures at %s hrs." - -/*-------------------- General Warning Messages -------------------------*/ -#define WARN1 "WARNING: System hydraulically unbalanced." -#define WARN2 "WARNING: System may be hydraulically unstable." -#define WARN3 "WARNING: System disconnected." -#define WARN4 "WARNING: Pumps cannot deliver enough flow or head." -#define WARN5 "WARNING: Valves cannot deliver enough flow." -#define WARN6 "WARNING: System has negative pressures." - -#endif \ No newline at end of file +/* +**************************************************** + + String Constants for EPANET Program + +VERSION: 2.00 +DATE: 5/8/00 + 10/25/00 + 8/15/07 (2.00.11) + 2/14/08 (2.00.12) +AUTHOR: L. Rossman + US EPA - NRMRL + +**************************************************** +*/ +/* ------------ Keyword Dictionary ---------- */ +#ifndef TEXT_H +#define TEXT_H + +#define w_USE "USE" +#define w_SAVE "SAVE" + +#define w_NONE "NONE" +#define w_ALL "ALL" + +#define w_CHEM "CHEM" +#define w_AGE "AGE" +#define w_TRACE "TRACE" + +#define w_SYSTEM "SYSTEM" +#define w_JUNC "Junc" +#define w_RESERV "Reser" +#define w_TANK "Tank" +#define w_CV "CV" +#define w_PIPE "Pipe" +#define w_PUMP "Pump" +#define w_VALVE "Valve" +#define w_PRV "PRV" +#define w_PSV "PSV" +#define w_PBV "PBV" +#define w_FCV "FCV" +#define w_TCV "TCV" +#define w_GPV "GPV" + +#define w_OPEN "OPEN" +#define w_CLOSED "CLOSED" +#define w_ACTIVE "ACTIVE" +#define w_TIME "TIME" +#define w_ABOVE "ABOVE" +#define w_BELOW "BELOW" +#define w_PRECISION "PREC" +#define w_IS "IS" +#define w_NOT "NOT" + +#define w_ADD "ADD" +#define w_MULTIPLY "MULT" + +#define w_LIMITING "LIMIT" +#define w_ORDER "ORDER" +#define w_GLOBAL "GLOB" +#define w_BULK "BULK" +#define w_WALL "WALL" + +#define w_PAGE "PAGE" +#define w_STATUS "STATUS" +#define w_SUMMARY "SUMM" +#define w_MESSAGES "MESS" +#define w_ENERGY "ENER" +#define w_NODE "NODE" +#define w_LINK "LINK" +#define w_FILE "FILE" +#define w_YES "YES" +#define w_NO "NO" +#define w_FULL "FULL" + +#define w_HW "H-W" +#define w_DW "D-W" +#define w_CM "C-M" + +#define w_CFS "CFS" +#define w_GPM "GPM" +#define w_MGD "MGD" +#define w_IMGD "IMGD" +#define w_AFD "AFD" +#define w_LPS "LPS" +#define w_LPM "LPM" +#define w_MLD "MLD" +#define w_CMH "CMH" +#define w_CMD "CMD" +#define w_SI "SI" + +#define w_PSI "PSI" +#define w_KPA "KPA" +#define w_METERS "METERS" + +#define w_ELEV "ELEV" +#define w_DEMAND "DEMAND" +#define w_HEAD "HEAD" +#define w_PRESSURE "PRESSURE" +#define w_QUALITY "QUAL" + +#define w_DIAM "DIAM" +#define w_FLOW "FLOW" +#define w_ROUGHNESS "ROUG" +#define w_VELOCITY "VELO" +#define w_HEADLOSS "HEADL" +#define w_SETTING "SETTING" +#define w_POWER "POWER" +#define w_VOLUME "VOLU" +#define w_CLOCKTIME "CLOCKTIME" +#define w_FILLTIME "FILLTIME" +#define w_DRAINTIME "DRAINTIME" +#define w_GRADE "GRADE" +#define w_LEVEL "LEVEL" + +#define w_DURATION "DURA" +#define w_HYDRAULIC "HYDR" +#define w_MINIMUM "MINI" +#define w_PATTERN "PATT" +#define w_REPORT "REPO" +#define w_START "STAR" + +#define w_UNITS "UNIT" +#define w_MAP "MAP" +#define w_VERIFY "VERI" +#define w_VISCOSITY "VISC" +#define w_DIFFUSIVITY "DIFF" +#define w_SPECGRAV "SPEC" +#define w_TRIALS "TRIAL" +#define w_ACCURACY "ACCU" +#define w_SEGMENTS "SEGM" +#define w_TOLERANCE "TOLER" +#define w_EMITTER "EMIT" + +#define w_PRICE "PRICE" +#define w_DMNDCHARGE "DEMAN" + +#define w_HTOL "HTOL" +#define w_QTOL "QTOL" +#define w_RQTOL "RQTOL" +#define w_CHECKFREQ "CHECKFREQ" +#define w_MAXCHECK "MAXCHECK" +#define w_DAMPLIMIT "DAMPLIMIT" + +#define w_SECONDS "SEC" +#define w_MINUTES "MIN" +#define w_HOURS "HOU" +#define w_DAYS "DAY" +#define w_AM "AM" +#define w_PM "PM" + +#define w_CONCEN "CONCEN" +#define w_MASS "MASS" +#define w_SETPOINT "SETPOINT" +#define w_FLOWPACED "FLOWPACED" + +#define w_PATTERN "PATT" +#define w_CURVE "CURV" + +#define w_EFFIC "EFFI" +#define w_HEAD "HEAD" +#define w_POWER "POWER" +#define w_SPEED "SPEE" + +#define w_MIXED "MIXED" +#define w_2COMP "2COMP" +#define w_FIFO "FIFO" +#define w_LIFO "LIFO" + +#define w_STATISTIC "STAT" +#define w_AVG "AVERAGE" +#define w_MIN "MINIMUM" +#define w_MAX "MAXIMUM" +#define w_RANGE "RANGE" + +#define w_UNBALANCED "UNBA" +#define w_STOP "STOP" +#define w_CONTINUE "CONT" + +#define w_RULE "RULE" +#define w_IF "IF" +#define w_AND "AND" +#define w_OR "OR" +#define w_THEN "THEN" +#define w_ELSE "ELSE" +#define w_PRIORITY "PRIO" + +/* ---------Input Section Names ---------- */ +#define s_TITLE "[TITLE]" +#define s_JUNCTIONS "[JUNCTIONS]" +#define s_RESERVOIRS "[RESERVOIRS]" +#define s_TANKS "[TANKS]" +#define s_PIPES "[PIPES]" +#define s_PUMPS "[PUMPS]" +#define s_VALVES "[VALVES]" +#define s_CONTROLS "[CONTROLS]" +#define s_RULES "[RULES]" +#define s_DEMANDS "[DEMANDS]" +#define s_SOURCES "[SOURCES]" +#define s_EMITTERS "[EMITTERS]" +#define s_PATTERNS "[PATTERNS]" +#define s_CURVES "[CURVES]" +#define s_QUALITY "[QUALITY]" +#define s_STATUS "[STATUS]" +#define s_ROUGHNESS "[ROUGHNESS]" +#define s_ENERGY "[ENERGY]" +#define s_REACTIONS "[REACTIONS]" +#define s_MIXING "[MIXING]" +#define s_REPORT "[REPORT]" +#define s_TIMES "[TIMES]" +#define s_OPTIONS "[OPTIONS]" +#define s_COORDS "[COORDINATES]" +#define s_VERTICES "[VERTICES]" +#define s_LABELS "[LABELS]" +#define s_BACKDROP "[BACKDROP]" +#define s_TAGS "[TAGS]" +#define s_END "[END]" + +/* ---------------- Units ---------------- */ +/*** Limit units to MAXID or less characters ***/ +#define u_CFS "cfs" +#define u_GPM "gpm" +#define u_AFD "a-f/d" +#define u_MGD "mgd" +#define u_IMGD "Imgd" +#define u_LPS "L/s" +#define u_LPM "Lpm" +#define u_CMH "m3/h" +#define u_CMD "m3/d" +#define u_MLD "ML/d" +#define u_MGperL "mg/L" +#define u_UGperL "ug/L" +#define u_HOURS "hrs" +#define u_MINUTES "min" +#define u_PERCENT "% from" +#define u_METERS "m" +#define u_MMETERS "mm" +#define u_MperSEC "m/s" +#define u_SQMperSEC "sq m/sec" +#define u_per1000M "/1000m" +#define u_KW "kw" +#define u_FEET "ft" +#define u_INCHES "in" +#define u_PSI "psi" +#define u_KPA "kPa" +#define u_FTperSEC "fps" +#define u_SQFTperSEC "sq ft/sec" +#define u_per1000FT "/1000ft" +#define u_HP "hp" + +/* -------------- Curve Types ----------------- */ +#define c_HEADLOSS "HEADLOSS" +#define c_PUMP "PUMP" +#define c_EFFIC "EFFIC" +#define c_VOLUME "VOLUME" + +/* ------------------ Text Phrases ------------------- */ +#define t_ABOVE "above" +#define t_BELOW "below" +#define t_HW "Hazen-Williams" +#define t_DW "Darcy-Weisbach" +#define t_CM "Chezy-Manning" +#define t_CHEMICAL "Chemical" +#define t_XHEAD "closed because cannot deliver head" +#define t_TEMPCLOSED "temporarily closed" +#define t_CLOSED "closed" +#define t_OPEN "open" +#define t_ACTIVE "active" +#define t_XFLOW "open but exceeds maximum flow" +#define t_XFCV "open but cannot deliver flow" +#define t_XPRESSURE "open but cannot deliver pressure" +#define t_FILLING "filling" +#define t_EMPTYING "emptying" + +#define t_ELEV "Elevation" +#define t_DEMAND "Demand" +#define t_HEAD "Head" +#define t_PRESSURE "Pressure" +#define t_QUALITY "Quality" +#define t_LENGTH "Length" +#define t_DIAM "Diameter" +#define t_FLOW "Flow" +#define t_VELOCITY "Velocity" +#define t_HEADLOSS "Headloss" +#define t_LINKQUAL "Quality" +#define t_LINKSTATUS "State" +#define t_SETTING "Setting" +#define t_REACTRATE "Reaction" +#define t_FRICTION "F-Factor" + +#define t_NODEID "Node" +#define t_LINKID "Link" +#define t_PERDAY "/day" + +#define t_JUNCTION "Junction" +#define t_RESERVOIR "Reservoir" +#define t_TANK "Tank" +#define t_PIPE "Pipe" +#define t_PUMP "Pump" +#define t_VALVE "Valve" +#define t_CONTROL "Control" +#define t_RULE "Rule" +#define t_DEMANDFOR "Demand for Node" +#define t_SOURCE "Source" +#define t_EMITTER "Emitter" +#define t_PATTERN "Pattern" +#define t_CURVE "Curve" +#define t_STATUS "Status" +#define t_ROUGHNESS "Roughness" +#define t_ENERGY "Energy" +#define t_REACTION "Reaction" +#define t_MIXING "Mixing" +#define t_REPORT "Report" +#define t_TIME "Times" +#define t_OPTION "Options" +#define t_RULES_SECT "[RULES] section" +#define t_HALTED " EXECUTION HALTED." +#define t_FUNCCALL "function call" +#define t_CONTINUED " (continued)" +#define t_perM3 " /m3" +#define t_perMGAL "/Mgal" +#define t_DIFFER "DIFFERENTIAL" + + +/* ------------------ Format Messages ------------------*/ +#define LOGO1 \ +"******************************************************************" +#define LOGO2 \ +"* E P A N E T *" +#define LOGO3 \ +"* Hydraulic and Water Quality *" +#define LOGO4 \ +"* Analysis for Pipe Networks *" +#define LOGO5 \ +"* Version %d.%d *" +#define LOGO6 \ +"******************************************************************" +#define FMT01 "\n... EPANET Version %d.%d\n" +#define FMT02 "\n o Retrieving network data" +#define FMT03 "\n Correct syntax is:\n %s \n" +#define FMT04 "\n Cannot use duplicate file names." +#define FMT05 "\n Cannot open input file " +#define FMT06 "\n Cannot open report file " +#define FMT07 "\n Cannot open output file " +#define FMT08 "\n Cannot open temporary output file" +#define FMT09 "\n\n... EPANET completed.\n" +#define FMT10 "\n\n... EPANET completed. There are warnings.\n" +#define FMT11 "\n\n... EPANET completed. There are errors.\n" +#define FMT14 "\n o Computing hydraulics at hour " +#define FMT15 "\n o Computing water quality at hour " +#define FMT16 "\n o Transferring results to file" +#define FMT17 "\n o Writing output report to " +#define FMT18 " Page 1 " +#define FMT19 " Input Data File ................... %s" +#define FMT20 " Number of Junctions................ %-d" +#define FMT21a " Number of Reservoirs............... %-d" +#define FMT21b " Number of Tanks ................... %-d" +#define FMT22 " Number of Pipes ................... %-d" +#define FMT23 " Number of Pumps ................... %-d" +#define FMT24 " Number of Valves .................. %-d" +#define FMT25 " Headloss Formula .................. %s" +#define FMT26 " Hydraulic Timestep ................ %-.2f %s" +#define FMT27 " Hydraulic Accuracy ................ %-.6f" + +#define FMT27a " Status Check Frequency ............ %-d" +#define FMT27b " Maximum Trials Checked ............ %-d" +#define FMT27c " Damping Limit Threshold ........... %-.6f" + +#define FMT28 " Maximum Trials .................... %-d" +#define FMT29 " Quality Analysis .................. None" +#define FMT30 " Quality Analysis .................. %s" +#define FMT31 " Quality Analysis .................. Trace From Node %s" +#define FMT32 " Quality Analysis .................. Age" +#define FMT33 " Water Quality Time Step ........... %-.2f min" +#define FMT34 " Water Quality Tolerance ........... %-.2f %s" +#define FMT36 " Specific Gravity .................. %-.2f" +#define FMT37a " Relative Kinematic Viscosity ...... %-.2f" +#define FMT37b " Relative Chemical Diffusivity ..... %-.2f" +#define FMT38 " Demand Multiplier ................. %-.2f" +#define FMT39 " Total Duration .................... %-.2f %s" +#define FMT40 " Reporting Criteria:" +#define FMT41 " No Nodes" +#define FMT42 " All Nodes" +#define FMT43 " Selected Nodes" +#define FMT44 " No Links" +#define FMT45 " All Links" +#define FMT46 " Selected Links" +#define FMT47 " with %s below %-.2f %s" +#define FMT48 " with %s above %-.2f %s" + +/* ---------- Status Report Format Strings ------------ */ +#define FMT49 "Hydraulic Status:" + +/*** Updated 6/24/02 ***/ +#define FMT50 "%10s: Tank %s is %s at %-.2f %s" +#define FMT51 "%10s: Reservoir %s is %s" +#define FMT52 "%10s: %s %s %s" +#define FMT53 "%10s: %s %s changed from %s to %s" +#define FMT54 "%10s: %s %s changed by %s %s control" +#define FMT55 "%10s: %s %s changed by timer control" +#define FMT56 " %s %s setting changed to %-.2f" +#define FMT57 " %s %s switched from %s to %s" +#define FMT58 "%10s: Balanced after %-d trials" +#define FMT59 "%10s: Unbalanced after %-d trials (flow change = %-.6f)" + +#define FMT60a " Max. flow imbalance is %.4f %s at Node %s" +#define FMT60b " Max. head imbalance is %.4f %s at Link %s" + +#define FMT61 "%10s: Valve %s caused ill-conditioning" +#define FMT62 "%10s: System ill-conditioned at node %s" +#define FMT63 "%10s: %s %s changed by rule %s" +#define FMT64 "%10s: Balancing the network:" +#define FMT65 " Trial %2d: relative flow change = %-.6f" +/*** End of update ***/ + +/* -------------------- Energy Report Table ------------------- */ +#define FMT71 "Energy Usage:" +#define FMT72 \ + " Usage Avg. Kw-hr Avg. Peak Cost" +#define FMT73 \ + "Pump Factor Effic. %s Kw Kw /day" +#define FMT74 "%38s Demand Charge: %9.2f" +#define FMT75 "%38s Total Cost: %9.2f" + +/* -------------------- Node Report Table --------------------- */ +#define FMT76 "%s Node Results:" +#define FMT77 "Node Results:" +#define FMT78 "Node Results at %s hrs:" + +/* -------------------- Link Report Table --------------------- */ +#define FMT79 "%s Link Results:" +#define FMT80 "Link Results:" +#define FMT81 "Link Results at %s hrs:" +#define FMT82 "\n\f\n Page %-d %60.60s\n" + +/* ------------------- Progress Messages ---------------------- */ +#define FMT100 "Retrieving network data..." +#define FMT101 "Computing hydraulics at hour %s" +#define FMT102 "Computing water quality at hour %s" +#define FMT103 "Saving results to file..." +#define FMT104 "Analysis begun %s" +#define FMT105 "Analysis ended %s" + +/*------------------- Error Messages --------------------*/ + + + + + +#define R_ERR201 "Input Error 201: syntax error in following line of " +#define R_ERR202 "Input Error 202: illegal numeric value in following line of " +#define R_ERR203 "Input Error 203: undefined node in following line of " +#define R_ERR204 "Input Error 204: undefined link in following line of " +#define R_ERR207 "Input Error 207: attempt to control a CV in following line of " + +#define R_ERR221 "Input Error 221: mis-placed clause in following line of " + +/*-------------------- Specific Warning Messages -------------------------*/ +#define WARN01 "WARNING: System unbalanced at %s hrs." +#define WARN02 \ +"WARNING: Maximum trials exceeded at %s hrs. System may be unstable." +#define WARN03a "WARNING: Node %s disconnected at %s hrs" +#define WARN03b "WARNING: %d additional nodes disconnected at %s hrs" +#define WARN03c "WARNING: System disconnected because of Link %s" +#define WARN04 "WARNING: Pump %s %s at %s hrs." +#define WARN05 "WARNING: %s %s %s at %s hrs." +#define WARN06 "WARNING: Negative pressures at %s hrs." + +/*-------------------- General Warning Messages -------------------------*/ +#define WARN1 "WARNING: System hydraulically unbalanced." +#define WARN2 "WARNING: System may be hydraulically unstable." +#define WARN3 "WARNING: System disconnected." +#define WARN4 "WARNING: Pumps cannot deliver enough flow or head." +#define WARN5 "WARNING: Valves cannot deliver enough flow." +#define WARN6 "WARNING: System has negative pressures." + +#endif diff --git a/src/types.h b/src/types.h index 1fbc839..920c779 100755 --- a/src/types.h +++ b/src/types.h @@ -1,512 +1,857 @@ -/* -*********************************************************************** - -TYPES.H -- Global constants and data types for EPANET program - -VERSION: 2.00 -DATE: 5/8/00 - 9/7/00 - 10/25/00 - 3/1/01 - 12/6/01 - 6/24/02 - 8/15/07 (2.00.11) - 2/14/08 (2.00.12) -AUTHOR: L. Rossman - US EPA - NRMRL - -********************************************************************** -*/ -#ifndef TYPES_H -#define TYPES_H - -/*********************************************************/ -/* All floats have been re-declared as doubles (7/3/07). */ -/*********************************************************/ -/* -------------------------------------------- - Definition of 4-byte integers & reals -------------------------------------------- -*/ -typedef float REAL4; //(2.00.11 - LR) -typedef int INT4; //(2.00.12 - LR) - -/* ------------------------------ - Global Constants ------------------------------ -*/ -/*** Updated ***/ -#define CODEVERSION 20100 -#define MAGICNUMBER 516114521 -#define ENGINE_VERSION 201 -#define EOFMARK 0x1A /* Use 0x04 for UNIX systems */ -#define MAXTITLE 3 /* Max. # title lines */ -#define MAXID 31 /* Max. # characters in ID name */ //(2.00.11 - LR) -#define MAXMSG 79 /* Max. # characters in message text */ -#define MAXLINE 255 /* Max. # characters read from input line */ -#define MAXFNAME 259 /* Max. # characters in file name */ -#define MAXTOKS 40 /* Max. items per line of input */ -#define TZERO 1.E-4 /* Zero time tolerance */ -#define TRUE 1 -#define FALSE 0 -#define FULL 2 -#define BIG 1.E10 -#define TINY 1.E-6 -#define MISSING -1.E10 -#define PI 3.141592654 - -/*** Updated 9/7/00 ***/ -/* Various conversion factors */ -#define GPMperCFS 448.831 -#define AFDperCFS 1.9837 -#define MGDperCFS 0.64632 -#define IMGDperCFS 0.5382 -#define LPSperCFS 28.317 -#define LPMperCFS 1699.0 -#define CMHperCFS 101.94 -#define CMDperCFS 2446.6 -#define MLDperCFS 2.4466 -#define M3perFT3 0.028317 -#define LperFT3 28.317 -#define MperFT 0.3048 -#define PSIperFT 0.4333 -#define KPAperPSI 6.895 -#define KWperHP 0.7457 -#define SECperDAY 86400 - -#define DIFFUS 1.3E-8 /* Diffusivity of chlorine */ - /* @ 20 deg C (sq ft/sec) */ -#define VISCOS 1.1E-5 /* Kinematic viscosity of water */ - /* @ 20 deg C (sq ft/sec) */ - -#define SEPSTR " \t\n\r" /* Token separator characters */ - -/* ---------------------------------------------------------------------- - Macro to test for successful allocation of memory ---------------------------------------------------------------------- -*/ -#define MEMCHECK(x) (((x) == NULL) ? 101 : 0 ) -#define FREE(x) (free((x))) - -/* ---------------------------------------------------------------------- - Conversion macros to be used in place of functions ---------------------------------------------------------------------- -*/ -#define INT(x) ((int)(x)) /* integer portion of x */ -#define FRAC(x) ((x)-(int)(x)) /* fractional part of x */ -#define ABS(x) (((x)<0) ? -(x) : (x)) /* absolute value of x */ -#define MIN(x,y) (((x)<=(y)) ? (x) : (y)) /* minimum of x and y */ -#define MAX(x,y) (((x)>=(y)) ? (x) : (y)) /* maximum of x and y */ -#define ROUND(x) (((x)>=0) ? (int)((x)+.5) : (int)((x)-.5)) - /* round-off of x */ -#define MOD(x,y) ((x)%(y)) /* x modulus y */ -#define SQR(x) ((x)*(x)) /* x-squared */ -#define SGN(x) (((x)<0) ? (-1) : (1)) /* sign of x */ -#define UCHAR(x) (((x) >= 'a' && (x) <= 'z') ? ((x)&~32) : (x)) - /* uppercase char of x */ -/* ------------------------------------------------------- - Macro to evaluate function x with error checking - (Fatal errors are numbered higher than 100) ------------------------------------------------------- -*/ -#define ERRCODE(x) (errcode = ((errcode>100) ? (errcode) : (x))) - -/* ------------------------------------------------------- - Macro to find Pump index of Link[x] - (Diameter = pump index for pump links) ------------------------------------------------------- -*/ -#define PUMPINDEX(x) (ROUND(Link[(x)].Diam)) - -/* ------------------------------------------------------- - Global Data Structures ------------------------------------------------------- -*/ - -struct IDstring /* Holds component ID labels */ -{ - char ID[MAXID+1]; -}; - -struct Floatlist /* Element of list of floats */ -{ - double value; - struct Floatlist *next; -}; -typedef struct Floatlist SFloatlist; - -struct Tmplist /* Element of temp list for Pattern & Curve data */ -{ - int i; - char ID[MAXID+1]; - SFloatlist *x; - SFloatlist *y; - struct Tmplist *next; -}; -typedef struct Tmplist STmplist; - -typedef struct /* TIME PATTERN OBJECT */ -{ - char ID[MAXID+1]; /* Pattern ID */ - int Length; /* Pattern length */ - double *F; /* Pattern factors */ -} Spattern; - -typedef struct /* CURVE OBJECT */ -{ - char ID[MAXID+1]; /* Curve ID */ - int Type; /* Curve type */ - int Npts; /* Number of points */ - double *X; /* X-values */ - double *Y; /* Y-values */ -} Scurve; - -typedef struct /* Coord OBJECT */ -{ - char ID[MAXID+1]; /* Coord ID */ - double X; /* X-value */ - double Y; /* Y-value */ - char HaveCoords; /* Coordinates flag */ -} Scoord; - -struct Sdemand /* DEMAND CATEGORY OBJECT */ -{ - double Base; /* Baseline demand */ - int Pat; /* Pattern index */ - struct Sdemand *next; /* Next record */ -}; -typedef struct Sdemand *Pdemand; /* Pointer to demand object */ - -struct Ssource /* WQ SOURCE OBJECT */ -{ - /*int Node;*/ /* Node index of source */ - double C0; /* Base concentration/mass */ - int Pat; /* Pattern index */ - double Smass; /* Actual mass flow rate */ - char Type; /* SourceType (see below) */ -}; -typedef struct Ssource *Psource; /* Pointer to WQ source object */ - -typedef struct /* NODE OBJECT */ -{ - char ID[MAXID+1]; /* Node ID */ - double El; /* Elevation */ - Pdemand D; /* Demand pointer */ - Psource S; /* Source pointer */ - double C0; /* Initial quality */ - double Ke; /* Emitter coeff. */ - char Rpt; /* Reporting flag */ -} Snode; - -typedef struct /* LINK OBJECT */ -{ - char ID[MAXID+1]; /* Link ID */ - int N1; /* Start node index */ - int N2; /* End node index */ - double Diam; /* Diameter */ - double Len; /* Length */ - double Kc; /* Roughness */ - double Km; /* Minor loss coeff. */ - double Kb; /* Bulk react. coeff */ - double Kw; /* Wall react. coeff */ - double R; /* Flow resistance */ - double Rc; /* Reaction cal */ - char Type; /* Link type */ - char Stat; /* Initial status */ - char Rpt; /* Reporting flag */ -} Slink; - -typedef struct /* TANK OBJECT */ -{ - int Node; /* Node index of tank */ - double A; /* Tank area */ - double Hmin; /* Minimum water elev */ - double Hmax; /* Maximum water elev */ - double H0; /* Initial water elev */ - double Vmin; /* Minimum volume */ - double Vmax; /* Maximum volume */ - double V0; /* Initial volume */ - double Kb; /* Reaction coeff. (1/days) */ - double V; /* Tank volume */ - double C; /* Concentration */ - int Pat; /* Fixed grade time pattern */ - int Vcurve; /* Vol.- elev. curve index */ - char MixModel; /* Type of mixing model */ - /* (see MixType below) */ - double V1max; /* Mixing compartment size */ -} Stank; - -typedef struct /* PUMP OBJECT */ -{ - int Link; /* Link index of pump */ - int Ptype; /* Pump curve type */ - /* (see PumpType below) */ - double Q0; /* Initial flow */ - double Qmax; /* Maximum flow */ - double Hmax; /* Maximum head */ - double H0; /* Shutoff head */ - double R; /* Flow coeffic. */ - double N; /* Flow exponent */ - int Hcurve; /* Head v. flow curve index */ - int Ecurve; /* Effic. v. flow curve index */ - int Upat; /* Utilization pattern index */ - int Epat; /* Energy cost pattern index */ - double Ecost; /* Unit energy cost */ - double Energy[6]; /* Energy usage statistics: */ - /* 0 = pump utilization */ - /* 1 = avg. efficiency */ - /* 2 = avg. kW/flow */ - /* 3 = avg. kwatts */ - /* 4 = peak kwatts */ - /* 5 = cost/day */ -} Spump; - -typedef struct /* VALVE OBJECT */ -{ - int Link; /* Link index of valve */ -} Svalve; - -typedef struct /* CONTROL STATEMENT */ -{ - int Link; /* Link index */ - int Node; /* Control node index */ - long Time; /* Control time */ - double Grade; /* Control grade */ - double Setting; /* New link setting */ - char Status; /* New link status */ - char Type; /* Control type */ - /* (see ControlType below) */ -} Scontrol; - -struct Sadjlist /* NODE ADJACENCY LIST ITEM */ -{ - int node; /* Index of connecting node */ - int link; /* Index of connecting link */ - struct Sadjlist *next; /* Next item in list */ -}; -/* Pointer to adjacency list item */ -typedef struct Sadjlist *Padjlist; - -struct Sseg /* PIPE SEGMENT record used */ -{ /* for WQ routing */ - double v; /* Segment volume */ - double c; /* Water quality value */ - struct Sseg *prev; /* Record for previous segment */ -}; -typedef struct Sseg *Pseg; /* Pointer to pipe segment */ - -typedef struct /* FIELD OBJECT of report table */ -{ - char Name[MAXID+1]; /* Name of reported variable */ - char Units[MAXID+1]; /* Units of reported variable */ - char Enabled; /* Enabled if in table */ - int Precision; /* Number of decimal places */ - double RptLim[2]; /* Lower/upper report limits */ -} SField; - - -/* ----------------------------------------------- - Global Enumeration Variables ----------------------------------------------- -*/ - enum Hydtype /* Hydraulics solution option: */ - {USE, /* use from previous run */ - SAVE, /* save after current run */ - SCRATCH}; /* use temporary file */ - - enum QualType /* Water quality analysis option: */ - {NONE, /* no quality analysis */ - CHEM, /* analyze a chemical */ - AGE, /* analyze water age */ - TRACE}; /* trace % of flow from a source */ - - enum NodeType /* Type of node: */ - {JUNC, /* junction */ - RESERV, /* reservoir */ - TANK}; /* tank */ - - enum LinkType /* Type of link: */ - {CV, /* pipe with check valve */ - PIPE, /* regular pipe */ - PUMP, /* pump */ - PRV, /* pressure reducing valve */ - PSV, /* pressure sustaining valve */ - PBV, /* pressure breaker valve */ - FCV, /* flow control valve */ - TCV, /* throttle control valve */ - GPV}; /* general purpose valve */ - - enum CurveType /* Type of curve: */ - {V_CURVE, /* volume curve */ - P_CURVE, /* pump curve */ - E_CURVE, /* efficiency curve */ - H_CURVE}; /* head loss curve */ - - enum PumpType /* Type of pump curve: */ - {CONST_HP, /* constant horsepower */ - POWER_FUNC, /* power function */ - CUSTOM, /* user-defined custom curve */ - NOCURVE}; - - enum SourceType /* Type of source quality input */ - {CONCEN, /* inflow concentration */ - MASS, /* mass inflow booster */ - SETPOINT, /* setpoint booster */ - FLOWPACED}; /* flow paced booster */ - - enum ControlType /* Control condition type: */ - {LOWLEVEL, /* act when grade below set level */ - HILEVEL, /* act when grade above set level */ - TIMER, /* act when set time reached */ - TIMEOFDAY}; /* act when time of day occurs */ - - enum StatType /* Link/Tank status: */ - {XHEAD, /* pump cannot deliver head (closed) */ - TEMPCLOSED, /* temporarily closed */ - CLOSED, /* closed */ - OPEN, /* open */ - ACTIVE, /* valve active (partially open) */ - XFLOW, /* pump exceeds maximum flow */ - XFCV, /* FCV cannot supply flow */ - XPRESSURE, /* valve cannot supply pressure */ - FILLING, /* tank filling */ - EMPTYING}; /* tank emptying */ - - enum FormType /* Head loss formula: */ - {HW, /* Hazen-Williams */ - DW, /* Darcy-Weisbach */ - CM}; /* Chezy-Manning */ - - enum UnitsType /* Unit system: */ - {US, /* US */ - SI}; /* SI (metric) */ - - enum FlowUnitsType /* Flow units: */ - {CFS, /* cubic feet per second */ - GPM, /* gallons per minute */ - MGD, /* million gallons per day */ - IMGD, /* imperial million gal. per day */ - AFD, /* acre-feet per day */ - LPS, /* liters per second */ - LPM, /* liters per minute */ - MLD, /* megaliters per day */ - CMH, /* cubic meters per hour */ - CMD}; /* cubic meters per day */ - - enum PressUnitsType /* Pressure units: */ - {PSI, /* pounds per square inch */ - KPA, /* kiloPascals */ - METERS}; /* meters */ - - enum RangeType /* Range limits: */ - {LOW, /* lower limit */ - HI, /* upper limit */ - PREC}; /* precision */ - - enum MixType /* Tank mixing regimes */ - {MIX1, /* 1-compartment model */ - MIX2, /* 2-compartment model */ - FIFO, /* First in, first out model */ - LIFO}; /* Last in, first out model */ - - enum TstatType /* Time series statistics */ - {SERIES, /* none */ - AVG, /* time-averages */ - MIN, /* minimum values */ - MAX, /* maximum values */ - RANGE}; /* max - min values */ - -#define MAXVAR 21 /* Max. # types of network variables */ - /* (equals # items enumed below) */ - enum FieldType /* Network variables: */ - {ELEV, /* nodal elevation */ - DEMAND, /* nodal demand flow */ - HEAD, /* nodal hydraulic head */ - PRESSURE, /* nodal pressure */ - QUALITY, /* nodal water quality */ - - LENGTH, /* link length */ - DIAM, /* link diameter */ - FLOW, /* link flow rate */ - VELOCITY, /* link flow velocity */ - HEADLOSS, /* link head loss */ - LINKQUAL, /* avg. water quality in link */ - STATUS, /* link status */ - SETTING, /* pump/valve setting */ - REACTRATE, /* avg. reaction rate in link */ - FRICTION, /* link friction factor */ - - POWER, /* pump power output */ - TIME, /* simulation time */ - VOLUME, /* tank volume */ - CLOCKTIME, /* simulation time of day */ - FILLTIME, /* time to fill a tank */ - DRAINTIME}; /* time to drain a tank */ - -enum SectType {_TITLE,_JUNCTIONS,_RESERVOIRS,_TANKS,_PIPES,_PUMPS, - _VALVES,_CONTROLS,_RULES,_DEMANDS,_SOURCES,_EMITTERS, - _PATTERNS,_CURVES,_QUALITY,_STATUS,_ROUGHNESS,_ENERGY, - _REACTIONS,_MIXING,_REPORT,_TIMES,_OPTIONS, - _COORDS,_VERTICES,_LABELS,_BACKDROP,_TAGS,_END}; - -enum HdrType /* Type of table heading */ - {STATHDR, /* Hydraulic Status */ - ENERHDR, /* Energy Usage */ - NODEHDR, /* Node Results */ - LINKHDR}; /* Link Results */ - - -//AM 22Sept2016 moved from rules.c -struct Premise /* Rule Premise Clause */ -{ - int logop; /* Logical operator */ - int object; /* Node or link */ - int index; /* Object's index */ - int variable; /* Pressure, flow, etc. */ - int relop; /* Relational operator */ - int status; /* Variable's status */ - double value; /* Variable's value */ - struct Premise *next; -}; - -struct Action /* Rule Action Clause */ -{ - int link; /* Link index */ - int status; /* Link's status */ - double setting; /* Link's setting */ - struct Action *next; -}; - -struct aRule /* Control Rule Structure */ -{ - char label[MAXID+1]; /* Rule character label */ - double priority; /* Priority level */ - struct Premise *Pchain; /* Linked list of premises */ - struct Action *Tchain; /* Linked list of actions if true */ - struct Action *Fchain; /* Linked list of actions if false */ - struct aRule *next; -}; - -struct ActItem /* Action list item */ -{ - int ruleindex; /* Index of rule action belongs to */ - struct Action *action; /* An action structure */ - struct ActItem *next; -}; - -struct aRule *Rule; /* Array of rules */ -struct ActItem *ActList; /* Linked list of action items */ -int RuleState; /* State of rule interpreter */ -long Time1; /* Start of rule evaluation time interval (sec) */ -struct Premise *Plast; /* Previous premise clause */ -struct Action *Tlast; /* Previous true action */ -struct Action *Flast; /* Previous false action */ - -#endif +/* +*********************************************************************** + +TYPES.H -- Global constants and data types for EPANET program + +VERSION: 2.00 +DATE: 5/8/00 + 9/7/00 + 10/25/00 + 3/1/01 + 12/6/01 + 6/24/02 + 8/15/07 (2.00.11) + 2/14/08 (2.00.12) +AUTHOR: L. Rossman + US EPA - NRMRL + +********************************************************************** +*/ +#ifndef TYPES_H +#define TYPES_H + +#include "epanet2.h" +#include "hash.h" +#include "mempool.h" + +/*********************************************************/ +/* All floats have been re-declared as doubles (7/3/07). */ +/*********************************************************/ +/* +------------------------------------------- + Definition of 4-byte integers & reals +------------------------------------------- +*/ +typedef float REAL4; +typedef int INT4; + +/* +----------------------------- + Global Constants +----------------------------- +*/ +/*** Updated ***/ +#define CODEVERSION 20200 +#define MAGICNUMBER 516114521 +#define ENGINE_VERSION 201 +#define EOFMARK 0x1A /* Use 0x04 for UNIX systems */ +#define MAXTITLE 3 /* Max. # title lines */ +#define MAXID 31 /* Max. # characters in ID name */ +#define MAXMSG 79 /* Max. # characters in message text */ +#define MAXLINE 255 /* Max. # characters read from input line */ +#define MAXFNAME 259 /* Max. # characters in file name */ +#define MAXTOKS 40 /* Max. items per line of input */ +#define TZERO 1.E-4 /* Zero time tolerance */ +#define TRUE 1 +#define FALSE 0 +#define FULL 2 +#define BIG 1.E10 +#define TINY 1.E-6 +#define MISSING -1.E10 + +#ifdef M_PI + #define PI M_PI +#else + #define PI 3.141592654 +#endif + +/*** Updated 9/7/00 ***/ +/* Various conversion factors */ +#define GPMperCFS 448.831 +#define AFDperCFS 1.9837 +#define MGDperCFS 0.64632 +#define IMGDperCFS 0.5382 +#define LPSperCFS 28.317 +#define LPMperCFS 1699.0 +#define CMHperCFS 101.94 +#define CMDperCFS 2446.6 +#define MLDperCFS 2.4466 +#define M3perFT3 0.028317 +#define LperFT3 28.317 +#define MperFT 0.3048 +#define PSIperFT 0.4333 +#define KPAperPSI 6.895 +#define KWperHP 0.7457 +#define SECperDAY 86400 + +#define DIFFUS 1.3E-8 /* Diffusivity of chlorine */ + /* @ 20 deg C (sq ft/sec) */ +#define VISCOS 1.1E-5 /* Kinematic viscosity of water */ + /* @ 20 deg C (sq ft/sec) */ + +#define SEPSTR " \t\n\r" /* Token separator characters */ + +/* +--------------------------------------------------------------------- + Macro to test for successful allocation of memory +--------------------------------------------------------------------- +*/ +#define MEMCHECK(x) (((x) == NULL) ? 101 : 0 ) +#define FREE(x) (free((x))) + +/* +--------------------------------------------------------------------- + Conversion macros to be used in place of functions +--------------------------------------------------------------------- +*/ +#define INT(x) ((int)(x)) /* integer portion of x */ +#define FRAC(x) ((x)-(int)(x)) /* fractional part of x */ +#define ABS(x) (((x)<0) ? -(x) : (x)) /* absolute value of x */ +#define MIN(x,y) (((x)<=(y)) ? (x) : (y)) /* minimum of x and y */ +#define MAX(x,y) (((x)>=(y)) ? (x) : (y)) /* maximum of x and y */ +#define ROUND(x) (((x)>=0) ? (int)((x)+.5) : (int)((x)-.5)) + /* round-off of x */ +#define MOD(x,y) ((x)%(y)) /* x modulus y */ +#define SQR(x) ((x)*(x)) /* x-squared */ +#define SGN(x) (((x)<0) ? (-1) : (1)) /* sign of x */ +#define UCHAR(x) (((x) >= 'a' && (x) <= 'z') ? ((x)&~32) : (x)) + /* uppercase char of x */ +/* +------------------------------------------------------ + Macro to evaluate function x with error checking + (Fatal errors are numbered higher than 100) +------------------------------------------------------ +*/ +#define ERRCODE(x) (errcode = ((errcode>100) ? (errcode) : (x))) + + + +/* + ---------------------------------------------- + Global Enumeration Types + ---------------------------------------------- + */ +typedef enum { + USE, /* use from previous run */ + SAVE, /* save after current run */ + SCRATCH +} Hydtype; /* use temporary file */ + +typedef enum { + NONE, /* no quality analysis */ + CHEM, /* analyze a chemical */ + AGE, /* analyze water age */ + TRACE +} QualType; /* trace % of flow from a source */ + +typedef enum { + V_CURVE, /* volume curve */ + P_CURVE, /* pump curve */ + E_CURVE, /* efficiency curve */ + H_CURVE +} CurveType; /* head loss curve */ + +typedef enum { + CONST_HP, /* constant horsepower */ + POWER_FUNC, /* power function */ + CUSTOM, /* user-defined custom curve */ + NOCURVE +} PumpType; + +typedef enum { + CONCEN, /* inflow concentration */ + MASS, /* mass inflow booster */ + SETPOINT, /* setpoint booster */ + FLOWPACED +} SourceType; /* flow paced booster */ + +typedef enum { + LOWLEVEL, /* act when grade below set level */ + HILEVEL, /* act when grade above set level */ + TIMER, /* act when set time reached */ + TIMEOFDAY +} ControlType; /* act when time of day occurs */ + +typedef enum { + XHEAD, /* pump cannot deliver head (closed) */ + TEMPCLOSED, /* temporarily closed */ + CLOSED, /* closed */ + OPEN, /* open */ + ACTIVE, /* valve active (partially open) */ + XFLOW, /* pump exceeds maximum flow */ + XFCV, /* FCV cannot supply flow */ + XPRESSURE, /* valve cannot supply pressure */ + FILLING, /* tank filling */ + EMPTYING +} StatType; /* tank emptying */ + +typedef enum { + HW, /* Hazen-Williams */ + DW, /* Darcy-Weisbach */ + CM +} FormType; /* Chezy-Manning */ + +typedef enum { + US, /* US */ + SI +} UnitsType; /* SI (metric) */ + +typedef enum { + CFS, /* cubic feet per second */ + GPM, /* gallons per minute */ + MGD, /* million gallons per day */ + IMGD, /* imperial million gal. per day */ + AFD, /* acre-feet per day */ + LPS, /* liters per second */ + LPM, /* liters per minute */ + MLD, /* megaliters per day */ + CMH, /* cubic meters per hour */ + CMD +} FlowUnitsType; /* cubic meters per day */ + +typedef enum { + PSI, /* pounds per square inch */ + KPA, /* kiloPascals */ + METERS +} PressUnitsType; /* meters */ + +typedef enum { + LOW, /* lower limit */ + HI, /* upper limit */ + PREC +} RangeType; /* precision */ + +typedef enum { + MIX1, /* 1-compartment model */ + MIX2, /* 2-compartment model */ + FIFO, /* First in, first out model */ + LIFO +} MixType; /* Last in, first out model */ + +typedef enum { + SERIES, /* none */ + AVG, /* time-averages */ + MIN, /* minimum values */ + MAX, /* maximum values */ + RANGE +} TstatType; /* max - min values */ + +#define MAXVAR 21 /* Max. # types of network variables */ +/* (equals # items enumed below) */ +typedef enum { + ELEV = 0, /* nodal elevation */ + DEMAND, /* nodal demand flow */ + HEAD, /* nodal hydraulic head */ + PRESSURE, /* nodal pressure */ + QUALITY, /* nodal water quality */ + + LENGTH, /* link length */ + DIAM, /* link diameter */ + FLOW, /* link flow rate */ + VELOCITY, /* link flow velocity */ + HEADLOSS, /* link head loss */ + LINKQUAL, /* avg. water quality in link */ + STATUS, /* link status */ + SETTING, /* pump/valve setting */ + REACTRATE, /* avg. reaction rate in link */ + FRICTION, /* link friction factor */ + + POWER, /* pump power output */ + TIME, /* simulation time */ + VOLUME, /* tank volume */ + CLOCKTIME, /* simulation time of day */ + FILLTIME, /* time to fill a tank */ + DRAINTIME /* time to drain a tank */ +} FieldType; + +typedef enum { + _TITLE,_JUNCTIONS,_RESERVOIRS,_TANKS,_PIPES,_PUMPS, + _VALVES,_CONTROLS,_RULES,_DEMANDS,_SOURCES,_EMITTERS, + _PATTERNS,_CURVES,_QUALITY,_STATUS,_ROUGHNESS,_ENERGY, + _REACTIONS,_MIXING,_REPORT,_TIMES,_OPTIONS, + _COORDS,_VERTICES,_LABELS,_BACKDROP,_TAGS,_END +} SectType; + +typedef enum { + STATHDR, /* Hydraulic Status */ + ENERHDR, /* Energy Usage */ + NODEHDR, /* Node Results */ + LINKHDR +} HdrType; /* Link Results */ + +typedef enum { + POSITIVE, + NEGATIVE +} FlowDirection; + +/* +------------------------------------------------------ + Global Data Structures +------------------------------------------------------ +*/ + +struct IDstring /* Holds component ID labels */ +{ + char ID[MAXID+1]; +}; + +struct Floatlist /* Element of list of floats */ +{ + double value; + struct Floatlist *next; +}; +typedef struct Floatlist SFloatlist; + +struct Tmplist /* Element of temp list for Pattern & Curve data */ +{ + int i; + char ID[MAXID+1]; + SFloatlist *x; + SFloatlist *y; + struct Tmplist *next; +}; +typedef struct Tmplist STmplist; + +typedef struct /* TIME PATTERN OBJECT */ +{ + char ID[MAXID+1]; /* Pattern ID */ + int Length; /* Pattern length */ + double *F; /* Pattern factors */ +} Spattern; + +typedef struct /* CURVE OBJECT */ +{ + char ID[MAXID+1]; /* Curve ID */ + CurveType Type; /* Curve type */ + int Npts; /* Number of points */ + double *X; /* X-values */ + double *Y; /* Y-values */ +} Scurve; + +typedef struct /* Coord OBJECT */ +{ + char ID[MAXID+1]; /* Coord ID */ + double X; /* X-value */ + double Y; /* Y-value */ + char HaveCoords; /* Coordinates flag */ +} Scoord; + +struct Sdemand /* DEMAND CATEGORY OBJECT */ +{ + double Base; /* Baseline demand */ + int Pat; /* Pattern index */ + struct Sdemand *next; /* Next record */ +}; +typedef struct Sdemand *Pdemand; /* Pointer to demand object */ + +struct Ssource /* WQ SOURCE OBJECT */ +{ + /*int Node;*/ /* Node index of source */ + double C0; /* Base concentration/mass */ + int Pat; /* Pattern index */ + double Smass; /* Actual mass flow rate */ + SourceType Type; /* SourceType (see below) */ +}; +typedef struct Ssource *Psource; /* Pointer to WQ source object */ + +typedef struct /* NODE OBJECT */ +{ + char ID[MAXID+1]; /* Node ID */ + double El; /* Elevation */ + Pdemand D; /* Demand pointer */ + Psource S; /* Source pointer */ + double C0; /* Initial quality */ + double Ke; /* Emitter coeff. */ + char Rpt; /* Reporting flag */ + EN_NodeType Type; /* Node Type */ + char Comment[MAXMSG+1]; /* Node Comment */ +} Snode; + +typedef struct /* LINK OBJECT */ +{ + char ID[MAXID+1]; /* Link ID */ + int N1; /* Start node index */ + int N2; /* End node index */ + double Diam; /* Diameter */ + double Len; /* Length */ + double Kc; /* Roughness */ + double Km; /* Minor loss coeff. */ + double Kb; /* Bulk react. coeff */ + double Kw; /* Wall react. coeff */ + double R; /* Flow resistance */ + double Rc; /* Reaction cal */ + EN_LinkType Type; /* Link type */ + StatType Stat; /* Initial status */ + char Rpt; /* Reporting flag */ + char Comment[MAXMSG+1]; /* Link Comment */ +} Slink; + +typedef struct /* TANK OBJECT */ +{ + int Node; /* Node index of tank */ + double A; /* Tank area */ + double Hmin; /* Minimum water elev */ + double Hmax; /* Maximum water elev */ + double H0; /* Initial water elev */ + double Vmin; /* Minimum volume */ + double Vmax; /* Maximum volume */ + double V0; /* Initial volume */ + double Kb; /* Reaction coeff. (1/days) */ + double V; /* Tank volume */ + double C; /* Concentration */ + int Pat; /* Fixed grade time pattern */ + int Vcurve; /* Vol.- elev. curve index */ + MixType MixModel;/* Type of mixing model */ + /* (see MixType below) */ + double V1max; /* Mixing compartment size */ +} Stank; + +typedef struct /* PUMP OBJECT */ +{ + int Link; /* Link index of pump */ + int Ptype; /* Pump curve type */ + /* (see PumpType below) */ + double Q0; /* Initial flow */ + double Qmax; /* Maximum flow */ + double Hmax; /* Maximum head */ + double H0; /* Shutoff head */ + double R; /* Flow coeffic. */ + double N; /* Flow exponent */ + int Hcurve; /* Head v. flow curve index */ + int Ecurve; /* Effic. v. flow curve index */ + int Upat; /* Utilization pattern index */ + int Epat; /* Energy cost pattern index */ + double Ecost; /* Unit energy cost */ + double Energy[6]; /* Energy usage statistics: */ + /* 0 = pump utilization */ + /* 1 = avg. efficiency */ + /* 2 = avg. kW/flow */ + /* 3 = avg. kwatts */ + /* 4 = peak kwatts */ + /* 5 = cost/day */ +} Spump; + +typedef struct /* VALVE OBJECT */ +{ + int Link; /* Link index of valve */ +} Svalve; + +typedef struct /* CONTROL STATEMENT */ +{ + int Link; /* Link index */ + int Node; /* Control node index */ + long Time; /* Control time */ + double Grade; /* Control grade */ + double Setting; /* New link setting */ + StatType Status; /* New link status */ + ControlType Type;/* Control type */ + /* (see ControlType below) */ +} Scontrol; + +struct Sadjlist /* NODE ADJACENCY LIST ITEM */ +{ + int node; /* Index of connecting node */ + int link; /* Index of connecting link */ + struct Sadjlist *next; /* Next item in list */ +}; +/* Pointer to adjacency list item */ +typedef struct Sadjlist *Padjlist; + +struct Sseg /* PIPE SEGMENT record used */ +{ /* for WQ routing */ + double v; /* Segment volume */ + double c; /* Water quality value */ + struct Sseg *prev; /* Record for previous segment */ +}; +typedef struct Sseg *Pseg; /* Pointer to pipe segment */ + +typedef struct /* FIELD OBJECT of report table */ +{ + char Name[MAXID+1]; /* Name of reported variable */ + char Units[MAXID+1]; /* Units of reported variable */ + char Enabled; /* Enabled if in table */ + int Precision; /* Number of decimal places */ + double RptLim[2]; /* Lower/upper report limits */ +} SField; + +typedef struct s_Premise /* Rule Premise Clause */ +{ + int logop; /* Logical operator */ + int object; /* Node or link */ + int index; /* Object's index */ + int variable; /* Pressure, flow, etc. */ + int relop; /* Relational operator */ + int status; /* Variable's status */ + double value; /* Variable's value */ + struct s_Premise *next; +} Premise; + +typedef struct s_Action /* Rule Action Clause */ +{ + int link; /* Link index */ + int status; /* Link's status */ + double setting; /* Link's setting */ + struct s_Action *next; +} Action; + +typedef struct s_aRule /* Control Rule Structure */ +{ + char label[MAXID+1]; /* Rule character label */ + double priority; /* Priority level */ + Premise *Pchain; /* Linked list of premises */ + Action *Tchain; /* Linked list of actions if true */ + Action *Fchain; /* Linked list of actions if false */ + struct s_aRule *next; +} aRule; + +typedef struct s_ActItem /* Action list item */ +{ + int ruleindex; /* Index of rule action belongs to */ + struct s_Action *action; /* An action structure */ + struct s_ActItem *next; +} ActItem; + + + +typedef struct { + char + Qualflag, /// Water quality flag + OpenQflag, /// Quality system opened flag + Reactflag; /// Reaction indicator + + char + ChemName[MAXID+1], /* Name of chemical */ + ChemUnits[MAXID+1]; /* Units of chemical */ + + int + TraceNode; /// Source node for flow tracing + + double + Ctol, /// Water quality tolerance + Diffus, /// Diffusivity (sq ft/sec) + Wbulk, /// Avg. bulk reaction rate + Wwall, /// Avg. wall reaction rate + Wtank, /// Avg. tank reaction rate + Wsource, /// Avg. mass inflow + Rfactor, /// Roughness-reaction factor + BulkOrder, /// Bulk flow reaction order + WallOrder, /// Pipe wall reaction order + TankOrder, /// Tank reaction order + Kbulk, /// Global bulk reaction coeff. + Kwall, /// Global wall reaction coeff. + Climit, /// Limiting potential quality + *NodeQual, /// Node quality state + *TempQual, /// General purpose array for water quality + *QTankVolumes, + *QLinkFlow, + *PipeRateCoeff; + + long + Qstep, /// Quality time step (sec) + Qtime; /// Current quality time (sec) + + char OutOfMemory; /* Out of memory indicator */ + alloc_handle_t *SegPool; // Memory pool for water quality segments + + Pseg FreeSeg; /* Pointer to unused segment */ + Pseg *FirstSeg, /* First (downstream) segment in each pipe */ + *LastSeg; /* Last (upstream) segment in each pipe */ + FlowDirection *FlowDir; /* Flow direction for each pipe */ + double *VolIn; /* Total volume inflow to node */ + double *MassIn; /* Total mass inflow to node */ + double Sc; /* Schmidt Number */ + double Bucf; /* Bulk reaction units conversion factor */ + double Tucf; /* Tank reaction units conversion factor */ + +} quality_t; + +typedef struct { + long + Tstart, /* Starting time of day (sec) */ + Hstep, /* Nominal hyd. time step (sec) */ + Pstep, /* Time pattern time step (sec) */ + Pstart, /* Starting pattern time (sec) */ + Rstep, /* Reporting time step (sec) */ + Rstart, /* Time when reporting starts */ + Rtime, /* Next reporting time */ + Htime, /* Current hyd. time (sec) */ + Hydstep, /* Actual hydraulic time step */ + Rulestep, /* Rule evaluation time step */ + Dur; /* Duration of simulation (sec) */ + +} time_options_t; + + +typedef struct { + + FILE *InFile; /// Input file pointer + + char + Coordflag, /* Load coordinates flag */ + Unitsflag, /* Unit system flag */ + Flowflag, /* Flow units flag */ + Pressflag; /* Pressure units flag */ + + int + MaxNodes, /* Node count from input file */ + MaxLinks, /* Link count from input file */ + MaxJuncs, /* Junction count */ + MaxPipes, /* Pipe count */ + MaxTanks, /* Tank count */ + MaxPumps, /* Pump count */ + MaxValves, /* Valve count */ + MaxControls, /* Control count */ + MaxRules, /* Rule count */ + MaxPats, /* Pattern count */ + MaxCurves; /* Curve count */ + + char + DefPatID[MAXID+1], /* Default demand pattern ID */ + InpFname[MAXFNAME+1]; /* Input file name */ + + STmplist + *Patlist, /* Temporary time pattern list */ + *Curvelist; /* Temporary list of curves */ + + double *X; // temporary array for curve data + int + Ntokens, /* Number of tokens in input line */ + Ntitle; /* Number of title lines */ + + char *Tok[MAXTOKS]; /* Array of token strings */ + char Comment[MAXMSG+1]; + STmplist *PrevPat; /* Pointer to pattern list element */ + STmplist *PrevCurve; /* Pointer to curve list element */ + +} parser_data_t; + +typedef struct { + + FILE *RptFile; /* Report file pointer */ + + int + Nperiods, /* Number of reporting periods */ + PageSize; /* Lines/page in output report */ + + char + Rptflag, /* Report flag */ + Tstatflag, /* Time statistics flag */ + Summaryflag, /* Report summary flag */ + Messageflag, /* Error/warning message flag */ + Statflag, /* Status report flag */ + Energyflag, /* Energy report flag */ + Nodeflag, /* Node report flag */ + Linkflag, /* Link report flag */ + Atime[13], /* Clock time (hrs:min:sec) */ + Rpt1Fname[MAXFNAME+1], /* Primary report file name */ + Rpt2Fname[MAXFNAME+1]; /* Secondary report file name */ + + SField Field[MAXVAR]; /* Output reporting fields */ + + long LineNum; /* Current line number */ + long PageNum; /* Current page number */ + char DateStamp[26]; /* Current date & time */ + char Fprinterr; /* File write error flag */ + +} report_options_t; + + +typedef struct { + + char + HydFname[MAXFNAME+1], /* Hydraulics file name */ + OutFname[MAXFNAME+1], /* Binary output file name */ + TmpFname[MAXFNAME+1], /* Temporary file name */ + TmpDir[MAXFNAME+1], /* Temporary directory name */ + Outflag, /* Output file flag */ + Hydflag; /* Hydraulics flag */ + + + long + HydOffset, /* Hydraulics file byte offset */ + OutOffset1, /* 1st output file byte offset */ + OutOffset2; /* 2nd output file byte offset */ + + FILE + *OutFile, /* Output file pointer */ + *HydFile, /* Hydraulics file pointer */ + *TmpOutFile; /* Temporary file handle */ + +} out_file_t; + +typedef struct { + + char + SaveHflag, /* Hydraul. results saved flag */ + SaveQflag, /* Quality results saved flag */ + Saveflag; /* General purpose save flag */ + +} save_options_t; + + +typedef struct { + + aRule *Rule; /* Array of rules */ + ActItem *ActList; /* Linked list of action items */ + int RuleState; /* State of rule interpreter */ + long Time1; /* Start of rule evaluation time interval (sec) */ + Premise *Plast; /* Previous premise clause */ + Action *Tlast; /* Previous true action */ + Action *Flast; /* Previous false action */ + +} rules_t; + +/* + ** NOTE: Hydraulic analysis of the pipe network at a given point in time + ** is done by repeatedly solving a linearized version of the + ** equations for conservation of flow & energy: + ** + ** A*H = F + ** + ** where H = vector of heads (unknowns) at each node, + ** F = vector of right-hand side coeffs. + ** A = square matrix of coeffs. + ** and both A and F are updated at each iteration until there is + ** negligible change in pipe flows. + ** + ** Each row (or column) of A corresponds to a junction in the pipe + ** network. Each link (pipe, pump or valve) in the network has a + ** non-zero entry in the row-column of A that corresponds to its + ** end points. This results in A being symmetric and very sparse. + ** The following arrays are used to efficiently manage this sparsity: + */ +typedef struct { + // hydraulic solution vars + double + *Aii, /* Diagonal coeffs. of A */ + *Aij, /* Non-zero, off-diagonal coeffs. of A */ + *F, /* Right hand side coeffs. */ + *P, /* Inverse headloss derivatives */ + *Y; /* Flow correction factors */ + + int + *Order, /* Node-to-row of A */ + *Row, /* Row-to-node of A */ + *Ndx, /* Index of link's coeff. in Aij */ + *XLNZ, /* Start position of each column in NZSUB */ + *NZSUB, /* Row index of each coeff. in each column */ + *LNZ, /* Position of each coeff. in Aij array */ + *Degree; /* Number of links adjacent to each node */ +} solver_t; + +typedef struct { + double + *NodeDemand, /* Node actual demand */ + *EmitterFlows, /* Emitter flows */ + *LinkSetting, /* Link settings */ + *LinkFlows, /* Link flows */ + *NodeHead, + Htol, /* Hydraulic head tolerance */ + Qtol, /* Flow rate tolerance */ + RQtol, /* Flow resistance tolerance */ + Hexp, /* Exponent in headloss formula */ + Qexp, /* Exponent in orifice formula */ + Dmult, /* Demand multiplier */ + Hacc, /* Hydraulics solution accuracy */ + DampLimit, /* Solution damping threshold */ + Viscos, /* Kin. viscosity (sq ft/sec) */ + SpGrav, /* Specific gravity */ + Epump, /* Global pump efficiency */ + Dsystem, /* Total system demand */ + Ecost, /* Base energy cost per kwh */ + Dcost, /* Energy demand charge/kw/day */ + Emax, /* Peak energy usage */ + *X_tmp; + + int + DefPat, /* Default demand pattern */ + Epat; /* Energy cost time pattern */ + + StatType + *LinkStatus, /* Link status */ + *OldStat; /* Previous link/tank status */ + + int + MaxIter, /* Max. hydraulic trials */ + ExtraIter, /* Extra hydraulic trials */ + Ncoeffs, /* Number of non-0 matrix coeffs*/ + CheckFreq, /* Hydraulics solver parameter */ + MaxCheck; /* Hydraulics solver parameter */ + + char + OpenHflag, /* Hydraul. system opened flag */ + Formflag; /* Hydraulic formula flag */ + + /* Info about hydraulic solution */ + double relativeError; + int iterations; + + /* Flag used to halt taking further time steps */ + int Haltflag; + /* Relaxation factor used for updating flow changes */ + double RelaxFactor; + + solver_t solver; + +} hydraulics_t; + +typedef struct { + int Nnodes, /* Number of network nodes */ + Ntanks, /* Number of tanks */ + Njuncs, /* Number of junction nodes */ + Nlinks, /* Number of network links */ + Npipes, /* Number of pipes */ + Npumps, /* Number of pumps */ + Nvalves, /* Number of valves */ + Ncontrols, /* Number of simple controls */ + Nrules, /* Number of control rules */ + Npats, /* Number of time patterns */ + Ncurves, /* Number of data curves */ + Ncoords; /* Number of Coords */ + + Snode *Node; /* Node data */ + Slink *Link; /* Link data */ + Stank *Tank; /* Tank data */ + Spump *Pump; /* Pump data */ + Svalve *Valve; /* Valve data */ + Spattern *Pattern; /* Time patterns */ + Scurve *Curve; /* Curve data */ + Scoord *Coord; /* Coordinate data */ + Scontrol *Control; /* Control data */ + ENHashTable *NodeHashTable, *LinkHashTable; /* Hash tables for ID labels */ + Padjlist *Adjlist; /* Node adjacency lists */ + +} EN_Network; + + +/* project wrapper */ +struct EN_Project { + + EN_Network network; /// the network description struct + hydraulics_t hydraulics; + rules_t rules; + quality_t quality; + time_options_t time_options; + + parser_data_t parser; + report_options_t report; + out_file_t out_files; + save_options_t save_options; + + double Ucf[MAXVAR]; + + char + Openflag, /// Toolkit open flag + Warnflag, /// Warning flag + Msg[MAXMSG+1], /// General-purpose string: errors, messages + Title[MAXTITLE][MAXMSG+1], /// Problem title + MapFname[MAXFNAME+1]; /// Map file name + + void (* viewprog) (char *); /* Pointer to progress viewing function */ + +}; + + + + +#endif diff --git a/src/vars.h b/src/vars.h index 1b95d1a..04f540c 100755 --- a/src/vars.h +++ b/src/vars.h @@ -1,211 +1,16 @@ -/* -************************************************************************ - Global Variables for EPANET Program - -VERSION: 2.00 -DATE: 5/8/00 - 6/24/02 - 2/14/08 (2.00.12) -AUTHOR: L. Rossman - US EPA - NRMRL - -************************************************************************ -*/ -#ifndef VARS_H -#define VARS_H - -#include -#include "hash.h" - -EXTERN FILE *InFile, /* Input file pointer */ - *OutFile, /* Output file pointer */ - *RptFile, /* Report file pointer */ - *HydFile, /* Hydraulics file pointer */ - *TmpOutFile; /* Temporary file handle */ -EXTERN long HydOffset, /* Hydraulics file byte offset */ - OutOffset1, /* 1st output file byte offset */ - OutOffset2; /* 2nd output file byte offset */ -EXTERN char Msg[MAXMSG+1], /* Text of output message */ - InpFname[MAXFNAME+1], /* Input file name */ - Rpt1Fname[MAXFNAME+1], /* Primary report file name */ - Rpt2Fname[MAXFNAME+1], /* Secondary report file name */ - HydFname[MAXFNAME+1], /* Hydraulics file name */ - OutFname[MAXFNAME+1], /* Binary output file name */ - MapFname[MAXFNAME+1], /* Map file name */ - TmpFname[MAXFNAME+1], /* Temporary file name */ //(2.00.12 - LR) - TmpDir[MAXFNAME+1], /* Temporary directory name */ //(2.00.12 - LR) - Title[MAXTITLE][MAXMSG+1], /* Problem title */ - ChemName[MAXID+1], /* Name of chemical */ - ChemUnits[MAXID+1], /* Units of chemical */ - DefPatID[MAXID+1], /* Default demand pattern ID */ - -/*** Updated 6/24/02 ***/ - Atime[13], /* Clock time (hrs:min:sec) */ - - Outflag, /* Output file flag */ //(2.00.12 - LR) - Hydflag, /* Hydraulics flag */ - Qualflag, /* Water quality flag */ - Reactflag, /* Reaction indicator */ //(2.00.12 - LR) - Unitsflag, /* Unit system flag */ - Flowflag, /* Flow units flag */ - Pressflag, /* Pressure units flag */ - Formflag, /* Hydraulic formula flag */ - Rptflag, /* Report flag */ - Summaryflag, /* Report summary flag */ - Messageflag, /* Error/warning message flag */ - Statflag, /* Status report flag */ - Energyflag, /* Energy report flag */ - Nodeflag, /* Node report flag */ - Linkflag, /* Link report flag */ - Tstatflag, /* Time statistics flag */ - Warnflag, /* Warning flag */ - Openflag, /* Input processed flag */ - OpenHflag, /* Hydraul. system opened flag */ - SaveHflag, /* Hydraul. results saved flag */ - OpenQflag, /* Quality system opened flag */ - SaveQflag, /* Quality results saved flag */ - Saveflag, /* General purpose save flag */ - Coordflag; /* Load coordinates flag */ -EXTERN int MaxNodes, /* Node count from input file */ - MaxLinks, /* Link count from input file */ - MaxJuncs, /* Junction count */ - MaxPipes, /* Pipe count */ - MaxTanks, /* Tank count */ - MaxPumps, /* Pump count */ - MaxValves, /* Valve count */ - MaxControls, /* Control count */ - MaxRules, /* Rule count */ - MaxPats, /* Pattern count */ - MaxCurves, /* Curve count */ - Nnodes, /* Number of network nodes */ - Ntanks, /* Number of tanks */ - Njuncs, /* Number of junction nodes */ - Nlinks, /* Number of network links */ - Npipes, /* Number of pipes */ - Npumps, /* Number of pumps */ - Nvalves, /* Number of valves */ - Ncontrols, /* Number of simple controls */ - Nrules, /* Number of control rules */ - Npats, /* Number of time patterns */ - Ncurves, /* Number of data curves */ - Nperiods, /* Number of reporting periods */ - Ncoeffs, /* Number of non-0 matrix coeffs*/ - DefPat, /* Default demand pattern */ - Epat, /* Energy cost time pattern */ - MaxIter, /* Max. hydraulic trials */ - ExtraIter, /* Extra hydraulic trials */ - TraceNode, /* Source node for flow tracing */ - PageSize, /* Lines/page in output report */ - CheckFreq, /* Hydraulics solver parameter */ - MaxCheck; /* Hydraulics solver parameter */ -EXTERN double Ucf[MAXVAR], /* Unit conversion factors */ - Ctol, /* Water quality tolerance */ - Htol, /* Hydraulic head tolerance */ - Qtol, /* Flow rate tolerance */ - RQtol, /* Flow resistance tolerance */ - Hexp, /* Exponent in headloss formula */ - Qexp, /* Exponent in orifice formula */ - Dmult, /* Demand multiplier */ - Hacc, /* Hydraulics solution accuracy */ - DampLimit, /* Solution damping threshold */ //(2.00.12 - LR) - BulkOrder, /* Bulk flow reaction order */ - WallOrder, /* Pipe wall reaction order */ - TankOrder, /* Tank reaction order */ - Kbulk, /* Global bulk reaction coeff. */ - Kwall, /* Global wall reaction coeff. */ - Climit, /* Limiting potential quality */ - Rfactor, /* Roughness-reaction factor */ - Diffus, /* Diffusivity (sq ft/sec) */ - Viscos, /* Kin. viscosity (sq ft/sec) */ - SpGrav, /* Specific gravity */ - Ecost, /* Base energy cost per kwh */ - Dcost, /* Energy demand charge/kw/day */ - Epump, /* Global pump efficiency */ - Emax, /* Peak energy usage */ - Dsystem, /* Total system demand */ - Wbulk, /* Avg. bulk reaction rate */ - Wwall, /* Avg. wall reaction rate */ - Wtank, /* Avg. tank reaction rate */ - Wsource; /* Avg. mass inflow */ -EXTERN long Tstart, /* Starting time of day (sec) */ - Hstep, /* Nominal hyd. time step (sec) */ - Qstep, /* Quality time step (sec) */ - Pstep, /* Time pattern time step (sec) */ - Pstart, /* Starting pattern time (sec) */ - Rstep, /* Reporting time step (sec) */ - Rstart, /* Time when reporting starts */ - Rtime, /* Next reporting time */ - Htime, /* Current hyd. time (sec) */ - Qtime, /* Current quality time (sec) */ - Hydstep, /* Actual hydraulic time step */ - Rulestep, /* Rule evaluation time step */ - Dur; /* Duration of simulation (sec) */ -EXTERN SField Field[MAXVAR]; /* Output reporting fields */ - -/* Array pointers not allocated and freed in same routine */ -EXTERN char *LinkStatus, /* Link status */ - *OldStat; /* Previous link/tank status */ -EXTERN double *NodeDemand, /* Node actual demand */ - *NodeQual, /* Node actual quality */ - *E, /* Emitter flows */ - *LinkSetting, /* Link settings */ - *Q, /* Link flows */ - *PipeRateCoeff, /* Pipe reaction rate */ - *X, /* General purpose array */ - *TempQual; /* General purpose array for water quality */ -EXTERN double *NodeHead; /* Node heads */ -EXTERN double *QTankVolumes; -EXTERN double *QLinkFlow; -EXTERN STmplist *Patlist; /* Temporary time pattern list */ -EXTERN STmplist *Curvelist; /* Temporary list of curves */ -EXTERN Spattern *Pattern; /* Time patterns */ -EXTERN Scurve *Curve; /* Curve data */ -EXTERN Scoord *Coord; /* Coordinate data */ -EXTERN Snode *Node; /* Node data */ -EXTERN Slink *Link; /* Link data */ -EXTERN Stank *Tank; /* Tank data */ -EXTERN Spump *Pump; /* Pump data */ -EXTERN Svalve *Valve; /* Valve data */ -EXTERN Scontrol *Control; /* Control data */ -EXTERN ENHashTable *NodeHashTable, *LinkHashTable; /* Hash tables for ID labels */ -EXTERN Padjlist *Adjlist; /* Node adjacency lists */ -EXTERN double _relativeError; -EXTERN int _iterations; /* Info about hydraulic solution */ - -/* -** NOTE: Hydraulic analysis of the pipe network at a given point in time -** is done by repeatedly solving a linearized version of the -** equations for conservation of flow & energy: -** -** A*H = F -** -** where H = vector of heads (unknowns) at each node, -** F = vector of right-hand side coeffs. -** A = square matrix of coeffs. -** and both A and F are updated at each iteration until there is -** negligible change in pipe flows. -** -** Each row (or column) of A corresponds to a junction in the pipe -** network. Each link (pipe, pump or valve) in the network has a -** non-zero entry in the row-column of A that corresponds to its -** end points. This results in A being symmetric and very sparse. -** The following arrays are used to efficiently manage this sparsity: -*/ - -EXTERN double *Aii, /* Diagonal coeffs. of A */ - *Aij, /* Non-zero, off-diagonal coeffs. of A */ - *F; /* Right hand side coeffs. */ -EXTERN double *P, /* Inverse headloss derivatives */ - *Y; /* Flow correction factors */ -EXTERN int *Order, /* Node-to-row of A */ - *Row, /* Row-to-node of A */ - *Ndx; /* Index of link's coeff. in Aij */ -/* -** The following arrays store the positions of the non-zero coeffs. -** of the lower triangular portion of A whose values are stored in Aij: -*/ -EXTERN int *XLNZ, /* Start position of each column in NZSUB */ - *NZSUB, /* Row index of each coeff. in each column */ - *LNZ; /* Position of each coeff. in Aij array */ - -#endif \ No newline at end of file +/* +************************************************************************ + Global Variables for EPANET Program +************************************************************************ +*/ +#ifndef VARS_H +#define VARS_H + +#include +#include "hash.h" + +// this single global variable is used only when the library is called in "legacy mode" +// with the 2.1-style API. +EXTERN EN_Project *_defaultModel; + +#endif diff --git a/tests/.gitignore b/tests/.gitignore new file mode 100644 index 0000000..f5257d3 --- /dev/null +++ b/tests/.gitignore @@ -0,0 +1,9 @@ +# Ignore app description files generated for nrtest +apps/ + +# Ignore results generated during nrtest execution +benchmark/ + +# Except changes to reference benchmark +!benchmark/epanet-2012 # Results for EPANET 2.0.12 MSVC 2010 32-bit + diff --git a/tests/epanet-nrtestsuite/apps/epanet-2011a.json b/tests/epanet-nrtestsuite/apps/epanet-2011a.json new file mode 100644 index 0000000..c45367a --- /dev/null +++ b/tests/epanet-nrtestsuite/apps/epanet-2011a.json @@ -0,0 +1,7 @@ +{ + "name" : "epanet", + "version" : "2.0.11a", + "description" : "Windows, MS VC2005 or older, 32 Bit", + "setup_script" : "", + "exe" : "epanet2011a.exe" +} diff --git a/tests/epanet-nrtestsuite/apps/epanet-2012.json b/tests/epanet-nrtestsuite/apps/epanet-2012.json new file mode 100644 index 0000000..7c46f81 --- /dev/null +++ b/tests/epanet-nrtestsuite/apps/epanet-2012.json @@ -0,0 +1,7 @@ +{ + "name" : "epanet", + "version" : "2.0.12", + "description" : "Windows, MS VC 2010, 32 Bit", + "setup_script" : "", + "exe" : "./epanet2012.exe" +} diff --git a/tests/epanet-nrtestsuite/benchmark/epanet-2012/Example_1/example1.out b/tests/epanet-nrtestsuite/benchmark/epanet-2012/Example_1/example1.out new file mode 100644 index 0000000000000000000000000000000000000000..3e18375691b561300f1bcb5c2f498b1e99b4c87e GIT binary patch literal 16832 zcmeI4cT^Nf_xA?`K@21j!9WnnL69g^-J-e(q8Px82^BMO227}^sHhlGQ8DKnP!z!k zh&j6|h?$HCNDxW>dS=*N_jz`8{k`Y)kN3QN&OJp}S9g88`ZKrg+^%7`XL?ujegZ*j zfj}V3TU*{_coT2`SxRdK0+n_GLDWDQfv(#C*M4q;blqn92Zl@!(Crr>nh_i}P1joY z_m)3by6Ot2NG=r6e8J#}x`DwH0;W$1nxs2^^7P=aDM10c69UHj&$85|!J^5!=$69Oh!>i+#H-xb^}%i3~EP{?;1 z{{MsjPmciCjN(7tWZ)#LzW;PgTyNdudq?@>);~CI^Mm6(zkl532iLdx!S!u^aQ&V? zxPH$cT;J*Y@&!LQ{r~#-{-2KDkH*LP2Y^`r01#{I?~jnR_4gm3^$%{)`Uimg==q7~ z74cltf;V~ITJomI+wXIbxL%I8-;XKrfB)WITrX~~z*{TczP*OHPJA74y||B`HE|!} zzQj7jTjuCGCWx|@n!t()u2?X^?`4#^^l$NTfhXT)5EH~0xC;dP-2U4?+cnuTrZuY)v_m2fnX!urah9`FT6rqPrb_Tf4g%9AIIEA$7V(o17h7KfdrvXVO z3b3NU1JvjV+()Yv+xD3OM`Rn|wPGu{nhLm&#zg2+ZYI_uc;;USYtkLaDD%lUWmp;3 zkvoXuRQF-Ql|Xg41U7Kq;c$U^LJG%7^t$|L1!k|8RfedP08Q#LwwxOgwf! zzsaV_pv5cXQHPZ_X!c@b`{5oh#g58vl$ zvsyuhb$4fGa%v8%Y>HreJ%!Ayp66+Yka;QyS#C)fbBhmSisoSwo<=eu`?OKO#pajO zyu=SQzP^kedRt1@cQ2=JJ5|wkeT(T6i#HOUoV*9~WfTZUzsG?ZZ?K-n3%vg5W2|KI z0`E|Mhue(3kCPuq3jK@b(UFEP;|O1Jb$yow^^xic84cO38SP=Y6J3k3zaI=F;dL(M&gW1|Bg ztioI}_8LQTVQqa+BFLDE`;9KZwSxUfG-o#!kRv!bq#IFGcE>7~PH4|^Pq5cAguNO4 z(nqcy2*cLA!79^V;Pkks_y+e5>$}{P+?V~1m$>BEYkYa%D-4_N<8kz+B{6?L z!;-jd6v)3&CiAY`1TCG15DBj#`PyzcRH;Gylh%OKJy~*kYZC00OMzERk#tM!#`hr* zEGU;j5gGC*G{6Q~)EguDY)ABDsXIzq+a9gxrjE?R%~5c#p(w@Q40$j3mM2@@AK7v; zf^xoZaT)T*zPRqn@w|*hqhy$mS|pnpID?ICDPlT{giJ0+$P6NcY~N5Jo3b#BO=&0P z$w5xUhJg%sAm9^?XJz!oqY8TU)JOVqUj?1GwwhjUQA`c5zvlZA2z=&`kle4#EI)X* zpbvcYO~w9`Q}L>}3;6Sm>-etr0c^8&lVt2qc?1Kmh24!^%N|#oa~jV(aDMHZsPQXf z%InK`u(XErY+T2AJ@ci@9rU>mYZAoTnH_6SUJd9(QeWFb`K2x}IXW4ux>e&Paz>C^ zARtEb21AP4G5lz0G+0&oOL$s%_bp`A8IXimnb^p03pRS3jLNT`z=8+nc#pt>h?Zqz z_scCXEm{awGzL6-4@kdS9Rx;^gJ4Ky2AN@2x^)vzgZ7#XU!6TZ~$h6a)kjtdls z_}MtNlSLCxw?t!FnW3~8OBC$ofF5|cA+5Ka(fyffNcX)S(w1>Y!VDu69-gW>sT$rS+%I5 zwaOKAe?leApI%Otrd85OLqF4QS4(Kf(zgaVlGa~`;5=UFL#QujDLgMWhxbR_M zx?)xke9ZTS>*fluYI-rg_UQtOo9@cj1-WTZ7>Cx?jpu)&moA zC()U_J1s}jieE$Pw~=5Re-#cid<5gCVTAN=fNSjzfbn7*Vr*O|8H3}NvMBXsOSGoi z6vdWXqWj|o;3^MCvd*1mvNo;5nVXx4P46#ai_}Ccqg=?i^O6?MMF&v&Dcj-n$;^K=3CR#CXq%L9-%Fdgn3@kv)n z4T7QrzTl#02J!K#@L}sVyt!}@zO$+~xWx8@C3|h)e9stM%yoy2XMZhE?e>|F8Q#uh ztDOm1>eZS&P#zCPc9)?hp%j{?gpsp{yO3wav9Q)-1leX-AsK@MNEY3Hs(`{4cSnM3 zJ2d~H9a8SZp}|MZkm(gwRMVu5wx>Cx5i<;srjFG&{^|QXwLSPQqvy^T?$P^5_P}o@ zyXX_nB4b7D?0ONC8!lp#97OD4Rv1egB=t}2`QyUFvRu}VYI-W8lAfJfP3x^IsCRM| zRoq-lQ*BD9AXOSal`b1E;YneMAKbj-0k3`4VRjz?TLUdn)R}_|MD1Xnw}XVI)TlAQ zYhhl`W0**L7?;-W9gWSC=bmQUQC?qqYiQz9PhaDTY&X%Q2q$iX+C?gE-*tc*dD2pi zoN4rjEfYWB+F5D1UPTV9C!EAF<#NOmffKe;c!E?25`iP@JkZ9-&5eb&yH>MMG_;?H# z@_u%Aj0c2#>IYrt*u%!tyRnnHF${Hm_e=c~T4O>UmpYNeDtq#_ye+Yv83_)`pWv?R z9T>PrNN&F~CM4?&1nnG0UdLhHFAD@``^%z3WeTX{-0o0_;Oy1c29E`Ru$ zu3TA2?Jr8d--QMTNO(FM;|JZBdBEuk4Orv>Q1Vg>g2xWUy8BhYX+peY>~|%O0A35z zcUs6S3!*qKX*b<#-HubP_Mp7Jw7TEq*qiCqT=Ld*${Gi9v*dHAxcz!>HBwfsM3n9> zfeuE8vBuJSIM`kljI5TS{30b1>6{1=4|G7Z`Uq^gu|x9NeAFig5?x!81Uvu|uV$dy zqZ*AGs++QYKgG`!8v7;c9P_7DY}W z=%Ck7RP#awjc(TgW!$$#OK0n$YYt|A_@^K7bbHRbjKQ<|)1peuwxwVL$=oZGb7E5_b;g7eyEX!TIS53ont7ueIHC^seP9K?9(c-V4>G;$_ zYE~lU>G@qLPve&P!SrtK&|szoEfopii+XU?a2__SR|kChl7y#7!y&+HVPiWkWcN#> zI0dqs`Wv+4Sg8l)^`-rmDaV#XS9ADWIvrR)kQ?19hl<X1h2jHiCG(eNYD=_3+DD9 z>*CsyrZ-c`MZ*vYPm_FQkzutW(sI;8y?p_-;dxpeNRabBH#Fmw3YylY1Jb=?i%NK& zlE2|e8aw@S{N%LkZN__pXzqBAxy=3aOt$xQIJ@{k#72D*G4Es%d%01>esd5q$J5gJ zPM$x0$f^Y=f4_!qYF|x9C)d#J5ufO0*&15@{tMkT^8*c)eaHJHKJRBL&W%OEeV(U4 zJ$I1z>jY2b349u&3o)7}@Zt~+sPB10!c+It1A*7VZdWX2O>>5E{Z7B4veEKfm!)=; z*Oz{7R3qm#A(LzPyouiMcj7`_E>dy(nvJUDwTdc98b1ymzc0kzAFg7HCoQ0I%VB(O zwj7zDz6R7Qzu>!~cz{6Ze7CxPHmn(^N(3i5K;S1Ud~5$L^tP1Oam5{NXtX1VlQC#^ zS%`IZ9ssYG>5!Z@DE(c05Dbp-0d+fT2wSKIod!4H56RDQ$xIHGOz96bLmZ+1hH7j# z#|&&+wfdzzZ4EOa`lB65{xTpf4}F5%ZD@9G*aJ1 zB;iSAvMf5Ms)#JZx}%C%dnEj1hela(=wh)sLT^=3pBLI_LbNj)6={H$xBqj#`w>qy zm)~S$e~seCO`OZdmCt0uKZmnMt8jL=XE=LZCt|C!L~O|{A+xWR@^rO^=PkAcm%aNl z?en#oM(+Adg~KbUUr`O!yIn_zjx41%1yY{kzfO|y6d3CVtIRy$<^WCD)Dd86Zbx`` z=mOSMQG>9QGzm{?y}g0g!deYj%7j;jael*c=qixssH+Du)2 zow#|!FH&*))*7nBb%F}%b7UNN1?J;7F;{WJp%##^Vn5b4mLu+gYvAUiI$Sn69)>QI z@)TC^9FFO$kUa{TuxF7u&dR-s>}QF&#^{2X9WinPz*P%z)Y1bmTq6TwHV;m(TO0%) zPkrF|K1*1vuK~i(4ftcjQ>@v=1H#YtgIQ1PKyFewo`2LBHeZtYr91@=H6c2aoyfHz z4#fClO9C6uL)mUUGV;|8SZ^yN0Y>+31FFc4T)rvsfI?=Is-)ueKsdKJ$%Z1XFjD)+Xrf9D zm9_dpTXt=vM$;?kY3BlofBJfJii9V$+7D*sxU1+E1dN{B<0EcWCWXED8~s?>ge;uwKTS6J)OAYGY!tH zqt<7e=T zoxr(mB|fq2F3!#M0IeKvc;aaTk!N4y^MwYWTqKUgeqB7iSj~iF$@e0So6X5{B@Hr0 z{{;9rYm$zu3t?J)2vOAUO0@46Kzp?jB)_(ggr_elvZ!Xg0&?GNiq@U&fui3#AoVwH zXi2IL3Tms4j;iRPRtp$1$}>U_K7Gs6_usd4+w~&DgBq~7%k!Amtl8|d(F|7mTLe?_ zo59B9M6e@i5v9H$HT;E0!6}Nw#*_JrjX_3&rZGZ`_ zL53W}+YH`f2h~BKmQ)74w|9VcZe#GAwwCZT`PqH&d!b7-XU)V#RhRLt%mh^1 zumKBVmf$=KO%kXUfQvp&K?$uG>}hKSeVaz4_i^F#1*?3Z$~3I} zBOaiz+Z#4!T7!DeXLy^y090O-{8F9@3XRF$O-`i!Ix|AIX^@+J4@0W9Iys(_4<#!? zNXs)_$k++5LFdRY@&$QFc#1EPMOI&0qNI)1sC2OzdU(ePnLhA9nNgakuw8o;H^UU^ zdH5oOQ)cL9a<6av)AxB2`#y!JWf=zRBbeC6)I_jhBO};v+2L$!B|qntgtG@P3gFXfEo>2%MvF5J9?*tAzE;dJ59 zJXc;HvmP~oOOLt9t<^t8D-H`dox`gneZ;I;X%}G&Ah29+eGzAr-$&z!H=1hYKD+)0W3}I=48mPqQ;kiRDW3ld=$2ic`wStDw$9UV_uJH6> z0rnj+k3Wx)pECj9{%7+A0zq{}DRzlbCk2O1Nmr{PSax{<4ByiL0Xv%D*4@4&>AF6d zcJMPCl=C9*D}e7qy2*^vMZe`3qY2YEbhJxfw6k;oqW#+-jq!%4?R;%yAL)QHw_Bi> ztL*>aN&JpZ`tELLb1BY4iQnbzY&P*ag^?LS+rydIro0a4o0bXfs3^^Iy|KLfph~dOBTX4crccgP+p37j3_ejvO0E^C_$M#2M z!2YE_&RX&ozg?pxIVZ7dB2;IaLIBo>Q}Je{#E!Xv-R5QpA|Bx%K6cv3kREKJJ4EB*^)O|T{HZgnBS_$%}Xav}2$ zco8X00>RZQ-H{Cc?v88hjXr+$L9NS&qC@JcD6Q5EwLGDNT=yfSEbNI&PPqIuPx%-4 z_jbI!Z4|M_coFlVB9`PMV&UE*HtU#>UxULNzaHm6i*R;Zt%!>I+t*fTv+LzPd z-BmO)ua-_z`A8LVb0yE(ajHMv>Kz82R2foU^@JnSp}QW0Zq|kc`-<`S$65H~iGx_IyLAgyNZQ{G!jC`2SI)|Sa7ih? zXE^JZ@g(b)i{;Y_A)fJb^^2o0_`E;Nce)RS?Mk8cB%kkXp-w*U`UvYmhn$l^f8pue zGD}om-W#Pq^FjBjhoaaz6$wvOJWt7c{+lO(;0*t4&+xuU9LtDHv2T*@OY6k`se48| z|L#|8sa-{^^qr9HJt<@~MaV4937Ok)A@ks*Jn86*SpTW{RBUpa%V<^CN;rnai1zRyCr+|`5V`H5TTN*gmt zA36T_x zfXbchaF#)lOKgyai+%6KNU(D$Hc}XY?VUH_fH*lE;2(_z?q?+DJPR8IUS*x(jz&<_QFYSu>Wju9Dc#R!*zk`b()QCgV zPPq1F1hm_F88(|1LWXM-^x3FP&MnFV*@RX^W(~hir+J%KZH`{+*rPFtKIm2Jk!ZJ% zKUz6T6CL1vlG|=WG&$c5ZK2M{I+y+7pZ+aRA^n9+Y#YLb?7EYX&E?lVbygKJg?2)w zb25zSZIR}mYG(>rwN5S-+p&J7bo-NX+U8*i-OuO9?blV&p1X_b%p=nE1y$#VQ0qaH z;kua|Ox8}_i^jacks4hE3sJjyxaNM z;jR{h7+l3!&o5!k4G*!FspyySw5lo_1HJ>obuGyHT`@opIfIS*R_Iax9PHAc!?ahj z#6=|&9(cY0xq?oA@T6EL<;m;DpF9=y_$yE0WsqrHX8B$EA zr+%cnTUXM>*Ndp)mp2mM;o04XTFf60daIl8K1CDI-jRnZ>W*X8M=iicv=-YHB;Y0f z3ne`5vp0pS!+NJ28m64SJ9{H7s(o{H=7tPcn^_L7ygsh<7$`kYoYjjIj>8)jXWsj$%E%c`JmkAf92`l%y(`0_$e(Tl!-0BIFxlf5z6jGhO)pVp={WqP!`fXl!YlvdGh)g z%AQ?+Ma33dQ$XiNme7)!`82w$m}>MYqpDtcbf|BR#6Kms>Pv6xjexwSJZ#cRA1-dW zjh}tpfX`W#;$n|E*!22j+*5Xtgr_I88$@fel>FxTZs#}i9k1^n*D*s(`||{`rKFi^x#>X7jzD{z3GL`+jWCf*)*K( zrUcq)Td}D1Ts&pSA)KcY_{(@29CsH_P~Howqwd48r(U2D*p~mU>jI(!sc>%3Zm?E* z3SVnhz?#x1@G5=%2Tu!|g@LuYaioiYgm%U*|wP;LUz)d1vY`(Bql)7z2$NX(Ns9^bmt5aqsgLt>t?^8Sp+ z|4sg>HJ^WS863jIX1_6n`Su852eN}%>ylu0D=(Pca|>oebb}>4H710x7k9I$*ftJ- zPY-(M)8PAWXw;}Yx@>0=oq78e)!gx$gs0(eDD7Z05X`xk`1OKLp!6{f&w0KMhX#~l zIR$?#sIkU#vs@)SCB=6Jtz4IswaxjbJAD30oG-BB^H01!R@Iz;`p!kT7_P@wKZ9MidW8<+XW*#?-9pKld>>M7#lI%iQhJ7fj^bTRwwjpfY53_iDgw~y$#yvF8r5CGADX%Z1 zy+au{e?UBaX>*Z`o?T73)*5{MM|MR`o9uaINp77ujStU?fQU2uaI|+Vo~XAUluYVi zK&Bs7kJCmKD?Opp?B{%60znVaCm289o4B9ILhfUGK-!FW)bHMDED&kKv~Ar;za?$a zD3PjjfL&*Zow)*^?CYNX%HA6;jpzXCa`kxZsW>b*lz)FbPXUYX>r;IbUbDI-EUa9M zbKV5wkS<3g_w;it{m$Oc@)QLxuc(k!r)7x~djc2Q?Su>agWv+czWRDmBJ_7^M^N1! zxVhdE%RkrvcF(FLJVg$aK}-GQkpF3f8pd`*cStX^u8g6qvu%-`t~#2RN8Q8O+|i*-S3Q(nxgN?M>hW_R zKlgo24Pm0b()liRI3GWi)zi`C{O_Wa^XbzuMKme0fTpi2qE1Un>Drw4blQn52~W3M zcuII0k4HmpUjvw&nTE$5(T1|4i?QLyC_FBQ;-ee7NX9;*UITb7Ed23ScHBz94fJN* z-kp`ybZ~dd>&xi>u#!7#;m8Heee2q1a9i%T!&9la{j(DtiQRA`V$ilF?DF&m*V2=C zr+zu^kTf4GzcfMP6?q7Ba>ONN6G1r5R5F%#0zbl)Kt%S8nSgbs*5kx4hmnyC|2~+H zv5n@LkR#a+IQn%rr1;zmV)y8QAlWlLmXEJT=-EQ&4xjj7H4R_5xdSV!$YAk(^;>QZ z30E6&M&=rBO~woCqU#m z3p_yh1PWcANXFpSTp6_8Q68RHZ#+!LO!3qbe>nMVIRC#J{s(q2U3CBe literal 0 HcmV?d00001 diff --git a/tests/epanet-nrtestsuite/benchmark/epanet-2012/Example_1/example1.rpt b/tests/epanet-nrtestsuite/benchmark/epanet-2012/Example_1/example1.rpt new file mode 100644 index 0000000..5764224 --- /dev/null +++ b/tests/epanet-nrtestsuite/benchmark/epanet-2012/Example_1/example1.rpt @@ -0,0 +1,1029 @@ + Page 1 Fri Oct 13 13:50:29 2017 + + ****************************************************************** + * E P A N E T * + * Hydraulic and Water Quality * + * Analysis for Pipe Networks * + * Version 2.00.12 * + ****************************************************************** + + Analysis begun Fri Oct 13 13:50:29 2017 + + + Hydraulic Status: + ----------------------------------------------------------------------- + 0:00:00: Balanced after 4 trials + 0:00:00: Reservoir 9 is emptying + 0:00:00: Tank 2 is filling at 120.00 ft + + 1:00:00: Balanced after 2 trials + + 2:00:00: Balanced after 3 trials + + 3:00:00: Balanced after 2 trials + + 4:00:00: Balanced after 3 trials + + 5:00:00: Balanced after 2 trials + + 6:00:00: Balanced after 2 trials + + 7:00:00: Balanced after 1 trials + + 8:00:00: Balanced after 2 trials + + 9:00:00: Balanced after 2 trials + + 10:00:00: Balanced after 2 trials + + 11:00:00: Balanced after 2 trials + + 12:00:00: Balanced after 3 trials + + 12:32:34: Pump 9 changed by Tank 2 control + 12:32:34: Balanced after 4 trials + 12:32:34: Reservoir 9 is closed + 12:32:34: Tank 2 is emptying at 140.00 ft + 12:32:34: Pump 9 changed from open to closed + + 13:00:00: Balanced after 1 trials + + 14:00:00: Balanced after 2 trials + + 15:00:00: Balanced after 1 trials + + 16:00:00: Balanced after 2 trials + + 17:00:00: Balanced after 1 trials + + 18:00:00: Balanced after 2 trials + + 19:00:00: Balanced after 1 trials + + 20:00:00: Balanced after 2 trials + + 21:00:00: Balanced after 1 trials + + 22:00:00: Balanced after 2 trials + + 22:41:30: Pump 9 changed by Tank 2 control + 22:41:30: Balanced after 15 trials + 22:41:30: Reservoir 9 is emptying + 22:41:30: Tank 2 is filling at 110.00 ft + 22:41:30: Pump 9 changed from closed to open + + 23:00:00: Balanced after 2 trials + + 24:00:00: Balanced after 3 trials + + + Node Results at 0:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Chlorine + Node gpm ft psi mg/L + -------------------------------------------------------- + 10 0.00 1004.35 127.54 0.50 + 11 150.00 985.23 119.26 0.50 + 12 150.00 970.07 117.02 0.50 + 13 100.00 968.87 118.67 0.50 + 21 150.00 971.55 117.66 0.50 + 22 200.00 969.08 118.76 0.50 + 23 150.00 968.65 120.74 0.50 + 31 100.00 967.39 115.86 0.50 + 32 100.00 965.69 110.79 0.50 + 9 -1866.18 800.00 0.00 1.00 Reservoir + 2 766.18 970.00 52.00 1.00 Tank + + + Link Results at 0:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 10 1866.18 2.35 1.82 + 11 1234.21 2.57 2.87 + 12 129.34 0.53 0.23 + 21 191.16 0.78 0.47 + 22 120.66 0.34 0.08 + 31 40.81 0.46 0.32 + 110 -766.18 0.97 0.35 + 111 481.97 1.97 2.59 + 112 188.70 0.54 0.19 + 113 29.34 0.19 0.04 + 121 140.81 0.90 0.79 + 122 59.19 0.67 0.64 + 9 1866.18 0.00 -204.35 Pump + + + Node Results at 1:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Chlorine + Node gpm ft psi mg/L + -------------------------------------------------------- + 10 0.00 1006.77 128.59 1.00 + 11 150.00 987.98 120.45 0.44 + 12 150.00 973.14 118.35 0.44 + 13 100.00 971.92 119.99 0.43 + 21 150.00 974.50 118.94 0.41 + 22 200.00 972.11 120.07 0.43 + 23 150.00 971.69 122.05 0.44 + 31 100.00 970.36 117.15 0.41 + 32 100.00 968.69 112.09 0.39 + 9 -1848.58 800.00 0.00 1.00 Reservoir + 2 748.58 973.07 53.33 0.97 Tank + + + Link Results at 1:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 10 1848.58 2.33 1.78 + 11 1220.43 2.54 2.81 + 12 130.11 0.53 0.23 + 21 187.69 0.77 0.45 + 22 119.89 0.34 0.08 + 31 40.46 0.46 0.32 + 110 -748.58 0.94 0.33 + 111 478.15 1.95 2.55 + 112 191.73 0.54 0.19 + 113 30.11 0.19 0.05 + 121 140.46 0.90 0.78 + 122 59.54 0.68 0.65 + 9 1848.58 0.00 -206.77 Pump + + + Node Results at 2:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Chlorine + Node gpm ft psi mg/L + -------------------------------------------------------- + 10 0.00 1008.29 129.25 1.00 + 11 180.00 989.71 121.20 0.86 + 12 180.00 976.10 119.63 0.79 + 13 120.00 974.05 120.91 0.37 + 21 180.00 975.44 119.35 0.37 + 22 240.00 973.85 120.82 0.37 + 23 180.00 973.37 122.78 0.39 + 31 120.00 970.04 117.01 0.33 + 32 120.00 968.23 111.89 0.30 + 9 -1837.46 800.00 0.00 1.00 Reservoir + 2 517.46 976.07 54.62 0.93 Tank + + + Link Results at 2:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 10 1837.46 2.32 1.76 + 11 1164.40 2.43 2.58 + 12 172.96 0.71 0.39 + 21 150.84 0.62 0.30 + 22 127.04 0.36 0.09 + 31 42.22 0.48 0.34 + 110 -517.46 0.65 0.17 + 111 493.06 2.01 2.70 + 112 293.98 0.83 0.43 + 113 52.96 0.34 0.13 + 121 162.22 1.04 1.02 + 122 77.78 0.88 1.06 + 9 1837.46 0.00 -208.29 Pump + + + Node Results at 3:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Chlorine + Node gpm ft psi mg/L + -------------------------------------------------------- + 10 0.00 1009.92 129.96 1.00 + 11 180.00 991.57 122.01 0.86 + 12 180.00 978.17 120.53 0.79 + 13 120.00 976.11 121.80 0.31 + 21 180.00 977.44 120.21 0.75 + 22 240.00 975.89 121.71 0.32 + 23 180.00 975.42 123.67 0.34 + 31 120.00 972.05 117.88 0.28 + 32 120.00 970.25 112.76 0.24 + 9 -1825.38 800.00 0.00 1.00 Reservoir + 2 505.38 978.14 55.52 0.91 Tank + + + Link Results at 3:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 10 1825.38 2.30 1.74 + 11 1154.82 2.41 2.54 + 12 173.49 0.71 0.39 + 21 148.51 0.61 0.29 + 22 126.51 0.36 0.09 + 31 42.05 0.48 0.34 + 110 -505.38 0.64 0.16 + 111 490.57 2.00 2.68 + 112 295.94 0.84 0.43 + 113 53.49 0.34 0.13 + 121 162.05 1.03 1.02 + 122 77.95 0.88 1.07 + 9 1825.38 0.00 -209.92 Pump + + + Node Results at 4:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Chlorine + Node gpm ft psi mg/L + -------------------------------------------------------- + 10 0.00 1010.67 130.28 1.00 + 11 210.00 992.42 122.37 0.86 + 12 210.00 980.17 121.40 0.79 + 13 140.00 977.08 122.23 0.30 + 21 210.00 977.24 120.13 0.75 + 22 280.00 976.29 121.88 0.49 + 23 210.00 975.76 123.82 0.30 + 31 140.00 970.32 117.13 0.54 + 32 140.00 968.23 111.89 0.21 + 9 -1819.86 800.00 0.00 1.00 Reservoir + 2 279.86 980.16 56.40 0.89 Tank + + + Link Results at 4:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 10 1819.86 2.29 1.73 + 11 1100.06 2.29 2.32 + 12 215.82 0.88 0.59 + 21 114.27 0.47 0.18 + 22 134.18 0.38 0.10 + 31 45.52 0.52 0.39 + 110 -279.86 0.35 0.05 + 111 509.79 2.08 2.88 + 112 394.39 1.12 0.74 + 113 75.82 0.48 0.25 + 121 185.52 1.18 1.31 + 122 94.48 1.07 1.53 + 9 1819.86 0.00 -210.67 Pump + + + Node Results at 5:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Chlorine + Node gpm ft psi mg/L + -------------------------------------------------------- + 10 0.00 1011.56 130.67 1.00 + 11 210.00 993.43 122.81 0.86 + 12 210.00 981.29 121.88 0.78 + 13 140.00 978.20 122.71 0.58 + 21 210.00 978.32 120.60 0.75 + 22 280.00 977.39 122.36 0.60 + 23 210.00 976.87 124.30 0.25 + 31 140.00 971.40 117.60 0.56 + 32 140.00 969.32 112.37 0.18 + 9 -1813.25 800.00 0.00 1.00 Reservoir + 2 273.25 981.28 56.88 0.87 Tank + + + Link Results at 5:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 10 1813.25 2.29 1.72 + 11 1094.76 2.28 2.30 + 12 216.10 0.88 0.59 + 21 113.03 0.46 0.18 + 22 133.90 0.38 0.10 + 31 45.46 0.52 0.39 + 110 -273.25 0.34 0.05 + 111 508.49 2.08 2.86 + 112 395.40 1.12 0.74 + 113 76.10 0.49 0.25 + 121 185.46 1.18 1.31 + 122 94.54 1.07 1.53 + 9 1813.25 0.00 -211.56 Pump + + + Node Results at 6:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Chlorine + Node gpm ft psi mg/L + -------------------------------------------------------- + 10 0.00 1011.58 130.67 1.00 + 11 240.00 993.45 122.82 0.86 + 12 240.00 982.38 122.35 0.78 + 13 160.00 978.08 122.66 0.59 + 21 240.00 977.06 120.05 0.75 + 22 320.00 976.55 122.00 0.60 + 23 240.00 975.97 123.91 0.21 + 31 160.00 968.35 116.28 0.57 + 32 160.00 965.87 110.87 0.30 + 9 -1813.13 800.00 0.00 1.00 Reservoir + 2 53.13 982.38 57.36 0.85 Tank + + + Link Results at 6:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 10 1813.13 2.29 1.72 + 11 1041.79 2.17 2.10 + 12 257.81 1.05 0.81 + 21 81.35 0.33 0.10 + 22 142.19 0.40 0.11 + 31 49.99 0.57 0.47 + 110 -53.13 0.07 0.00 + 111 531.34 2.17 3.10 + 112 490.85 1.39 1.10 + 113 97.81 0.62 0.40 + 121 209.99 1.34 1.65 + 122 110.01 1.25 2.02 + 9 1813.13 0.00 -211.58 Pump + + + Node Results at 7:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Chlorine + Node gpm ft psi mg/L + -------------------------------------------------------- + 10 0.00 1011.75 130.75 1.00 + 11 240.00 993.65 122.90 0.86 + 12 240.00 982.59 122.45 0.77 + 13 160.00 978.29 122.75 0.60 + 21 240.00 977.27 120.14 0.75 + 22 320.00 976.76 122.09 0.63 + 23 240.00 976.18 124.00 0.25 + 31 160.00 968.56 116.37 0.58 + 32 160.00 966.08 110.96 0.37 + 9 -1811.87 800.00 0.00 1.00 Reservoir + 2 51.87 982.59 57.45 0.84 Tank + + + Link Results at 7:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 10 1811.87 2.28 1.72 + 11 1040.76 2.17 2.09 + 12 257.86 1.05 0.81 + 21 81.13 0.33 0.10 + 22 142.14 0.40 0.11 + 31 49.98 0.57 0.47 + 110 -51.87 0.07 0.00 + 111 531.11 2.17 3.10 + 112 491.04 1.39 1.10 + 113 97.86 0.62 0.40 + 121 209.98 1.34 1.65 + 122 110.02 1.25 2.02 + 9 1811.87 0.00 -211.75 Pump + + + Node Results at 8:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Chlorine + Node gpm ft psi mg/L + -------------------------------------------------------- + 10 0.00 1012.76 131.19 1.00 + 11 210.00 994.80 123.40 0.86 + 12 210.00 982.81 122.54 0.77 + 13 140.00 979.70 123.36 0.61 + 21 210.00 979.79 121.23 0.75 + 22 280.00 978.88 123.01 0.63 + 23 210.00 978.36 124.95 0.34 + 31 140.00 972.87 118.24 0.58 + 32 140.00 970.80 113.00 0.39 + 9 -1804.29 800.00 0.00 1.00 Reservoir + 2 264.29 982.80 57.54 0.82 Tank + + + Link Results at 8:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 10 1804.29 2.27 1.71 + 11 1087.56 2.27 2.27 + 12 216.48 0.88 0.59 + 21 111.35 0.45 0.17 + 22 133.52 0.38 0.10 + 31 45.38 0.51 0.39 + 110 -264.29 0.33 0.05 + 111 506.73 2.07 2.84 + 112 396.78 1.13 0.74 + 113 76.48 0.49 0.25 + 121 185.38 1.18 1.31 + 122 94.62 1.07 1.53 + 9 1804.29 0.00 -212.76 Pump + + + Node Results at 9:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Chlorine + Node gpm ft psi mg/L + -------------------------------------------------------- + 10 0.00 1013.60 131.55 1.00 + 11 210.00 995.76 123.82 0.86 + 12 210.00 983.86 123.00 0.78 + 13 140.00 980.75 123.82 0.59 + 21 210.00 980.81 121.67 0.75 + 22 280.00 979.92 123.46 0.60 + 23 210.00 979.40 125.40 0.37 + 31 140.00 973.90 118.68 0.57 + 32 140.00 971.83 113.45 0.38 + 9 -1798.00 800.00 0.00 1.00 Reservoir + 2 258.00 983.86 58.00 0.80 Tank + + + Link Results at 9:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 10 1798.00 2.27 1.69 + 11 1082.50 2.26 2.25 + 12 216.75 0.89 0.59 + 21 110.17 0.45 0.17 + 22 133.25 0.38 0.10 + 31 45.33 0.51 0.39 + 110 -258.00 0.33 0.05 + 111 505.50 2.06 2.83 + 112 397.75 1.13 0.75 + 113 76.75 0.49 0.26 + 121 185.33 1.18 1.31 + 122 94.67 1.07 1.53 + 9 1798.00 0.00 -213.60 Pump + + + Node Results at 10:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Chlorine + Node gpm ft psi mg/L + -------------------------------------------------------- + 10 0.00 1015.26 132.27 1.00 + 11 180.00 997.65 124.64 0.86 + 12 180.00 984.92 123.45 0.78 + 13 120.00 982.82 124.71 0.58 + 21 180.00 983.94 123.03 0.75 + 22 240.00 982.54 124.59 0.59 + 23 180.00 982.08 126.56 0.37 + 31 120.00 978.59 120.71 0.56 + 32 120.00 976.83 115.62 0.36 + 9 -1785.48 800.00 0.00 1.00 Reservoir + 2 465.48 984.89 58.45 0.79 Tank + + + Link Results at 10:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 10 1785.48 2.25 1.67 + 11 1123.12 2.34 2.41 + 12 175.24 0.72 0.40 + 21 140.83 0.58 0.27 + 22 124.76 0.35 0.09 + 31 41.53 0.47 0.33 + 110 -465.48 0.59 0.14 + 111 482.35 1.97 2.60 + 112 302.41 0.86 0.45 + 113 55.24 0.35 0.14 + 121 161.53 1.03 1.01 + 122 78.47 0.89 1.08 + 9 1785.48 0.00 -215.26 Pump + + + Node Results at 11:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Chlorine + Node gpm ft psi mg/L + -------------------------------------------------------- + 10 0.00 1016.73 132.91 1.00 + 11 180.00 999.32 125.36 0.86 + 12 180.00 986.78 124.26 0.78 + 13 120.00 984.67 125.51 0.56 + 21 180.00 985.74 123.81 0.74 + 22 240.00 984.38 125.39 0.57 + 23 180.00 983.92 127.36 0.37 + 31 120.00 980.39 121.49 0.55 + 32 120.00 978.65 116.40 0.35 + 9 -1774.30 800.00 0.00 1.00 Reservoir + 2 454.30 986.75 59.25 0.77 Tank + + + Link Results at 11:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 10 1774.30 2.24 1.65 + 11 1114.24 2.32 2.38 + 12 175.73 0.72 0.40 + 21 138.68 0.57 0.26 + 22 124.27 0.35 0.09 + 31 41.38 0.47 0.33 + 110 -454.30 0.57 0.13 + 111 480.06 1.96 2.57 + 112 304.21 0.86 0.45 + 113 55.73 0.36 0.14 + 121 161.38 1.03 1.01 + 122 78.62 0.89 1.09 + 9 1774.30 0.00 -216.73 Pump + + + Node Results at 12:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Chlorine + Node gpm ft psi mg/L + -------------------------------------------------------- + 10 0.00 1018.99 133.89 1.00 + 11 150.00 1001.90 126.48 0.86 + 12 150.00 988.62 125.06 0.78 + 13 100.00 987.34 126.67 0.55 + 21 150.00 989.42 125.41 0.74 + 22 200.00 987.44 126.72 0.57 + 23 150.00 987.04 128.71 0.36 + 31 100.00 985.38 123.65 0.54 + 32 100.00 983.83 118.65 0.33 + 9 -1757.04 800.00 0.00 1.00 Reservoir + 2 657.04 988.57 60.04 0.75 Tank + + + Link Results at 12:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 10 1757.04 2.22 1.62 + 11 1148.60 2.39 2.51 + 12 134.16 0.55 0.24 + 21 169.69 0.69 0.37 + 22 115.84 0.33 0.08 + 31 38.75 0.44 0.29 + 110 -657.04 0.83 0.26 + 111 458.44 1.87 2.36 + 112 207.40 0.59 0.22 + 113 34.16 0.22 0.06 + 121 138.75 0.89 0.77 + 122 61.25 0.69 0.68 + 9 1757.04 0.00 -218.99 Pump + + + Node Results at 13:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Chlorine + Node gpm ft psi mg/L + -------------------------------------------------------- + 10 0.00 986.31 119.73 0.99 + 11 150.00 986.31 119.73 0.79 + 12 150.00 987.85 124.73 0.74 + 13 100.00 985.44 125.85 0.55 + 21 150.00 983.42 122.81 0.71 + 22 200.00 983.81 125.14 0.61 + 23 150.00 983.68 127.25 0.33 + 31 100.00 979.90 121.28 0.52 + 32 100.00 979.02 116.57 0.32 + 9 0.00 800.00 0.00 1.00 Reservoir + 2 -1100.00 987.99 59.79 0.74 Tank + + + Link Results at 13:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 10 -0.00 0.00 0.00 + 11 -358.36 0.75 0.29 + 12 188.57 0.77 0.46 + 21 -70.30 0.29 0.07 + 22 61.43 0.17 0.02 + 31 28.67 0.33 0.17 + 110 1100.00 1.39 0.68 + 111 208.36 0.85 0.55 + 112 403.07 1.14 0.77 + 113 88.57 0.57 0.33 + 121 128.67 0.82 0.67 + 122 71.33 0.81 0.91 + 9 0.00 0.00 0.00 Pump + + + Node Results at 14:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Chlorine + Node gpm ft psi mg/L + -------------------------------------------------------- + 10 0.00 982.48 118.06 0.97 + 11 120.00 982.48 118.06 0.68 + 12 120.00 983.49 122.84 0.72 + 13 80.00 981.90 124.31 0.55 + 21 120.00 980.56 121.57 0.62 + 22 160.00 980.82 123.84 0.61 + 23 120.00 980.73 125.98 0.32 + 31 80.00 978.23 120.56 0.51 + 32 80.00 977.65 115.97 0.30 + 9 0.00 800.00 0.00 1.00 Reservoir + 2 -880.00 983.58 57.88 0.72 Tank + + + Link Results at 14:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 10 -0.00 0.00 0.00 + 11 -286.69 0.60 0.19 + 12 150.85 0.62 0.30 + 21 -56.24 0.23 0.05 + 22 49.15 0.14 0.02 + 31 22.93 0.26 0.11 + 110 880.00 1.11 0.45 + 111 166.69 0.68 0.36 + 112 322.45 0.91 0.51 + 113 70.85 0.45 0.22 + 121 102.93 0.66 0.44 + 122 57.07 0.65 0.60 + 9 0.00 0.00 0.00 Pump + + + Node Results at 15:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Chlorine + Node gpm ft psi mg/L + -------------------------------------------------------- + 10 0.00 978.95 116.54 0.95 + 11 120.00 978.95 116.54 0.56 + 12 120.00 979.97 121.31 0.70 + 13 80.00 978.37 122.79 0.52 + 21 120.00 977.04 120.04 0.54 + 22 160.00 977.29 122.32 0.58 + 23 120.00 977.21 124.45 0.32 + 31 80.00 974.71 119.03 0.47 + 32 80.00 974.13 114.45 0.31 + 9 0.00 800.00 0.00 1.00 Reservoir + 2 -880.00 980.06 56.35 0.71 Tank + + + Link Results at 15:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 10 -0.00 0.00 0.00 + 11 -286.69 0.60 0.19 + 12 150.85 0.62 0.30 + 21 -56.24 0.23 0.05 + 22 49.15 0.14 0.02 + 31 22.93 0.26 0.11 + 110 880.00 1.11 0.45 + 111 166.69 0.68 0.36 + 112 322.45 0.91 0.51 + 113 70.85 0.45 0.22 + 121 102.93 0.66 0.44 + 122 57.07 0.65 0.60 + 9 0.00 0.00 0.00 Pump + + + Node Results at 16:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Chlorine + Node gpm ft psi mg/L + -------------------------------------------------------- + 10 0.00 975.88 115.21 0.93 + 11 90.00 975.88 115.21 0.55 + 12 90.00 976.48 119.80 0.69 + 13 60.00 975.55 121.56 0.49 + 21 90.00 974.76 119.05 0.46 + 22 120.00 974.91 121.29 0.56 + 23 90.00 974.86 123.43 0.31 + 31 60.00 973.39 118.46 0.41 + 32 60.00 973.05 113.98 0.31 + 9 0.00 800.00 0.00 1.00 Reservoir + 2 -660.00 976.53 54.83 0.69 Tank + + + Link Results at 16:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 10 -0.00 0.00 0.00 + 11 -215.02 0.45 0.11 + 12 113.14 0.46 0.18 + 21 -42.18 0.17 0.03 + 22 36.86 0.10 0.01 + 31 17.20 0.20 0.07 + 110 660.00 0.83 0.26 + 111 125.02 0.51 0.21 + 112 241.84 0.69 0.30 + 113 53.14 0.34 0.13 + 121 77.20 0.49 0.26 + 122 42.80 0.49 0.35 + 9 0.00 0.00 0.00 Pump + + + Node Results at 17:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Chlorine + Node gpm ft psi mg/L + -------------------------------------------------------- + 10 0.00 973.24 114.06 0.91 + 11 90.00 973.24 114.06 0.52 + 12 90.00 973.84 118.65 0.67 + 13 60.00 972.90 120.42 0.47 + 21 90.00 972.12 117.91 0.39 + 22 120.00 972.27 120.14 0.53 + 23 90.00 972.22 122.29 0.30 + 31 60.00 970.75 117.32 0.35 + 32 60.00 970.41 112.83 0.27 + 9 0.00 800.00 0.00 1.00 Reservoir + 2 -660.00 973.89 53.68 0.68 Tank + + + Link Results at 17:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 10 -0.00 0.00 0.00 + 11 -215.02 0.45 0.11 + 12 113.14 0.46 0.18 + 21 -42.18 0.17 0.03 + 22 36.86 0.10 0.01 + 31 17.20 0.20 0.07 + 110 660.00 0.83 0.26 + 111 125.02 0.51 0.21 + 112 241.84 0.69 0.30 + 113 53.14 0.34 0.13 + 121 77.20 0.49 0.26 + 122 42.80 0.49 0.35 + 9 0.00 0.00 0.00 Pump + + + Node Results at 18:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Chlorine + Node gpm ft psi mg/L + -------------------------------------------------------- + 10 0.00 970.94 113.07 0.89 + 11 60.00 970.94 113.07 0.50 + 12 60.00 971.22 117.52 0.66 + 13 40.00 970.78 119.50 0.45 + 21 60.00 970.41 117.17 0.34 + 22 80.00 970.48 119.37 0.51 + 23 60.00 970.46 121.52 0.28 + 31 40.00 969.77 116.89 0.30 + 32 40.00 969.60 112.49 0.25 + 9 0.00 800.00 0.00 1.00 Reservoir + 2 -440.00 971.25 52.54 0.66 Tank + + + Link Results at 18:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 10 -0.00 0.00 0.00 + 11 -143.35 0.30 0.05 + 12 75.43 0.31 0.08 + 21 -28.12 0.11 0.01 + 22 24.57 0.07 0.00 + 31 11.47 0.13 0.03 + 110 440.00 0.55 0.13 + 111 83.35 0.34 0.10 + 112 161.23 0.46 0.14 + 113 35.43 0.23 0.06 + 121 51.47 0.33 0.12 + 122 28.53 0.32 0.17 + 9 0.00 0.00 0.00 Pump + + + Node Results at 19:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Chlorine + Node gpm ft psi mg/L + -------------------------------------------------------- + 10 0.00 969.18 112.30 0.87 + 11 60.00 969.18 112.30 0.47 + 12 60.00 969.46 116.76 0.65 + 13 40.00 969.02 118.73 0.42 + 21 60.00 968.65 116.41 0.31 + 22 80.00 968.72 118.60 0.49 + 23 60.00 968.70 120.76 0.26 + 31 40.00 968.00 116.13 0.26 + 32 40.00 967.84 111.72 0.23 + 9 0.00 800.00 0.00 1.00 Reservoir + 2 -440.00 969.49 51.77 0.65 Tank + + + Link Results at 19:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 10 -0.00 0.00 0.00 + 11 -143.35 0.30 0.05 + 12 75.43 0.31 0.08 + 21 -28.12 0.11 0.01 + 22 24.57 0.07 0.00 + 31 11.47 0.13 0.03 + 110 440.00 0.55 0.13 + 111 83.35 0.34 0.10 + 112 161.23 0.46 0.14 + 113 35.43 0.23 0.06 + 121 51.47 0.33 0.12 + 122 28.53 0.32 0.17 + 9 0.00 0.00 0.00 Pump + + + Node Results at 20:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Chlorine + Node gpm ft psi mg/L + -------------------------------------------------------- + 10 0.00 967.07 111.39 0.85 + 11 90.00 967.07 111.39 0.45 + 12 90.00 967.67 115.98 0.63 + 13 60.00 966.74 117.74 0.40 + 21 90.00 965.95 115.24 0.29 + 22 120.00 966.10 117.47 0.46 + 23 90.00 966.05 119.61 0.25 + 31 60.00 964.58 114.64 0.23 + 32 60.00 964.24 110.16 0.21 + 9 0.00 800.00 0.00 1.00 Reservoir + 2 -660.00 967.72 51.01 0.64 Tank + + + Link Results at 20:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 10 -0.00 0.00 0.00 + 11 -215.02 0.45 0.11 + 12 113.14 0.46 0.18 + 21 -42.18 0.17 0.03 + 22 36.86 0.10 0.01 + 31 17.20 0.20 0.07 + 110 660.00 0.83 0.26 + 111 125.02 0.51 0.21 + 112 241.84 0.69 0.30 + 113 53.14 0.34 0.13 + 121 77.20 0.49 0.26 + 122 42.80 0.49 0.35 + 9 0.00 0.00 0.00 Pump + + + Node Results at 21:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Chlorine + Node gpm ft psi mg/L + -------------------------------------------------------- + 10 0.00 964.43 110.25 0.83 + 11 90.00 964.43 110.25 0.44 + 12 90.00 965.03 114.84 0.62 + 13 60.00 964.09 116.60 0.39 + 21 90.00 963.31 114.09 0.29 + 22 120.00 963.46 116.32 0.45 + 23 90.00 963.41 118.47 0.23 + 31 60.00 961.94 113.50 0.19 + 32 60.00 961.60 109.02 0.20 + 9 0.00 800.00 0.00 1.00 Reservoir + 2 -660.00 965.08 49.86 0.62 Tank + + + Link Results at 21:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 10 -0.00 0.00 0.00 + 11 -215.02 0.45 0.11 + 12 113.14 0.46 0.18 + 21 -42.18 0.17 0.03 + 22 36.86 0.10 0.01 + 31 17.20 0.20 0.07 + 110 660.00 0.83 0.26 + 111 125.02 0.51 0.21 + 112 241.84 0.69 0.30 + 113 53.14 0.34 0.13 + 121 77.20 0.49 0.26 + 122 42.80 0.49 0.35 + 9 0.00 0.00 0.00 Pump + + + Node Results at 22:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Chlorine + Node gpm ft psi mg/L + -------------------------------------------------------- + 10 0.00 961.33 108.90 0.82 + 11 120.00 961.33 108.90 0.43 + 12 120.00 962.35 113.67 0.61 + 13 80.00 960.76 115.15 0.38 + 21 120.00 959.42 112.41 0.29 + 22 160.00 959.67 114.68 0.46 + 23 120.00 959.59 116.81 0.22 + 31 80.00 957.09 111.40 0.17 + 32 80.00 956.51 106.81 0.19 + 9 0.00 800.00 0.00 1.00 Reservoir + 2 -880.00 962.44 48.72 0.61 Tank + + + Link Results at 22:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 10 -0.00 0.00 0.00 + 11 -286.69 0.60 0.19 + 12 150.85 0.62 0.30 + 21 -56.24 0.23 0.05 + 22 49.15 0.14 0.02 + 31 22.93 0.26 0.11 + 110 880.00 1.11 0.45 + 111 166.69 0.68 0.36 + 112 322.45 0.91 0.51 + 113 70.85 0.45 0.22 + 121 102.93 0.66 0.44 + 122 57.07 0.65 0.60 + 9 0.00 0.00 0.00 Pump + + + Node Results at 23:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Chlorine + Node gpm ft psi mg/L + -------------------------------------------------------- + 10 0.00 998.30 124.92 1.00 + 11 120.00 978.35 116.28 0.69 + 12 120.00 961.40 113.26 0.50 + 13 80.00 960.84 115.19 0.40 + 21 120.00 964.86 114.76 0.32 + 22 160.00 961.22 115.35 0.40 + 23 120.00 960.83 117.35 0.23 + 31 80.00 961.63 113.37 0.18 + 32 80.00 959.78 108.23 0.16 + 9 -1909.42 800.00 0.00 1.00 Reservoir + 2 1029.42 961.28 48.22 0.60 Tank + + + Link Results at 23:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 10 1909.42 2.41 1.89 + 11 1310.99 2.73 3.21 + 12 85.91 0.35 0.11 + 21 235.69 0.96 0.69 + 22 114.09 0.32 0.07 + 31 42.74 0.48 0.35 + 110 -1029.42 1.30 0.60 + 111 478.43 1.95 2.56 + 112 75.66 0.21 0.03 + 113 5.91 0.04 0.00 + 121 122.74 0.78 0.61 + 122 37.26 0.42 0.27 + 9 1909.42 0.00 -198.30 Pump + + + Node Results at 24:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Chlorine + Node gpm ft psi mg/L + -------------------------------------------------------- + 10 0.00 1000.72 125.97 1.00 + 11 150.00 981.10 117.47 0.87 + 12 150.00 965.48 115.03 0.64 + 13 100.00 964.30 116.69 0.38 + 21 150.00 967.12 115.74 0.60 + 22 200.00 964.53 116.79 0.26 + 23 150.00 964.09 118.76 0.25 + 31 100.00 962.94 113.93 0.19 + 32 100.00 961.19 108.84 0.15 + 9 -1892.24 800.00 0.00 1.00 Reservoir + 2 792.24 965.40 50.00 0.59 Tank + + + Link Results at 24:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 10 1892.24 2.39 1.86 + 11 1254.61 2.61 2.96 + 12 128.19 0.52 0.22 + 21 196.30 0.80 0.49 + 22 121.81 0.35 0.08 + 31 41.33 0.47 0.33 + 110 -792.24 1.00 0.37 + 111 487.64 1.99 2.65 + 112 184.18 0.52 0.18 + 113 28.19 0.18 0.04 + 121 141.33 0.90 0.79 + 122 58.67 0.67 0.63 + 9 1892.24 0.00 -200.72 Pump + + Analysis ended Fri Oct 13 13:50:29 2017 diff --git a/tests/epanet-nrtestsuite/benchmark/epanet-2012/Example_1/performance.json b/tests/epanet-nrtestsuite/benchmark/epanet-2012/Example_1/performance.json new file mode 100644 index 0000000..3fb10e8 --- /dev/null +++ b/tests/epanet-nrtestsuite/benchmark/epanet-2012/Example_1/performance.json @@ -0,0 +1,4 @@ +{ + "duration": 0.87, + "max_memory_MB": 0.0 +} \ No newline at end of file diff --git a/tests/epanet-nrtestsuite/benchmark/epanet-2012/Example_2/example2.out b/tests/epanet-nrtestsuite/benchmark/epanet-2012/Example_2/example2.out new file mode 100644 index 0000000000000000000000000000000000000000..3ff37448977ac5e3e006175b3a0bb0503b8e7b30 GIT binary patch literal 108236 zcmeEv2UJr_*LExvMUh?v6ct2JREi2Db0(RIB33M@D55B0Lq()0C;B)mS?h7n>^*04IC(Np_MADR>(H_;eMa$k zCOjUm6`&WO`Nit&)odGUJf4~=kLRP%hBw$*G-~j8;ahn$6wk^a5 zv=ATILVQpQal01c_ASJ%`u}kQs}?SxRSOr;s)Y+^)xrg|YT*J}wQvEgTDX8#EnGmW z7A~N53)j!Oh3jYC!u7Lm;rdy(aQ&=XxPI0xTp#Neu8(yK*T<%X>toZx^|5K;`q;E^ zeQa8|J~k~}ADb5VW!*x&g+K4_@uvlT{5}5s@%a4z{rJ^L=hb{} ztba{F^SQD2WnkyU&XJuP%l&?PZtOga0o?%XSaxnkO|}PEcK!a!V1G~TqK{%8Zz$pM z&H#Lxz{(8(W6I;*1ZxYnHNYM)wh2SQasV9wi?d@`*`mqE;$8sOhFNwz>x+SvnZLR3 z<}$0#2fxl>I43brk_G3M0tjlf#l67K>Sv%0^Yw!^ufXaw`BErPatwR`xewyJJ?x7#zc6=i1x{>zf_N`e)^aCN#aDyuDDw<1z4>&|LqmWwGDZo6F4K zjNkfLx!KQbbL_YFxA8wL^WJXnS)S+EO_bjyR>UkrdfHiGp5qqbR-XZKKdZ+K>qo%H z;t*>D_*vU#2)jlWL3|6;o#g$*l};5!zNlyI**Wv_yL5#5VxhU4OvC^&%)nxB9De>W zj1H_%r*AS+X~OVSYNwM*sd_3k?zo;_xs*gV0hWa((IsH*zDa5InM4}BFp;hiCsMIx zA~o)xNRurRX?sA2-idUaD3Olk66sXHz7Gj>zd<4$aX*2U+)1GQ0RsS- zvZhLn1ZH+3&peEYgO?RCC=Vbn_7U<;mP5k!2arOS@x=LK00|pmOh#^~6pb~vBBu;1 zMMb@1NNMOsat@GS5kt!J4k=x8Q!lUNL7)OZ7;Y86!y=O(IGZIA4{kPB`Gk4J4^EXh&5#S|ZTx0w? zyx&B~IK_B0wro{N&P^kEz0ha}W}K3#zQae2PUA~u{qd)_nz%53E>|zRi^t5kjZc3b zfi!zo;;RQukW1lZeByc$E)GA6Q|_+EQ+Z){!!cEyQ*Z@2JFnoXqGjmk&!K{m&-A#< zd-`#^^WUJsKAd3W%U9@8_&l5?^}|p5&&0l^0$f+thD%y%$c?f*gi6*$3C?ai2^cJ} zUU~<8{34Opx4X6zKs!P5tuL52aW~E!`k8aNwvXd&?t*TduEs8hu45OMYzK+!Dfqni zIxGx3hEJT@f_c#?m{&gr^LiOz-kcnc$D1YKU6CTE#8do*bp!1aO`Zy}RgMV)?n?y$ zGotNZmL~{Y+O)>J_zqY`K61RLyE&P~6ai22P5|SVz$wuaejP|0U`z)a4nCf|o@mm} z-o?d*mH$$F``7ak3SkWV3xnz1@1X-|28%V@-)r~3>4#7JFZ{Bb+X)FXHIb`{HA=oe z5M^nLQ7um$ov>3!%f)R_p|S=#11Q_4fnF`tLX}S1s3b=pxqBI*J9UGQV6!)Jojn?z zygd_5sYpeSf~KJ(86%M4{@KVf)*LLAcObr=yfX} z^h`??%?Xzx(XcfrYQ{X|GH|zm)puuQBQmT#$MiG5i-M@^;~daR1vzGWNIUN|YcJQ&vX4)N+Ifzk%dsK` zh+ziy@xgJ}D{vd#_bh|XOG=}DgVShL$297roJLE(ucuG*lIcl6f45}19c)!a5`DW; zN=Fz=X?lGky?i5)j=7acEh`i0M<`FJN~ErD5-Im8kq!ZzTLrc-ks2*Zq%I2*=}YfK z`VMdd;0YT136ALp?JNN=z{e2okw{-Zx=(~r2Z+b^8|3?t8)U0=Kd~~%AZKG1lkCo0 ziCXzcviM{o;W<#EJzbORRn#C?^1GA3JR9O~xQrZYJD7a9v7MY(T1Fc7P9VACmXRyD z-HGz1TG4$s8=~~8M%1laESc+{N&EoRIF>XxT9d%uo5=FfD6eC();VMuG^Ba&%v1EgoqThgT@B!mi_e@PvpOB<%@FYg!XDXx9s5S#E=aY+fOP90v6BupLjRjGlF0DL6T5v|#j0vAq_5 zgn&2H8uNw@pr3c4-roVV*v`e} zh8^SDTR?5wV0^ngM9!ssNx^tP-Ev$9++w`@0DQBm4aNCyLb39fP+Zc)xuf9!SH1h6 z*5kjjcCvH(BkgO}&1U;YZU47oZ1nx>zLB`~=&`XD@}8uJcs2&eH()r54EIG@&n_aR1UIA^ zHx~7swh*oNI)L`fUWk6m3{dM<2FS_M5G_^jiXxTWP;G)In%!j?GS}RT#%{_$55`8L zInQUJi^8qQvMvdY{+WZue>;bme!d*@1#(1J5wi?7HW^@-!Z@S?d8Dg|)rY=QB;aFh za5KQqV$A?ultO$d)D7f)jXi21@<9diKG`Rf=U5~3K@N}~tb`n6C5vGO7K7t(c-das z=lBNt$Sa-B&`YN(chhKSZ5lmtF_p$GOQDMaC8jBK0$8K8WV((d(OY#=nkJJ{$8}N~ zyIxA4#7SuhV0eO*X6}^I^6gT39?;W4N_YE7=}RLionk1ZKMka`E!gK^oxoQP#~gun zhX6mo$FNpaN{^3yNL1e+B%idZi1UF;a^&X$vU%kOLWcX1r^~jHQz6dem`M^TdpVd? ze$Xbj}e8?zr72_!7y$ydjQ6*tT(}U)-@ROE(hc8uY&Od{t7(4I24b15Q-~7XJ>p4#l=rT zaU7Hv0%Dei;(E_ed@nW>b4}yk$0i#CW8z6@^R21gJSHB2I@8a9*6qc6a&D0P6$K>Y zOD=ijV>hn$f0o)UIYOR&Bqa-??TBZ| z9FfAXjN)r+Du5RbB=doXXp!%MqFeo^i^AHRrDE4h)S>%H$~XeNB#di}Uyo1T01i0- zuL`hYVQ$Hng&pM_yJv3$JY6c+PqO3;9vj;ipK;T}-Nr5DoOeCMLC@~v`g2BTY2r=n zllom?ytN7&c%H|LN>1WKh1qyg_e5NLR~z>ox(UrGP2pN8nPeN3+a2tC;U;`9hC1+8R)C%4;Pm_qro#_no|z2u z#xKUar4)4VFz9D{fy>A9DAAyH04TpBL!oKtLb+H5+XNF1i%+u8hDgmY|{M z^s&ngB>}HE3G}mu-cW zU`bRX-*y8Jl!13MYJ+j?RX{}}=e|aV;As$>bUPHQ0e`+iIRJjsZQK@$p95EaK>Zci z#|uK`{A3(t{JZ}y6mNj`FCfmi*3e|F!Pm^a|FD1S=O1fyOS;QO-@opQ>1WSnn#gvk zHQJ*$5TWS=)ooHkznoP={0t>z8wz}xkJ$2-1rimiUHPMB4x@cTfXQZ}fAZo1| zfDC>(p`{}w=%~tJH0AIVG%asDYWU)VIxabgkm6#rAht7l`b8g!c?RfYtPzT>b4OYw zUg*(|U?h|6K%?U_Qs|toN%Tk(%;$P0QDes>I@>vkmJCgz835B^Np$IqB$_!r ziEab%-b-n;WfDDADWwB$NNLS=DZLFS2lN16CLEIh?cxB{;A5C|T1vZ0ACZMNhl#68 zH97FUigXqpBCX3e5~T|PWLQBCS^IM==`t&stXeRXWccfmY;|2Sw#ba+=Gl>R-9yO8 zcEiZjNx4M7V+dJzWD5B?IfQiIZ$^4Ad?2=ABb+n#}UD!&15j3Gn9E|gUEy3 zTS)bkVdR>kJ#hd(!|mr4A}_Bf%&>k)6kfeF96uTqi8u6*#2g8i>nF2;Fy0InhGV{V zINqrcj%_}L;nvP!SnX>t_KOR}9v?!n^UX&6eEkb(DPTy)_80e=v=;s#DPkYBr$N}`z0rYeHS`R_xe?vdd{2Tq`-Ts1wA$wqc^*^Vd zvXDmogu9M^PCw85*YuNd?d_~6oHjfPr+BTzdf!6vEt^Q};T?fDzJlLcz&Bsu!+udX zRx%F9d)32nyC%++D2C!Y^`SV~3%CWj>21)z&)^#s8-jm<-(>gUxVFHtx`Qj^{A3(t z{9A1mg3IoQ;$2N`_CmYwfC=z^XTbgh{H>oY>EfS_|8=#KotZ!AXXmAw=(Yc!*Uw_m z&$VkiqbZ=DUONI%3g~AK&`;xzhaq?U_xkzpUCa8p<#+V6ebocFC+R3+mch$w9oU(| zPrZ0>pVAS=>~%t$tj`di$=cv%fS<*h0Xb~6yp5Nas=TkNY(toD%$4`au7$bVY2na& z7Q*FPz9I&Q!CYi6d>42;=o_Efb%GYSW>Vj_8>rvm3_5g022Bjipfl#A)9$LNR2iU^ zzn<22Or__|*Hinm$@IH>GQHXd=5#wI)4lr1G*Ug8`T)9X0(wJ^YMxA!03G%v(bW1R zdNLu2E{X@e1(*Ul0UW^R567&8c3}V(W5ALm8s7B@xmt9DY`I@U)`ZrO9p?{|S;IGx z-6xk3lR-O3-3J%)<6a8c?K+%vzy{>PC4KV1--1-R^9fxjA%c<-#P^_#JeeUOZ5Fwc zvegojGtYt)NS}$M-yO((!OmuN&xYRbvin_%alKfxeDCUXB}JJhOuGd%y=6 z2iY9)R~X-zeiqaAV&#X|$;vOsh{mzq#NVcic!!RWm~m&iw!WD0=j~^0@ofiHak#9# z_?W|EdaU6xJ$>&e(cGI+^41iS%kz4PzLh;KuDo6eys#kS$!O93ZCi^L9vCS){qh=p zR8dXmO}#=HM}U`bFFsnt_~mU<4ji%oUgcuMKTvYLyp^0|mlvA=PiM;WPu;6$@UYjG zSeU4f9bA@kPfk6;*!&^(I<1CuC*8)Dc~1p5D{63X(j|Ou&ROhfy&c!KTaO(&=;C0{ zAE?4&1Gj492f@R3I|Y3s`f(zwFKEH|ov3Ssq97XXp-g>}hG+Y4#1UiGA*ZlH^lO+~%yyAh# z<@-HsuX+dOZ!H~o+1KIwVdt=L-vvA{J|F&`eVA7jgk8>##x56+!u`)}aR0Lg=5Ku* zoDv_}J6+XAx>Ln)@6$lQ-=}H6Pg7sO>opPb)#;cQ;f#5Lepr_KgwHEp0l8*=Q~s&k zlz&!&Z!_edtls!)<{Pswjz!=Cmk3CCxc5+%6pB?| zgyLS%rU&31U?FgDDRA!-l-ch%#?8O>z0berZ<~4aU$sm>cQ4RH+t*p6F%Jfy$rU0b zic~`lGBxCru7Z;6)zL3M_0YXsb(DBT1MP9wLY*36-5tzIQaT zP>Dt%^^;K_IuU*QyaXv096?4rKV;{sk3LrEq2tH((Uj)~D8JAHbv{2AjTDEV{!`AN zT(ynJ`OHzIXX$~u+HRKDoBtj*fBRyL9?a85ikM~a@+uU*7?TQ`+FR~t^@vzg0zMXp zSR=sC+BO5mt%DFxg*qqiC);O?=w8r6)?R-9GtW^0>YkvdTR>m8uoz}w=K{xJMG{NL zhh@@^eKyc;H#4YOP6qwFIfIs@r&HUBpr3#XimCM8gj6~zY&}gIoI>56Cev%jlc~Yh zWZHgbGS$dVrdzX;X))M0`N@=jB$*BdOq`WW^W(wqoJ<2pCDZzm$@B~08pG(uV|qb5 zQ@|VWy#=HXfx71t64>@AdDWwq*qy2&UYbY9{;W;pb7>ICJ+y<+))UFx>FbHo>Jg;x zYeVui*MO+a?L*w>Aac|zjGW3JMGW`uBC%7$h=TicGA1;PTz2V0cKW{-^)*H0`Qulj zf-Z^Vif=Z#3CJ``B!k2PvQc3x@%lN6xaT5r5&V|`3Bzx~b?X7T&;y^}+8YmFwE`Em zwU%Q=W{6xrnbm@E-P`(AVf?p*W>16i_!0VC zXD{)H*Au-T*NyC5eMK~)v$}Bd$RfrOk`%W_soiGV8$tS|Fd$@X&fQwk2kc@#=c|$XPbQw8wK6L zyOTud=l&`@ew_hw&8ff|n@Vy2L&vb}PC7m>j>L|)I^nZU%h0`xD>>QX^=Mo1Resyy zhMd_eQ%*GJKDtC?ywSTtyzwrm%{Hg;M#ED-A!n#-ve?`$bW00 z%runcuYc?3pUpFWuN`B}U-!l4Z%MKy>SSk)Quhu-WA;$g|Ct&pH&REhOxvRHyXq(f z%Ev=AP?eDu%73PXS`W}iH?s}Vo9sbI=~F0Lz#oOICd`K4m^|LD3+||mXcW>anv0G) z%8<>Sxrh$yf|QpSp>+*i(DsYnQJWDH(Vo83QNE5Js(!Qqow%Khj!{K(qN_0!y~6V&~ML0Wkt28dw>_IJT?xcf#i7LnVd2cmw(6!(=-0ZZfsFl1yVSCDWZ3 zlj$SC;Cso`@^&({1H{*X9)k6#O{Qg4&=z0~Py&ptff%&cg}4@=7x);)Lffsrw@Jj9 zeMGC^8d;ZejhuV7m*_;NlfFWK!vC<1*!CGqUS}qeku^g}y{RGTd(wd1de?_2+jC@9 zZWsx19!+*N>>_D7VPx9l>7=4Gj9k6hhYZ7(7qab8ubpxd}>m_hKd7kYOd)PiF7IcoV_>PSLr@VH?AMS?Wc{l>Me;$g1HNx>gaRgq}te<;B@X4Hc_z~DQis5)X zj8jZM*TWcfXLAJZd^Qw|VJzDPb$!Sqa>0)7EWyAZ%D2p;Yj4MqHk~FBp?*J-TVF^6 z3y*=$#fc`_@1=}CDRevKw@RSr%A)8I=TQJv+BVadDA-$(D6~oBvA8pN-P>8TXr?dY z2pQQvS#*gugfYOg^W{-N6Oz-N(N^+$mik?7tay>)?Cb(yC}(07nLZTLWh=Fa~GrEBf|iZp!0!x-l z#ga^ISbsZ*<84(2Z5t`z&9K6TFM5N1<~Z;k>cRKpsDsm`_IQu}4UVT*!0|530G)k^ zWRuM~7nc*rMSGWEVO@8@kp(gbo@6v=V=>(OOqH*{gcEAS+k?~Q{p)6T7czxDI?bn(yn z+35S%eK|pH>37=*IocyMZT?_%Jaz)|K5l?^8yh0M^R19^su2>s)kKG`bU_aWbVtEI zdmx2%7N}^jEqbCt&;h66$f?#7sji!hVnr*Evh6}tsxkt#cbJWEbrwnro{jvTP^8@f zqt5#rk&SFAdU1L@nqlsa`rq+EYrdwV_OqoZnHPnW)WVS0lg7Hu@XS}F z4A#!-z}PH(weZ(-TTV!5By3+5L5>OA3MG*H9*08)1))Ag|Cb`_x4 zo@5#XcEjFensO9!$77It79~^LvSeC*B$<{1769f03=2U&Pr#a8K!?g?>USoY4!Q;L zo5>UdM1TQ+M}S$-z9+OZ1#nPjnEe29Rjq3Bt!NjCn|qnO1@tK1MY`&)Cwtc}CibxY zR`y^N=>h9+2XI4(hk^k~S2G}AeEJZJ>xgiYFtWVWXwv=qF0vK!&JgeEWYg*}GIm}c z(r#tFXzF7`jvcHQozjvL|G8VqQot-I+goxZ#e6I2tu>lh6d|$%{0v!Z?!wxv0~`Yz z&&D~u;gq&laedpg#iuP4<@%{URT1xnacS}?Mf~tqKb$YM!Vfd8aGPQ)99=R1(?y{; zPYLdof_`e$g<#Jn{rt)e!?mi`at&pAxg9i;7r7jp!5EctI|P548G2dl^T_Sw%z9q(^~8A+?=F!^#Gh zC3^NN^Xn=oV;liqiWt`zzozbx0*82vXSm;^cExu+%H;Jy>p#Oa>Wv>)kpVleKNIk( zKMIz@9PBj5XM$(XrD(Fpb~N^Dji9^lEA+v9k0AJqIyZZ#3pe_rA2(uZ8t0{Oj=Lgl z%~^$ea%1A|^25SbqVJiNf<@0dq4`w>$l_fH+7^9|AELAZoy%{FQPB_1F;f{=bu_~9 z-C|JP>>SiHPly5s*YhV?j}=U^zRJHo#zQc3b5|&j6`_;{L5d@j$JSSgl(ao(soSbqT`_h66Ax z%!0K<6LEe1GORE=2pdia!;{rl;+XTHSPOW^a@CPTBzVWBP}~K|rGO!TzxDIa>g4aW zW25h1`;}iCjow^@wRk=;sAtpt1qeQQKW1C`~U5$wq{t>H95__)1^& z?Li;ZYnC~({yq;`-&%t1&WJ=E=fxrorEEDK2y@Zg4LebMej*C$=fq{K@%u~Zr#*~~ z`3^A83J3-q1Y8D0@ZsJh0{sJMammyRY&2v(VUEePXc**>qu`$9Sh)WQ^R0ORSHJ{- zGR(p1j{qG7eD+MH(uL14U0Txha80QP;;ao*{-()0y z+a>ZI&=bnt$EA=XZG4DeLN?hqWhCj@Dv{V978B1E`ee&0ee(0E1+f|-Amq4&gfD}* z+AflFOhO_bx|6Jv65@K_f^=H)TIA_2AZNC|7M*iWBulqtlVHGXC=0g%d*$Q`EZ;MSQL}25^aBr$%8Fp{dPc6{GdT7Hmk?AGVPXXxcGw6%e&)Pt) z06udVvur>=4Z*Uuv3)`g{rqNJau@Vd2lO)?^ivD;^L|jHe%=pi)K4Dh=bO&b(nQeD z7|_olpr1*gpQk`SYZk4>E})-c(9hU^qo4MmpJAY%GeJKYM}AK~4?X->{cK3Kvl?>k znScZRbO8MngMO|C{cM=tsGp^a8})NI=%*d%XBW`VL7<<`pr5s%pJt$+28nn1F`%F3 zn<@q9-{@x{=%*>@Cm;0l{2%CN$3M`|m*s_^oeck8KY5Zy{ggfbWBqhWa8hlS(zGyVbo*3aM5#Xsx+ zYilPvy9qa=(R96asLT6xNDTVfw|owg>Ne_UO_P46e;R`>f_`4BCaBL`0g{4#E(QI( z3i@fas1QBSSb%;gN1>r7*8Yor=5Mn^UZ9`$-})f(2l{#RUaVX{nV$Zme)bkYZrGGx z_J*9YF@G%kD3tqIyaaZf_1fEZvZ^_u=WR@Rq+61iIZqQuy>r2 zsLU&gy89;4fgwq>b!-wnJTr;z0jz*^w|;;PK}nPgOrpa8iWN!p!=@zq^f{~>O=}h#*@4mBBJ1{L!{|CWRqSm@^rI3$)6QMwtEdD_K~^7%_D>usko5^J4483 zUN16t@pDnqeS1=C@1JKtm%LDK^&`(3qO*ZG&59}MT?3fcv+aBh9 z-wgVxBhopjU6Q_GGwA2=()&SPrHoVegR&w2tQ7KiXF)$T%M#c2DvRkmu55_zTF}p~ z47wrK=TrLIu?9Vs?`R4}6KR-kMnJ%&~I}G}{2lVqe=w~(Pr;sqNF@87X57ndUr?0zWZp$>Hci(vls9jq-ffM-O#IoYt2 zFn7BFxy*8de6qmaDRHu(?yxBw|Cpb>wnFf-d>gDElG>XEWD8spdcgW#UF;Iu2lKq9 z!SgHr0^VjN=x;C_a}o4126UTg=Vq||0r9`i-?IF3dGc{?Ms^AJa_oD~O}!hQfAA~U zv!f#JHe)RQ{8x7H2PW~Jm_Sp@`$A{rb znTxPwHZQy`uK>)0mj2*et|Bh@DEF-}eJTaJ$vFUJGMhv7#C60B4KYkuK9a@@l3 z7+VR>gK`#V=S#4E>*t@<$=_?&$iTnyzwN7ikoF`A;#~?wW`mZXp_yaQ6@yXe)8N)< zss}|0`5LI(F@!Xx*r4Yp`=R}9Es&;N543c~C^WP!hlX#-K^LaFppkEV(Sn9$NK-Zp zMQ@oU&p-FyorMC54N?9fBeY+`5V=0qL+2-rL$gBMkok&5XteGo)Gue9JpW|+sS=dY zlz*N@8EZbs@2PI}*$DTX+QBn*8)1FtM!0_|25SU+AncR-S>2$E4B|$t4Ls-B2=J5N zKveqCLEh%#g>rd6_kyZLpL%)A%k1-sD_srsm#|LP4AxJY5s1MWUvD9cLEpmQ@FC@A z4kl0rz5NL^7VObO2{iEp+&8;Y!66t5JMC!XXkt*gV(o6P< zv;d$oFp<6)0Q)CM66sfe*q0~(_9$8-r8C~c9tyQm8d@QxD=x!xqk#5+e0Y9kAGF^O zZF2xi!M6;c+ck*_22_xeyW5C%)EP2y%^Bixe;X+p98XmGd6K$$8_CUd5vkOQAL>SSD9GKXR>=|Z5c0LLq^Bxm&t~iHq)d6Hce-|P;97xW0>_$GN-4lJ)vL%J3 z4@8UB#F2M(n@RVZn~7yq98oqOMD}QBl3|A_>EU5boo3QLS(&R>U(e zb;7(S!MMS@Kb{BU%CdOKJ%96?o{dXPKj&yk@R7ZCa{at!WQVOrMc`6cSIOedsJyvc zo_`8!La;ORRSsjJd0DtzKW9SS4D@p|Sk~VA>`2?{lbWR}25UrG^H!I*Ezm42{a8@y z7I&?bacXKOoifHBp2V&!S}D3L<4}5;w7R5BI_YOw<-tVqWGatxqn8Ws4ay;(vXn^m zo-K?ci@5Qet z9+20&h#$Z@Wh0@TuXcaGEtRNp=RLH0)m`+~xDx4}isquS(>Pz9GwA;N8t&1V&8X;` z0`A#WjJF<~j)$I#!8aG>F0%+HypE_{AGD0A04{LoS+Hh}j43g!pL@qaAemDLrd`DCHbpdvqcX0tHBX2q0 z?19+j)p}SzEfs=_~M?$5XIx8wuXRx&6w!X8xUnW5QJ>czypMyi6d$XWB{dB;eb~ec||l z59l(K!$CjO!TznEzo(0T*3U-YzxGS@{uHGAdM?6B^U!%qH}vWRM*c1W^m^?pLC!^M zr0m)jl?GU%x3A&e=T2iZYK8%->!XeK?-ihxyDd>$8jHO3DKa`V4XqmDiH;1nM;WKb zp!Oq@&>-Ge_&KIG={Z44RbTWEcmxyL7Mk3NB0gVm|M@h5R z$m^R0$w&k~cIDWf34ewC6Q;&&D3huuQU*Q1DnKE?L^F{Z=qA$dI*F98mq_n}HH5wR z-ol=H?pBHP-k?OQZR`(+1uHupfL3PeK!^T4e=E4$C3OEzXjV-e<{OD0ixjCo}bCk)?Mx zl2N{dj942(&Q{oyJ_l7vz&TZ-IMA33*ltCFb_I}t-Gr>$nM3q)14xg)exnAng=0oZA+j?c!-JQz5&wL{K5-^NZ z{BjCgYPTW7U%QC*xBbcXKlq3J6Wm8clnr>bqHLSxsxror-`hXodzNGIKkuI~R50A? z6ER2QK3Sn*8dfOtRxfnGv=ZrzdxzNm37hZr=GguT?&HUCqTF=uqwPNKmhx@R9BJSf z6~yhHyNzpz>n_+mvk(n)GeFL+&gh@_PuOWD#0|6U@Pu=N@r>y+e|P@`tIpYif8IZ# zapbCU(BdP$EY|_FtJ*|UnBkp;p0f7w5w;sERm2z&kQJ;Cu}KEgd4*gs)<^1tq%z!Spz z6+<4X_v`)%Oh1DQ-plW~KmVzQw?9?E{o!7_S$!8wT>IcnY7+cmjRY5Kz}zaV1^O+I zT7#CdIgMy{4;%%YbF`X)KW0q9uYh-2f*Dva9@g#N@%V>h*zxTEi-3d9&|exnU!`Up zf}??VUiIO)2i$Mi0_9<#pSpkRC;9K~oxn8qum3vlu>zTeL?NrzQD|;SD4LWr1=*NQ zlJB1|@5o5`{t4H93_*t71t?;&9m<$#jnZs-BWc_uGX)0?0n(){!e~U!l z&m(RY6_86_A<6qF1lDxKpA{}e~Y4Ob%tQ#z3tp)pY%Z$-u* z4Ir&|5~8;whfD_j9Bx0JEWH&#*4r8r2lrC1Jf8R=xiggTG76tp;0w!C^ zll1Q!$@trZIIp%Mf#7HG+gL8w&tjOf{jm5l7q`wCOZzqI=PH;p>ppcjUJ9DW^z$%` zQNJC_SosKyXL&G=B{k{iHmLtQ{k-0!pJ$+t`7l;>f@6{AB)NVLfx0&6=WDR6PvDQ( zVbmkqR{sF;n~+9&WUe7qJz9$wf9HuAcWe**LK%PJ$$NT-JfMdj*U^gohX9#0s9Q3* z_IMheqpnJ>y^a&T(XA+a)NdQ`qCdI9O%N@!&MjJ+HD2`HF_pSb+(@6MB~iu^;3cdD zUkiI!brUrdp92o{2VU*PdNU;@Stg2dj#Yj$2cFKy4-cQk*-<%o$yg2S6WhSKyEt)X z3McS7*JHR~hc>dEa0n|#y%oIPu@}d;--bo|H{s{+SK|AjURbvKJ;#4_m_NM79Bxm? zG3ZP6dIzPCid?WGkK5304|>o+MNn{bH#%)O728I8;=mgd@r{#wY(KvkjSYQ>jyOdi zlN?jQJm+|Tnjq#(1sc(RioC9VZw=pWelh<=3_Q>IZ7DY6@;H~>;qd(DTY+TmMr>G< z4&Mt~2VPkM*r5(i_t)U)`+nGEwio7I=!SWBzj8d`QqV$)or`}k(@^1;C<+C8mzbF|5%Dez_+^z;W!oa)C@{X75svw7z4wPU02U-vcNMH6L9b&;U|AQbA&N5!j#qP!9aa@*73ONA zv@8QOQ>23ePU)iR2t73Nt|5A$W{g}13eYLrU1+ezSk%it5bd}ZDNHGziG0H+ARS*{ zl>SzR_U-jWRw~`l1EDc;%;<*7+nJ)DZ>J;IOLLHmatMm#r9l4~DE8GZR6b-rQhJpv zulJdK16k~E#igfNmcz3o@@F)eWk^qR1>0AsQdK7Rv$}%q33%qDFMI9;Hv{~zSNyLK zjy*a`-X=Y*M&6Hw+Z%YMC0t%+pAbBc2ldv%(vNm9*K7CdUUV!5ed9%buc%_mMp~tw zL1z}FQ`Jz|e`skstN}`+4?$0_13HgRp{KxpktNd_)g+o0FQr$;N$H;6QffI^O4ay{ zn2MzI+*m2SJ_hy@0!+#Uooy?n?^i%R3;3`c=6(Us0GpuR1CF}{?JfdXjKOmz?5TU< z5t(3cn9LhkO`e^qBBRs~kzMW^Nw>EC#HVr_>8v`2xP~Q>u6>7)G?6y>yjGKlR85FU zwJiyX2qKGT3?WvLJIJiLK_su;B;v9+h%8hxAv>e%M8#ul$^CJ)qG&-Zu};V&g8<#2 zd~&%pxmvr4IPM!l2D=TA$5_04$Qk%O)0bmlc|n-n0WM>~SkCs$M6Sz=axU?(6|VJo z&N2PW8m)lM&UM6Vx?ud4cw;}<1K}m)3Y*|Jy-P>X!w*(?I+P!>aSg_)gB!t@$r@h@*Wi+2pt6X#C*NoCVsQ&Ch28Qj{Ww9Tpsq_xG;B74KR zB~}91M?S7Kc{jO>C^|K`sE>t_Xo24k`bMvn*zovQ$~XeNguVMli5S0PZYINihOHUT z@DJsRlDQ*J$?F2AuN>plMw4$?HsLC^<=fydyL#Z{l+|2T={uYpRgY~2w*(ipUSYxM zeS({tp5b}z?qOA(Te!7+Vo?5gZyZk`L|p{ zVQ#`xMLgh=3zuP+#myRFff?`e^kDDJ=O4Lsii)^;r4H`+!4ZGCw*c1D2H+^*&J*BN zq-PNBIU@v*d%6IZxrbtP#<@|;UYu#`C8f{FE5O$Tsxuu{+*D( zLKA7%c0vWKx}q@_YH0swH6(UbM^Z~|wEU0`iqao|40nt~z8Qn%^Q=bKLeb~T6H!+7 zG&C_N1o>7}plzlhC|jhB{9kFIHWF=g;FJz>+BX*++Ubj27eyf1tBWX2oGHia_ft^8 zj;)C3<;nYFSB&W=+lTv8ug&uPxLNt)g@;0}{SnxwcBI_T>K*Hc5b&`!5NibZS=(m7 zehW1aKLT}6d0*Oj!SIY;J9(MC*QZ{_P+tvsZ7HlPVbAZigL&h2LKcJLaOZ1t=+@V; z_t3m_+D0>-9)-PcH{3|0S;eVzhR4E1YafXzpOq0@DcPV`c*b9(&NGS)$1MT)#2pVN^X9n{q_ms_x|!UW4XG%j!&HjPpF!+xEhVw4}s-J9JJ3PNH7hp4#OC03F zy;E7j{iucUt7@fOKR+(=DF^tXMu7~kU4aO*QXse`VE!WSRFn)Ca{mcb_#^?~aekv?d z6;D}mpBT+8AeGVCq^gavm~-eNW}LczPFKwMlYUl1yn3>tcu*EkTpfRvR$a-X$~uRL z>+u_+h|PLrn4UT@jQc1YIjoRzgscq95mgRuTij8!O%!XMO1~>^rhJ%FXB+`u62>*g zFDt1LaA>uhN1b&eOPs=-<@KJ15twm`=_mj0CG6VT9@h`o!_tTaoafx9xa7(sJZjS% zq`vefE_mgDbZu_n2R+KL>Wu=t_c`pl@N@-sey52QHudFtc`oCQow$lz$L|+R4>aI@ znd!*gR95ERb(=PEtrFk?;nqEYnkJ(YqYuc$yS{HriYZ2w!>KjF$_Y}n~OJpXygK{Bp_>8C?o zK{ae>)uXbJ`gV)bP4LGU+@JjXOk@ax0*?jU;;SdJ=sA=-54pwq2P-XT5_x;p?R|8L%D@1Ly|6XK+j% zw5tUqfser#_Q~y<_LP`DJxV&Xtt0l*S~BU@5puWZX3{Y%h`dPJLB2;#Ahmcs@yQ%c zZpL>ei!SSt++pSrkRO*qlr~_((K3#GZVa z`%pBxG>){>-9kD7zLmiGp;d#(w&hvm{p;c6U770Wfw+ z)(6Y=Q(Z+}e0X0i5o|n5cGVUU?Mr6j`|Zudj8o<#OvJXppLFx?;&XL|;-2$6i~So~ ziJu?*K%Z!pkn!3MrSbKyWNk!r@$sOQC66j!123M7u69%vq5U33-rqWi#JslR!@Cv4 z?@zZDGmZc+VNIGnXgD@=viH?4!V!Wx)S?=Ccjf8)af%Q9S7K3_k3o0CTwZP0xQyn1;gt-J;!; ze@cFReuZ%@x2pp8evSgSQ@K00ZbV=14y-Tkk#5ZS_1DIXZ${2;*vZt9^V;IbNv@CO z&J1$n7RGvWgImw%;`I9Aj8&_-uMV#G#LHlO-p&kH=I!D9fm=#rmcv|@1lPv+$@!@i zyO_JsFpI0-yp2Z0&IFkWS{IK&zOet3rESHNC;$042xz9I5{7Nf~9cl(;P7cz~6 zdu32x2=}XYfG+P~F_>d}Eq?|Tj^lEYwZyhxH&cbcOsa2|Nek|8q`m7l(yrMX=;tSC z^aDd!8hr~kwjz~2Bp$>T175=Xl@ULpZ4q-BNgnbsS z!80jYfc1b>KoXz`U}=;>qo6GVi!mhEB+=7(FUW=O#|SmLP0Dg^k=<_!NLsIKqOB(( zYrSP8(Zr2J>86wQI%CMZz^>$am=TG+X-Ql|IU=CpWY08b(mP@|IcytF4%B*(kD_q$ zzSfdV416VuJB7%JvKONGv;-3TI*Y6TEQNBy06zIqvz0uJa3(MMVR?+jOD5L*s-HpT z3fx1H0=N7_d#(^*>#ic#Psbn?xqeD>Rk$F{j@;Kx9l2N|HSR*>Y%Vlz2-ki5YOXV^ z^)RgMj6VjQ=E9)N#xUd5aIOHxFXItW+`Yj8a{cTG_qLdR_N%^>FpgbW z9fqgD*fmY;FW1k{Z8XFak5m!T?lO^Ro+EkNEyO`$3o+xA+nrvpf8Qybe%4H!pld2N zuI(Y7(xroV3a2d2-+Y|pon2kJ?cyw=Y#LjVRajW^bL2<32e44|)gxZ$J?l}S{)jl? zHh(?wgVFlpSCzVA#u4Bp?D2OTev3^OF3whl^Ir&f&80qhT4Hn2R$lLFG>c=LV*1%m z>pJG6{`jJ)Id(5jL3c6hs76P!Sanvxo`KSyi0cBqJ(fL_`z;14h6ssHg~vM-O{K=#wY1*|Vflf8)opHuG;%?R~dsXwVI6S6NNHGn+u! z7d>b*;*hMJ^;Bv4noLg$2^(}9yQ}uFD(a`bxX#JfQ2AI%aWB+QeqH>d zj!nPb*9yBTlJ}rUP94`F$;h{FcFoBXtrnE?uJE!WUAe9~(a2IK_l^FGb3ux|NwmiY z65833w7%v-2R>d-t6400-qD>rR9!$1qkgJ%#WL7O7Jqz!#f4}daH0>)bfh#9pAjE2sE7rSwNBxo6A z)om7iu&))iLZ4Au@U5Q~bavN*pj%pS_eLNXrw75bgv}6SWK3K~#)@P2s7wG&^=4$N z&jYc}zcNopUo7Q0I>+Sq7ISs->-e~(Vh+XXv^!!uufJE~{xUm0M$FmCOKivW&)O1$PjYfOF?&nb%b=vcH<$MQbP!Ta#pv^YV#EUyP@Sleh} zly0<;trIOALIi0>3rdR7!s}+y!dHc8;SQFknnnv<5uFgO2sdo+@i9tx^a0~4AT}ae zG>H~2eUB2ZA?Bdp7w{RwHKGM8^=Lr>p@e9}7=X92T#Dn*;<(d@rda2g*b<-l`Z}At zVjsJ1c#)m$c#)m+*~bz}W7vqih3xzO9qg#;D0XjB1XG%9$7VEZ&m0h4C-q`!hX{*% z6~IQ_cVN%0_p#M41K8%V(^$u^0c_IfUd-tEYk8|97^ko48~I+32u6-O1!8SO+J#&!;A>D}M;Q_E11R2nOilbsbw(I7?A%3fL2&t6r^qJG{ z)yU-sYGhWA=HzZ)19GyDIICsrz=Z!hSF0wO~Nlc|fto(RD6M3}LGxVEAu?4W1 zaZWuQ?Z!BNs?!Fte5*)#&dw662%}3S#zWSWPStLdPa2`fMo)icb=>5v{LbOF@(;Oh zI7e9MjsfyM!?zZ_x9uGisl}O6w?hw0IY*F}c(!V;xHjNkpMf0G#e1UY_$zaZ zPx{;!>p2E3d0tT3V@M3?G|-(mZ2kbx5*@*Aa;9|Kk_p7!Xavzb`BfG**@$R8EtkDX zZAP9tj)#`1Au^MDG2kQxQf^^SHeQ@aetU&SbagQ1$hn)ct{v=v=l3j`840gWKaqAB z5&?aSlE^FjFfz4rCmA#28o_*@aPvqBpwAXm7pTkbW-ms(m6DOAvL#n7#Cqz=*-{)Q z&A7K3xG(jfb~5@-*Py2P56*~J#n?h0ao>-kr6dL8!{dIQa3dGlXqT?ok873wJl1VB z#vY>RgPn@;ho<5>W(400gnH;HZAjIVC0~Cdb?f>Lb?~J$!*Uajn~US(@Z8E={-e_a^8_O z4)pCuWm5h~l_W3uFU~zvRwo1RY$KD*G)PyE`Se5HGAKRE|E|9l=!CbZ%r35Z`nX;XjtPREx{K;AX(Nhg=lS26+f8t`+-j(_XtzrB9? z70Mv&qBT5rn+X#Rt^vp8TIB68TWEM>2c?zGAzZ-$5+1Yz<(^J(xnvQjMr?$-z?mRF zG8B&N9S5y+#=@@jKzMS|6S^oahuHq>pv-nM5b06~aXJk*l$V147AZ7)3$U$)CAis} zL-(bayUf!YrW_4`i)#|$%+y4f6!8)^whjfju~V$~SfE4JdG{A>za8@d=bv=+zk-O0 zil=yX?l?VS;3T&5`q_sg&}Vj>_ha7=V7t{HKua6%VE=J!n&El|%{Gi!nAJ;w<7MLV|5;alAK< zn~UIm9NjS{;GOo@nfsYc1`8|M6vP6&PBe&N>d_0>l8!rA!PAj!(YbK;P_Sj&KIk*o zFZ#?FIi@EYdH1FKSvIiRS1;us9)z=6 ziyiC+q8hI^Q~--M+QIIA8OiSK!ZFy+(K+KbzOU$s{@W0o&wAo~HUi5YXd7fLQ4;kt z>5LL_Znv6t+@MVA@>R);IyDm7svWuFsZUB1o0FA$!pS_GllpXDL_Yks@OS%@Yhj^! zFHsZ6ElnqX)X$ijg=9%de^EaN;ygBI-dZaAsho@Z4u-&z`7*iT zT6m6eY!?pR4t1ZOz?iPa=15>4&jy8wgG2vufRtS+lh$b_A_mPm3`Ve()5TsOIkqw1F=@`>T2v2F;;}6pF zBMyl5ma}eK;yBbn{t3Z#$|jahPG3>Gwj$10YS(_ox*}ft@G%y3x%;IXUiXtZUBx}J zu31=Wp&tH*XI@fK2l*#O%>Rio6fDz%^)dCz8%x|jGzj(6(#Z#HTCTHEt3Yx9_ZvyF zQ77&3xzRuEPi}9faGMi<-kwtVyNBt}|LC!5uB8M6!^jo14xxL$( z2Cq;iX4$HwlcyTFqNPR5-sqD-QO$|X@_FP#wK`dJwimU`SxIw)29jpN0J3ttC4GeY zdH<#*^;t2OCciEvoP!)zdz#QO>!uSGqwcgfo)KN%#+RBS?;eMQl8`LSU4R;T1@+VN zZ~gqY*H6xDDB27+FD8KzO@xKZ!=bS0N;rFI3FJAogUg%egG!?wjFr!T4&Nq#lBE+g zGams*&f7rOiOXQq3wP009N63+gsVHiA~^yUwDf@*g-tM|=Rq(SzX{eI0LV`#pm#zB zA1ek!M(#>@ZW9P*?V{jO&2JcZHw6yw2qYd&3P5@K5wXtyUB5mU`v>Q&)_CUR|KIqx zJT{vF#@yq$gK_lqu@6DK&_tqoRazET4{!66z;L2}V=Uwsb|k#Q1zVSl31r zPKXkYBIZnu5>~mOJ&JJlL#^~eyL1)yuf+Jeh;T$8Vj#BV;&?wC=Zgr%Hjeytc;EbL zcDG$7TX^yUyNXb5pUIjnie}4%`OLsKjrk;uU@D!$*_TFZ*7dj^n{h^um74ZoZ-ZqR z55b?=9vQ)u`)0BYb^gr8XewLx(4V#M+=E5GelG8tEMpEvFXS2X!`Y;hX>1B&2wp2Z zl(FT3X{@n)1bgc%V-v8QW8kNoqJGNJ_M~WcenY!+{SZZBg4&rfP??M!+lICsphBWP zEvG6sRms7fYNW@gc0{Aik9nzKOLO8=oAOWf^Vj^9&}HJE^fP<&NMby~P1MgWIFHRn z{p|ZwKks`!&z4~P+ke&gef_)a>udLK8ov+MM}Nk@b^25M+dc)4P(OdfzkPZlTlznV z-^cZHHS%p8>Zca^JG4 zn+CVnAO&v+^1MugK6-)iI9$w#=@3^UJ>Hd$c3eT{dYFou`HOqJ3~APb&TKN3 zxYoWWDX5>BZGCBHwmFOQ+yIp;vson1AJnfdc&q`H@0?P^4fQ87UMbM+(m} zA_bidkwOc^*T6`j5wSJ_^LNHZ3Y!s07_;x{u}C53JKp~_Qi%S7ZHUE)tZ%r-2*=OG zabAc>Y~$F0IYh@@yUOYU_Oeyo&$EIa=b6)*y-ejsBzs^qm&Mj^XVQS7Y-HG0_Pk!s z0-v;Ihc(+Uryx_Nqi)GY-&w_kyF*!V>0UPP#wyl*r87I$el>fx$doneJdy9WwPcz( zPvn=v!dT@)%>Rjq-WtYUc9$}pwyA7chhfb7j0G#lb`I;uwW5A{nJS`>P?0#w6v^~J zMKV4{i5#<3A>+rJ(J}p1NwvE_^|4VSD{{SPt-d$)`1xMX^)s$zJM!tk4AK5fjp+F& z`}5ZvW)ZeP)X%Tq?h?NK$=lzL{Gp%9IFB{Oc(@&Y>gVM#&2rYe#*{cO)sX9)P%pWs z7*o zQJe1U#;Z+okL`SK*#c&i8!k_5+fZ~mAxz#_lTmi}$&NDf2M5YHN065oOR@-KV@;8# zRIA}W@dXGxkFqDA_*mW`v2Il9iGGglRMo7wZ_LffP}|)D(wokNmlN??w>i1u)`6UU zKO9P}r;^LrMleYpL~?&SL$dnR6JbD0n%4+)%vK+AJDYwIVI%Xr zStUEtHd?Hc{)JK;_tSRlgL)T-wqy+2guQWZ&q6$#8H@hgG)u`#Eqs4Ifag-2VE?r1 z=+|vdB%Y4M>Fa*ns~ZeX>DK5&LZ7Zgz`e9Oc>j56TJxha-+K{a{*|XE!esaiDzQ?g zPK|n0GR}@>bf1jRe~tN9`r^;$_xkfaw^(k)lAi_j|GEFx$%5;rC$6>MMg6QpyYmfl z&S?|Avn42zCjs}!{81{T`ymV3#731ER#=mxdfxOCa?TSubEVT^a=A`UlgiXdOvDb- z`P~xQys0TkXnLENr8J|OV^`33n-%|}pPX-JUImd;=fcQ$wUzXmzAsfo-rb2?LzcxY zr3IG$v>5fX-QW88AEBSed@x5sqd(|t27t8xVkk**gv?tGu)D9A6SP!_ZuiiVAcn(@9c_xp0lOBfFA4|zZz%-wsU)&t)6 zGJwRM9pV1!4$y6+KFn+91nu6qU_9Lg@bYyqc&>?nrqlCb+MKO$^LD6Mw+y}yY^a*( zzvb(ixHkE7ebWx-=pXBs)oHF`JFmZ2n#gcXlaCSCMtOVH?L6g8E!p#r7@R z&GvLIXDf}$*|g=m+4pPVY=^NIyHLN4sRi0H*RU-t;WaRi&f3g)f;J1jZ^Txw7|6<6 zEoXbR?U-iWZuU)mIs15N0$V+2IV&zRV$Vw+$k$g5WJmWuluvB2l~qsM&YmLHs$>3a zlfi6w-xQ`d*N!Q*9muX=JIA}OSMhhNM`qh9lBVdh9X%TNM7~fY7n>=QhG?`moqXuh zrfP(oTqo+M{ZHNerHfoYC7Pbp4Q)`z8@A+LOgj>py@-yuz2q-*828@}dOPuV{aiY2 zs%U>+!*-p774$RCXZ*Qgi&b+*%|2SP$+VAL-@bQAo6iL$xHeh(&U8I$Xcn%27NUNB z#J}<5(n5KUGXFu=Wh-la%E+_rWi7QV*_sQfY^21{3NDUeca$>aA6t KrR^99;iw zS^VBMTb_I3N}058MVU#{x-!lY_=+>Z|G zJDWCb7E8J*DAF1EYh(p&^+;CTQaB&93)epw8fA}YNNi5 z7GBGxR$aP~n@8jX&#mD4X9!>W6xTe1x+=hut4G9o>dGg${@LZHey+j2viDH??ji!D zP6M*h0dWuY^Npos>jc!#rc&cQ$&i|9NF@0qiR5h}kz_ZeP6H}Iq7shZ;g=TSJGNpj zG8bIWY%6PXQw=sH)!`YI6Bac=*JYB@33%4Uo=P%jP)XBB+&`2ilN>MyNy-}W%!@>F z3^ns5>gRPVT@l59($6aNaR~UncB%)ifjZ(^`$t8h(NdYH_CG}YcdL?1hJ9%dH8tX~ z-ky&71LvaGkE9aLkxX?GJfS(QInj+yk+mZ`qFm{x<$h$*s1>w6amDX`+h08WRYqG^ zl0m|J>Vf-jFC*{zZS*4PCT?_0PmH65`uPz#`1kqG|J!q(|7;weegCyT#xLi+ z$c3fQe()T))prD3o-qU(yMC7$pl-$=QwN^_3+Owg56r%WafiSJ^0sya_p3u-^*IYD zXq5~%oJWC)<^s5&uo(K9*+Nf0S4i{Q208m(QHRn0YpxIP6WYRt6}s?bpaXOW9uK+4 zy~Op;@Nt`Aop&~jbKDHMl5p^vuoZeNh!orSZ;qdH|5wPLU87N478@Nda6F8{m_b-x zL)?oE7c!H>1%JdSj5#y`%XK@#h3!Yf1;vx$!ukv0f=6w*AjcTEGK6h@xIhu97W9P3fVefl(W+NPK$=kd!XJc;Pl3#J&!X6*k#@-?FCvRc1 zi~F;h_sJ~$nl)P=V9x5X{Sjij(gjgJW7jH@PiBgwNq{0bzDkJ<*riMcr>l`-4JR7= zWG>ak`Nk0EEw4Y!WqeLDLM%l63~%a5g9b>6Db|OKNhH0RF8s?J#`Tlm^UMCc<#3$v z{X^TZ-5<}lT>Yt^-$F(Vh=0GOWJr>tT>EvilJDEMl-S?xk9*RFWBk5Axo+<;j2~ZU zCGkI0IxVefS@J&JvXh&nWhd4A%9g#*!1xkNSib&Ss&mnhm0GNopP1YJFZr`;N2HdG zElnu%YPqY7bL5}p&$fQNxcDFQXFJ*InkDRXfG&=cz)d<4Y`Z%^(PRa(Xon8r`Lo?_ zjv+jMw(a%+GNbS~dE#C}M)XsmEj`U?-4hQ|^Xwc+_qLO%gf~ECEWw=j7{mJ6pYvyL zJT{xYvUH=fb}ppGON0Mu{_KHm&&d8UfA)`=s~zSV3*vF(Q9s*Dot!4g9t;kWiTSgw z6i`3;+NKq_q?JO(tWO}(Atb{s8Fdq5+dk}zapVJWeY6kPLfnI!FXj2OeebQ4)dWrX zkw1H=0@|N%h|@|nDj9B0|C&FWYbLipH)6@{&;QDw&F#;+AYA+Gqey~ry+C8VA~6qE zA|83l*s%j{pnY@5RNxr z0~?|O;LBnkP)%}zEr&(}&!2s6tQ{C#(}DJ?a?lAH2#+83gEL=yLZ-0^Y|JzoX=KdcnImLT4Pnq#185)L?l1YXdA{tlybBSLH-q#P~`!&rPai2irOgr10kcVWVjH(|mF#1#Q!;kFGIo=yrErcDSJ&W{fl zt{^fH4=3Wed>p?6$E6_3v5lk7J6v#*Ut+aS)7i?XGwew888-G=I#agW%Icchg=^f&y%yFUOU;GLq5#Z(UBFr_F+$k zbY!oJugmwfH)GAG)yOY?31JsTC9_&Y)aMZPT+)weuT5f0-pJXUI2mfu&|D*r*Ci-t3|AYOv-_9tKbgD=Wpx>iafg*8xszmNRT|wRzc@sB9Z@Q%1 zh`wlv?_&SJInFOv#O(=d$Suyf4L#|{=I4klw%JbUK?}Cbqhlv_{|it5$lHd+)98Z> zj&#(S)%3Ru4n%FC9nDAXEkXU{<=^(_KeH?UZ`*&o!Tl@$x9}hQw`J(R-S_|Czb!%k zZR!7#|F*2Luq-|<4$o{V;oKaDKG`@c&PntWTAi=R6x(@y@2j1B?&f3g+{zDNJFaW~ z4jemB9E1KJ@j2$^RoFK3=QA<7=FIh2uczkbT`-Po7tC`ulXCFC9X^|`wUP;mxE7h$ zEmGK`5h;{@j1aE9j}UIeMhJ^fg$tetg+1ZI6fDp9g$ue(!iB7xn5*S{m_W+Hgj*-W za0gPDARtN*b`@blAnNQ!g!*pG*Mge-B^k#c`XTxwx*-Y>{GQb~j)V7cn8$|+LmypZ zmpkuelEeyjU}*)LTCs-(u8d#+=w_y*EoH5{v|ui~HQ3EE1D5ly z4?E@P!!(A=S?x!RQ#9O%DHM-kE*?H?QK12Qx$%npZcQK79&@TpD+p#CG?SPiLKCk$ zE$PE1J0!7Do^m!uyRX>C`(Ia;^4~AHuc|~MCg9%NSBfO8r?O}ZG{ZBHZ!GOdBJSJZ zafl}U!(7Jaq_K$2sGpac|Ikk-tdBUelzji*{V)2-b7%8=e%YUDP3>sKYs?vn`niI~ zSNN%)$R8oJHc9Z>>BB13$Fjs4E9M+}Sa8-nBydi>+np(J{*?Oc7IrHo3pXs{golnR z5mN=fLk?`mq~6837tYB~Ds`9NJiEVWUQaLNMZJ8_k>+xZ?^B8jGu7m4ttJWWYh8uh zMNR_e2=Wrg3`7o9S!qkxA&2Tk9?k1%QDXQZORTp!aud%s>(S%;HqkRS?)2HgGh{K@ zM+`L7$@rOT=rV@@+HpXcO#k#U`te4hZ2rE*^lG3x)!FPq(>@HM_I)~2wF7&I!@@1H zDF=bXVZAVZf&$u< zzA~p_7@NJW9eyv+CLExR$`OArac^1b6_@(8lXN#xz}*=^}F`R@D5!hVwaT8nAiI zxTOIck#vHEyA5DqH$zzPz!2{E7{ks4Q>Y8Ig_x>1xUt_EB5$w5?}$V)HESMRGjfNG z_x#~q$8+Ft*&nn_yMg&CQ%FAB4OU|gZO!YmL229)h}7K(ubd8mS!1qvuk@N9Sbh)r zz0RL!liTsc`3iNMk9;xip1KJ9zM^)9Sb5Ae5Zig(OhpfKm4)zr?E3+1NB#UA7NXB=En;D zCb2@Nw=u%_7coN3=V)Qr-AKV0;T9b!C}3Ht7by&T6)r4`L*MQEaG?Zk%Kk3l!YnLx z5IxYQ{B3Ty5H>qph)49md@bi~!i8H(;lhJv;lg}`FJc@*3){5uo&X%T3Na7cI8wia z3AKrLSZeS=rjlF53S6q#g3JS~k8>PTKjF=2ZYpzcJ&Y+$3uE<1tysx)EjGctC2Ra> z$Uc2BV^^p9q1`C3GoN>|X=D7D&C9XOG~ADkZZKpTyQ<}ON6nanezm;AoDep;IEgtU zOlF6$iMRSPyV1$)UAVxWLq9eO+c~DJIw|TW*H8g%fvux(A7piF@;tINF-=q=r#`BX zov}a8v3S-hk%1V4H0b_Zs`_{?b!nVSZ?^EJiMYSR|EGSI;G9>IKacjtdFnOJgSkz- z>3;0z^H!nKn&0(v2G+e2YzaBI?l1a@{IRajQ?PcQc!9-+>}TI!C$Uz7u63*X+88&% zSAP3N3v13Fo7T;(%L1ENA0eLvqrDddDik z9Fx#tlymCHhu^5;s3K|*ZAxwCXi{4*chbH5I&CPwMi0L12W|!w( z8;<7CS-R2mTKhE=s?_MDk18-=X8=iR9|(nOyGcI}XiH*U^+=BWCGeM<$R6iZf}W}` z-F|r$)k>d7U$z-ZAGWLq<#-j6)FT{4-_y8Gxzf_#_9%WoHd-319-_xp5~-7OIF(EtK_$C8 zP)SSf!+m5aagB$%Tpv*j^Lo|GrYy!iLl_I5`)_N4++>pWct$Lx4aNahrji2{#OZ2x zjNf+*_YkR~&o>CQlIMAuk7WqncSo$_S<=B6e|IqFTDZR+d2>pe9CT15W_gO_OnPh5 z!9#^KHpwOyxQ}S|s8)1IkrF9=n?u&$n@cy=&!x?t&!u0RdsFR+9`rc!Z0;Qg`r@rC zb#FPC=HY&D?(5zE%tI`{AuojwbLpkl-t;B%Z#?$>($SRSsnj-(PfpH%b69nB5HxJJhUybqu=;}*2w{rQB&QYFYhvu$ z;o6{oQwPdZbYbrkJ+K3|89YK=Y88*M32o=xWKz*(se4U;Iv-`(@{=ysx>Ffn%m&3*SQQIic z!#1v;YSjw3PiUr`mmKFSc3>`*wN_6@>xk{V-ffsB=Db+T`?2o_u$_G<| z?K5J9K09NDeFtI$wJvmHAw03fID5FZ>HR)jxcoI-SdJwi z{61i=vlbCTcl8Lt3^D64>gc_2;Zh>%WkR^HH$GfAfQUe}!!~|TI~=En*n@2xiece` zPwibMJ9~(^y}iP2CtP6@8xFGAsCXtfUcm0ur!mRm5iBb#oITPKm`jp2d+*kY9X0C0 zns({WI&EIcdM&YGX_~uP*s`TerTutTTey^c(e1*5Pu0mQYt7l+Zgq0^`Ix`#L^3;y z2%fi@EvWCujNG@e%px1MYgT`@AKN+f*Pg}SStD@N2u;a1_qIek`UB+9Pw>FKwWy!G zrrS6M4?`$YU5eEym@pJ&RK?P%4ENe zGPyBTjdu1JG1?zVQ&$Fks2Uv60 z6gKLau616Tjy300*Cs77{_PGco0;m?5epQobJM>G z@Ar^HHpr`!wB9eKB(isFkz+3$2<4pO`nfcq1TrL$MIqz*4UiFauY&ADHVIV`)v zNYhJHYljltc~VaAG!)YJ&+_Sk%dym_<9gb>g9Z(}WGl-w-$?W?yTD<`b<$GL4&-H$ zCb4^825S4-$qZJNLcp56T4lC4<% z9$ejh#QMkO3DQS%szv>@TC$z$^tekTvb`9;uLDS8D(IyNXL+1GOaI%)@%u5~GQ4{Z zoiiZ?;}6A9$&|6w>7)tf08K@ka+8ImWxPQoB*41|y#=q@p@$`hf zEMq8YZHfua*oiT7Up~Qm+Th;XoqP{2?lW`q@`J7s~bX7vFg4He8o@ zEl?%PgFiwA`at5AXA{mhr#MAA(6coex>J`doTN%_5Bb5lW_AW7rQZztxyN+cKVc+o z$aSaN#?PiBgl=>Oa<0i-59)!Xxu&wn&#hg4@NeSEp|ncZn@&akO&;q)b-DnJnCMTV zZ|^17ugj?$o?l*~gn8jHMlSbl{A=UC^P{SxZ-)2(D*wB&|66^WHUGLFuAloBV_ZEy zbI92=2h|KL_IUZM*MdfJc?qyv43F2o1uf#sU^u(iivxcM{* z+H@HK51!8#ZOC*_7jS<)2IAIw!wuCGsDI&&QMEciYCwDFb+!XMe`E+w2b|$i!3-cZ zOJL8M!;n`R16SV#LFr3T(!Rqw|Q9teP7U0>FE^=OS#K(DIX^H34FN^KG zzN$kx_FM8Xn6v5!upRB8-+|`_rif$W;~K>0++8M-H=2fs*ZeQkF)+=l(NrCE))sSY z*z!Kg!Ta#p^mD>lA!|#bkU2A6aPAl{3~7iHO76!AK@Vev+WpbO8N}47(Lz3!U#~?8 z>TB?fihiVE)DR(fo{bPL)!=@gOPCiMF}*56_<*tH@76~M4-uPYMhIyc=nuAz5FQH= z!eK-%Vi&>>+jihRH8}1%g7?MK8!Zeh)P4`BOlW6taxd-m9Mkl4rjOKZ;KyGaFb z3^1<*mDsnSl70=MCtU-_ogabQpS_gt)XX@Mg<57}~5U*>bZvIpC;6W@xCA zT(m!b>F1Z6d9*9q2S@UBNXG~rQe34&Tw8h5jcZoXiD)PM(oX}l5oY)A_b2@fHuMtp zGvQ$~Yf^oV#m~%Tf%$tePfc6vQ8(LIb52c))WQ5KURE|rt*j^QYi@nDM8!IfJ`nQ0 zUlMGL4zY^;4JADyDf^tY#;QFtEqR%61$m*)O1^iKTc~d+3d=W@f3B+&F5kW__`bX@ zaE>4^@r=t9+-D?_FS(n798yPKh0tT3c_nS`UJ>hi?kXWqEk*s5DV0#Cd}BI9QJ1!O z=SK|dZ_;bwH|Tc%XEOKm)pTb~p=^4aD|FcHb2M!GDcYwroem3#rK>h+(X&qoxw9@2 z1Mh)sj@w&|^Vf@1g_pyk`!s+9cqr|2U6Vyr#f z6YFn&mVZAijjxtcmj#EZBs7ys>U^nWt0Q#^%p^{_eLzxN3p@`Q=Ki#-d)EruOi__- zO1dWIU#SVIk~%r!8i}bl+Mcdd(%BmA+GOdEvny+U#vdw1?Zo&A{^TxjFk_OxC%j;JE%mcE@yTVQFNt|RjE4RVn4Z=p&Q8GszrLH@Bv z&UDfeN|XAnpwY;|n-3{Hi)TXbVeX$M6aLoE{{;IpWw;K!t2PJO;el|#ok5bd7L;PF zz3IC(!TPoq$od;V5wwEP0oqWTp$!i*+rX8rdJr&n5M;?F!2YlH80$6z?2NX+N4v?8 z*$>aAcrSx_nz@h~y9`#WZwuG;+rrn&ZK0{JK5QK84vAA206Vu1rtd!lMddrdXKWDc zY~c!>?`#q4zrG*b{>-wwYn5+cgYWZq@vP=u(RZ6=H_a+-{#UUb{a{uNHE*$h8Xtq_ z>VAOxYX1Oy?++BmaDOd-j{RMIj1#k4d?x>S`35I3cbTbGcjtMSdukr(!Y7OuU$8A+=r%iEc;Ox|w3!npER)9w(-3xV(3gv4{ydD|*DFfsQV}V5 z;Q7x1sHKxbB83m@B85AM8S5j3;>1XyKb`@#M0od!6x!gq(I9QCx5D$HT9Lvl#0^9f zY-MS&tQ8YQqL@G-ZCvEm+d)RZOLJD7##|m%X^TiunXMGaZxF%zULO>!4LH zpJ`;l3LGBFQ$B^Vwhr4_N5t2Uq3qkoL2Od%RQA?n7<+?xxm#m9@1GS~g?4=!aC{&; zA%9{8RNC!_CMt(O!M;Y+PhLKsXhy3>)WG)o8t~{;3%9q_LSt`JVzc`TNON>BcgPR@ zEWfHkN)F7U3vsTRn`}mMyv#_2gxX|RJ;C|`= zdw40Jq^;%v_T}3^x#fV9#nHVhkQb(`$MfOxb`_~b#nXq$6K0+k(xF0Fakot196?^9 zjT(sWYWi|`S%MrgMPBWu(?9Pn+3Fw>IaU$d8F@NQ)X&?F#q|3F6S}Uf72W7NpV(cg zqc+l7`qo4fYL;E${#IFPlZ!O^!3lcebP-j=b102kp)`F?OIqLY3Zy@cAVv?H!H7Yj zvg;R3$Rv+Om@;=4m^V?Bb?UJbzFgQ!E5=092&*mh=@oyvRwEzOx7I>btCirs&_LE% zZ4*LGcC3XON!>kKtlL+wldjXq|Dm6oQ|ZZ(w@AidJkx2_6(s#H(k))+(f%~C^j}?u z-;chQbuW+7$dh}h(?N^{&(EQJkWwezy=Y6$mSuc8f%az*KXYPHH%E^lXrC9}4H&tc8UE z(V!6K40B?~!;1LD(Bj!q2n}5fV~**=Dl>g3G-?OxJ{{ollWA~n&MfF@?+=$=r@^6J zDX_i?z`7zgSaUN?^xv){4Wd1}0NS6>$x@c5&7UjhB}ZJ`9xI)!4H&but=P`%34YoP z+ju|r{Q$P}vA=*?yB+&CV7;689OqC=jA>NP$BX{kwE54_o*jngKeyuf&#k-&nij6w|DV^*MWH90p|Y{md6XqVR6F6O))|yVmFHsLaL z-_aM$?>Ua+3J^=MZ7Cw+IO=Ql19stQ4qJ?I{G88Jv&ooq%jg>Bv;OACmN@QW&XdM6 z;ae1Q(;Uif)abKGhxOQoRo&UKwNf_I$Deh(KAdeR%3uw1{Ml?D7xpCBpMCc3&W;>? zEFUCDnU>jOxk|qy;1Q8W=oxhg=M^)uT z&}-NU-#6WYmS1l|u;h@apS;w!bD%904uQ&+!|-W&HatF+3&GbF$n6d_AoTtM%hfz7 zw?m)3(*9jPd#9)q7gu%S_gI~9y-Z!~OH` zd+P_kNvwC|o@HBAGTHf72`sQjE9)`WTUv8YnQN&nqRCX_e zBjYw%?){=m$}6;qZ{}I39AGFLZF&YWZZD#q<5y6lyt#CO+X&j{@J*PJszA(U$AVQI zk)sHjb@q}a0#PSyO`dkTda}N;Cx2Il(WMmWMUGKwib zTIm?^>v;>>b=9CtlBZFrUlXDyG=lAMGg>ofCiOP!M|D@=`B3C1=OE{wvbBN8LFqxD zd8;PVR5xq7I(!i=j($wq4;N_2{r6Eq$%QCk z2F9&DcMNlaVmS#BmKP-)uE2Qk=TKJ>K8vD+C)+X4%9JP}WHOeBeu%b+b|*z#5Rsa7$dMuyPh33&0}pQ*04&28Wu4+mp#+KbDGY}*!DHMnNH3)wy0MO+u=5x z8EbW57w)uUgLd^~B^bB%&Bg#W`Rhn#dpVQUuMS{Z+gw?{?E!3kTu-*K`nmk9D=NzKosix`SB=4y^xkV1?MuaWeV_#v{lF4oUkf(CB;%)ZXU6 zz@>TcFuYdOPhN(7SWO=`s{>ufI=Hv124YTJhFQ~#VO&iv$SiNbDUWe<_GdLxx&AZ! zvOiNUWy8ApIZ$Pr4O~APac#-NVV0<$#~nJ6*3>St-lQ5q;1(YhoJnp84_6kA`V$xEte z73Q5cmYk;nUQOWC`_r_o-%*;Gn?s+QMbpH=>u9?;HF|J%q3q@L^<<{D1H68cCv85Y z9YI@?s7^ix6SwQi0$&zD`7O*rUbT*P|GJ#MIX4B@^D3dmy+&9(X$u^->L>FUmVjs{ zbGq3Gxi@Evb+d(0(*By2Qh%G^aC_#3rGBqQ<^VNBgtGkG-_T&t5 zZs}9WnJzSAlMa<^e2n{t)KMoB5O<|N^wad``X_JWzFWiJ*FS&pjhB1h-hgjmN1?hp z7xH?agZ^Pzg!66L%uQtYky@~o*TI=fwGh|m2j}b`90!G#N6Go@B2ayMp7`x@rIQ2i zLWs*b(3<#`Y(MHwrET63hYslLMt)b6-=UbEd{C5)-ZOxHe7kB1J<~I$fZFK-dDGW&3gv}#+L(SuMzOx&kq#W4J7q9U0_3%Bg7cY zhxyWduyMqExHY!}e9mtV)$cn%@kc{AcWDarzB&`?o-T#mPq#zM3yBakE(+9d&4fuV z3F39u(0sViugn@&?8k@)O&6ee{rd4BL1=t~LDtwqtIM z-+_CLnu=rMpM9n;l;}HS(qXc^#zpNojc>4g`vHlTp0I~D8C_w|qpTcp)h)3Ag zfG~O$C46{X%T!{sScBdr)>`8di*?IlAKhcwu+t0Kro0_&OQQp8<`%&YVw|CCzU|m* z&vxwMv7SszmoTe20qp6Ok<904CL24#$#YdvHdvWZo{45_4C)fMh%X`?eqeOHP3_WT_b71 z!ZACpy^PqjNe6U`93O)r^!PXNsFy%@$_QWwNi%^~*6vQ7~KgWD8q2CRBc?elqJD zzeC>HU=!yEyKZwv{yvRE^akpu8!f%umsSL1ld_ay@`;~ zpo+9h!eJ7U8cgmvtcTJ)1Ib;t`{cle2J*GfV<3k*k`~(Y;9eHa|5ft{+17Kf@BP}!FS}=Qpx;e(q~#EY9r|p%z-fdE=FGkZ zPBtBh#6Aw6SBCcIc#z!T_ZXsf-a))X-Hb#VcB5G5`9z(ZoGgCv>{l5qxdit6j>49! zd7w5Vop9c*wy`B@FLL4X%RG2D_88b`o)J0s_{~+Q^ei9^dKJ(wnvnVd)gu4;rJaBjnJTHwF(CIfMp5f4a_XM2h`RP}LK+&K zsCK3&J&5mb-{1eOpa1UqIl$Wx<}EdWi6z$1uKzgjd*ugL+L}Q92NMV~{v;da-5qM4 zszFS6FIc2J80ITcIR4fPo>~rr@~#B#c>!2PctT#d14Lo|vX8M|FnqB9efmy;8K)B9 zj`tKeon;1^Ee1e;pFXfS)&z6+4uiTAVx{!zT#p#oUcN8Klc3qw)4JU!2L#hu|E{+ z=Hhes`WG(N@$sVn*8c7%D^MG6^<;FcoCEtP$B%zQC@n9^E+h9a_cbCJgp(ibuG zL8Rc<5Ggnyonf$`=KV{G{&h**T%G}Kxg-vh^WLma|34p24V;r1=FF zq<(=ZP0wI&k47=qJKoIw1m^3M4re{)g|Y8R0_zyomJQBo%k~&{XA9${?1tda_H7)_ zE=6as;S8@^yD(#Cf3{e&JF~d`OdfVt%05heF86|P7891nQW3Lc;Y>F`#(F*FTf}_Q2PojT|bG(LeYQA#}77He`IOs`jTF2K3h$<`c&MTtS`9*-Ak=^Se0__ zwAit+l=EkxWOu30qsG$W+v;W6Yxk3Xc|liIK`3Vw1!OKs%E zc-G8ojeK;!$|7a&0C{;yY}vKg@Unffq%zJCjQ4^0(e|Rw+Q>7myh9F+M_yIawrge< ze;Lt5pFur{@b`xqJkST|68Pe`4@Rcn{O+o0CYhfiT+FmQ;JRf}Ey{ zi7e_68I@Q;X7!QKWwoYsjNK&C>{}t3dPpiOyiy0d+{|HI*nC*sy*;UFeHA9yb(7_; zsDeYcrcmMOSnB6GgWk1XOAF~^;4vDins0|&yNAds_wPsak$oF;Oy)2%IHxT0wq5X;DUZVD;OQ$T3#6)QSH(f5IuP?rAMdJV2qz& zf%wdRsMHl|F`=1?EA0%X&2xJgDuy>apkoj+qVSLr;djW>5lLu zSs7k8*n^TH`fjf<%$GeFItBHIbCJDaV`Nv*eD4Sv9R(OPZ7-~inhd&^mxI|0u{NU=P7^vMJ0}fNXV9J74?V|nE4oJgUM##NS`zlZgnR>4BuDwy=h9yUuml1+-9&FnjFXN%t0v&OTbto>vwwix%` zHi^(>8d_c1xVXX0$YmwdojQbhH`&X|C$40{3X@sg_LWTYqY1Nfdn9jKH<;z=Hpmw? zY-J9R%3KgykG8U_wHEC7xb199>JYYO#9%f8+d1MVUzhXwEV6tboFA7B%Hxi}z>_&} zeN#Ss*`5!5jg`sUL;3J^RVPyYJs-NuC(ttCzjRT^F92gZD?0ud#-CTp6X&tt<~We8 z`@j5U4&(ZH{CYkdC|V`z>TNBn-`79?sGnQD1|Bi_no?4`xV5}pwr)vMVoFK3tYM|^ zm(48YoO-e>4deG+vr?0sEq$x6T^1W`TDG;fW0`%M;IiVA=mve-jRZ5gkc=*0(Rc*XnR+d|~ZpQvH_m0QPI7g6|xE5b7=lq(oTN^p#FY@T{ zgP`I&(*&_TD8>)hMKfr1A{(%($_x5m_W>bkG1%LA;d`YvQJvP6jQKnfE-si$-gfH) zjvvAZ-E)2n+47H6i)n|KAKl3al^Sj`8^n_)d?>x*w_8OeNw!<9TbI?{j ziN4-MaB_D>dosnsc*tZK<}SndeGjE+!IxxDPo$%UMp$(9O_Mo=b;13*ZK%`co>Ve( zDs{T*gE8fm@p(3Q&w13%t*D!P-_UNZqlm44($DrU_kqRWgJA5F2d^s6LTYUe^b5|1 zgyC0U(T;qmR5?H{6y(FJ?0IBAa_;3%&S~C1LfX6-PanEe!8gNLGWJ6=5~gtyOdKAN z;Fgsz>e&Q3(X;X|-20`YW*2*s7KeM%U|&O;ziScISl9pqn;X&rE^f5=`~P9@O`vkz z+CToMGKNZKDyc*zN=5ZN*S&l8{e(;r4@Jm4lPQ^wNQETLl8PkJJSWmrvu<}p)d zng7?m!|OfoIcNRW|F`~Yo&Wngt@SIYpv)2<{JX`x8|NP(jYGVZc zo&OtN?Fx6du7~Gm*TbnJt}y?_Xz2QF1iZ6Vgib{~jKlrgZH@pmooEI(kN1OwCWfGD zsSClbBf+)_2eB8n!w9u;Fyh5hNHJRl?GlE9&)%8B`77zSX2Oy}4am&Wgd}whkU!`Q zXB@}C3ehAmShpDLJ4M5e!-1gQd>>SO2!^EbenNe9KRbxnx?J#2mLL8d^3P(-EmU#e z2-9L3#4BJq7}X7#@q>p3@!F_k$c_JE7sPL{58@4Gpsy}KuKbE1{?Pg$UU^y&-xOsq zHHg)5znZRD=}5bDSwpF-9Al<7uCa&S>oSf$C?DSFpI-{IVF`ZcPrsTA zwiiD`>f2mU>(G)LS!ls^{w1em&m;v=1L*B^Ad7}o1M|<={ISGC)r9Mom-8RbFy^15 zi@w6xi)#h{#0h6`8ShLU?_EoN^-t$0ufvbGdlxxj{y7lyPaVuZ12F%5zppX>e7LVM z|5U^L(;oBBM9e>9F#p_#`DX{rKigscx!~A+vJmsnQJ8=B_=Ehj81v6^%s=;G{>ftG z&+^aie~^C$sZ)!0i+Pxg`KJlypZhTXbjSSD;Cuer<$M0Q4)afK%sN4XpNyZt@=pRZVbG#ZUWOEH|C#9TJUfI^G^rNKc8X# z8IAepyLV$i1M|=7DTlxt^Up4rf2#aJ{u$Mv4M*#Hz)H+N>py7-`RDux(_psoil6x> z%QeSf{(1Gt8`_ERm-qQ)^VFhw!|!AnbR?dN<)-4!MR>SI86Y$-dtt558*S3f8z!)D!pbd9psj z#QN}aWa_rvCFutOd8UD=1E}jL9+`dCNrAiuYC>`#uYjdEEs$3&3gp+F4CF<~kJq^z z$S*1g2$aIK;*{Gorc z&zRymM^-*fSK#{OI&mjGH~pJ`K6dvy{8oK#QQr$!ZTcR)QmEoHr|6h#yW*bJ`pA!W zlJ-kpfb+KzHf=lticLRUEMBbirnu%t=aQPqb|nqdA4)&E4W%1CoFK*>l<4s1a%pOl zZ!AV=)ZMAl#Y-~^uA8_>U50Nekvv;ha?p5l35yYoOUh!6#n*%5moSE`@iTekQ_SGP zhDB|J7`ts{hH>gnOlqYT9B%-F-)4;4h<024{eP!lgO(wkjoIqiBj`9(YCjkuQtc| z+Yp?;z50Fq5X(8){H+a^Z2eH@pZOtWj?rc z4C6*=rr`_5$@SpVh? zT8iEvFIo>R<0is$eMb;SGzY7k5g;}11Sww!K~o(GES_Kq+o$&je={S9^mhdLn8EOB z$Y$tu#|7q8U0yJ9!!xrhm9oL?)C|eh#&f2hb-6XI(JR9!E zuYrVwc(C`65ahEW29{Ls1-(;2VAD_lZ^kw5VL7bTR9fiW;67<;D83uTj{JYi)lD*F8pIzZbUY3-{ra2eR|b*m&XnXUW?~!r5oBu6|M` zc4iq9`+tX@BkQjmC^`KmfM?=R`KXVmK;*!8_!Pju`WV1FBUA4pmNDM~`2B4I`FEWI z`N`UWe6#^F-&F(olc?q>MU4E%P z6+CwZ8GkQP`q;)aYH1*E7+XbC?c?Z?hEp``!ztQiR2BUGJBh-3r603eu=DO{zR6N) z@XW%3sL8I<^;i5#jI}qHtWXFnVKIVnNm;D1_%boOk1@o~)H_MKwi;J>WnU*D#$NWe zAuOik*3`m%wFSIB%fY^@01j;|z+~5bPEftnvzXZOCh4^NOB_E z7TI)TNwd?-$hb{cAxK|=`?NI#T%=-&jqW~_uEhCfvSjGVl|p^N*WF?q_uW6=T1Q=x zqkoRU`Wy5xL&^5La-6@Ni}`0c`X};-VmrX9q%eHnSpvAng_9q5!Kg9g=^Z0aV`z#gKp3aA(?TW!DHwQXy>c%yH*_xZz*^%5lmdI_vSi11L zn7fVj4yf7ngSd?ta~F+UlY_T*akbbs^=mWIxR>+v{;{wd1OP^3T6~zxv0IWSs;MxiU`0OwaUJbT|o$aFFvCU-IH+D5_@!kOK z{2ie0ltmCWD+5wyFM`5+9k4p03+1hJpf*PfyjnQJ@wqd>a+e$QX_pEP(GhUh!3Rcl z4TrNfv0!{H7#zOE!Pw7b0LrgR%*@&$2f+jL#&&{`bbvW%5ORom3hk`^{Bw6?js~%Q z>}wRZqc8rHO?i1T_6K2`rEs6wvxeg5YuR|gKhKtnuk@n03Uuo zfVZecU%eE-yIu(3E6yVu9?Ls;7K_uZ0Dc;3U|9e^?{Wa2g=e?;7YFcdQJqn5ivsw| z$c{gSpBaqfWGFRkXDUOr-gK2-c%DFq+MT2KJD;OD^AhNwbHS85Gly0kj-~?xY-!Mp z?R4&EG0okrLMxl8&~u%;Q!>JYcJ*CB5obut+s9FbjVtIBl`%9RWd%LbtUEow;*!+R z%7n()Uy}Cx;7jdBL{d4bYlAN>ZRkg(^CGEJs+5kJh%8)eXL5X2B2~cW**jSV%#CUE zPn(8D|E#liB+ub0QU)6RQ{fMuNs8FEE6Ra5MKt>7R~-9i`KQY{=YR80PsJvrTfPcc zwRDn@f9~ptd|38pD8J^{uR6g8*JAGMu%5p4nm}VsuJf^@YLN5pAys}~fsC?T8+d$@ z-}5+wUzl@<-?4oQuN~>c*R=GYU%6M(v}VVpskYywQ+!pVIogc>=uD*#y`-HBE*ALS z-z054tq;Gfg%y9buP)DG1mlvjSYz>Jwd4!NkTWtrYRMw0VNn?-gRI_a?O5dE29f3- zsiaJ22lC?-NyqR?u6?=|cWv(;;_Mqm9;u7LsCgJE9n%ql`v#Ec_>IKv%}VmLY%uxK zLW>+8P{nZL{Y40@fswoB?@lojcB?sRT_;ahj%;Pj9j-(@U^{OzIn*1*2 znqs{W+m%MHO@e3dra}+3wUFa9A8yaLhpeOX zpk8%9Y!}T3wSoPRt!0dy856Kow}z<4j$piFD!4sZ4k4m=xHl&RN)_Xv>(DjO$2(l8 zzYf|A$FXf!xN#}V3tLqW!(8(bt{HlSz8Qe!92-tq3D;;0Lq=bKOu^o(0%Y>3yvDOYk0M7N zd9}Ub@%+!|K;8h=bT2aH^Kd>E&jZy#C7K8F@oVs0P<1?)Of8V#g7QPHM5SOJ+Ydj} z7RRZgHe(x8?biTa%j6!_S)WP0!Y)&JpUX62Wd_|)6+s6XET#2xV(ECBk@Qr%Alhv$ zPmh04r?qq2(sBhIT7IiPHJag0hYXU@Pi_b3(qZm2;iH_sj&i5!?|RVd{n9 z=2lCudTgV1myi*U>Vwx&%}l9xc@(`6Eu-g#n$f}7&UDuAIL?db!m{bvut$~-?F-W3 zH{`^X4bK2CQIiNiA_CVR+4aai3$gADM4^HJN7k zbt=&+r8l={(KT=MWJ9WSWh{2y=5|MZUx3Z7EgCYVJ?&+EKC8)Wuf5=9-D>#Nm4#G) zcyf`^+#%HXY7c4C2?|9YF5SYo=u6`skCEPSj4vp2aFCwvaFcJ_=^p=T$u*wE2*xG8 zOW2AG4sGd~ZKp7X`eIxqlfcDCizcNs5n}9uZf}g!IV4i=Gg&FCB1>9XkU`ekr1GjK z*FUtLkosrDu0?y;`}rX`_UM^pit}9}zfn!@2cIXMA19OW8Bt`ok0x=SrNVVRx{p(D z_e9d{))7fm>Oeeq`wOz9li>Z9mXfiT62Mq7h9t!9Co3jJljOAlSZ75P8pY=yQYMQr|bognEg; z7v|O;%^xt2u)enYI^pe46ZzmmB68Y{?AvKXo_`Yk_KZYS`3(1eI^h|iEs-TWqEguzw@zxjP zNfFh*jTvd}=RjuZb|bGb){bDC{}^kbe1zjhVeDyP+eMTy#+wAi;xJ&QF=-avggjh1 zfE>J52G?6X= znM2k-8!+Ch3cF9JLU4jI>=>m6J+nK*qXIQpU)vTEXQ{(v_l{tc(HZKDE#Tp}smSyj z3?+%1;BY@rsP%G!i*Lt4O5bHLDnA8MoR`68lg@Ax zKznpJBuYY|rXUq)zbRk=jrHYPHE`Fz37+v}T>7r}Zd|k20-r~_kqxxl#@vj@QUm9~ zQiOI^e|z{K_G_>)uET_c$KJ=i~+PM^L@UaZId_$+Vf-sCFHK6iy)=$uU_;9P36 z1~)ppGLF9c;y}l)386DP458COst2R*RWBpdv zXW0HAVA4=e^IPVhF7fk7 zj!zmFGtUa~vNy<{w=42J8;Sp4iV(2v%2@+{CfTtII2E+(F@^U25k znMBcWJ^sFj!R-b8Kz!($c;gfz**K*@e8jXLcW-Q4ye5(rhL&O+C-`UKyh36x`OL}R zU%`Ev6X8;u&qQAT0eL}5R-$RI@b{z8DktPAnY`=^ctzY5&b zqRch&Z{XDpbSXy#f zklf>6F*hwc3m#*P{iamUW!*6&D=zAjsBbnT-l7B9iZP`-+e#>hTYVMclf@v5KiBMD zq!RmkWBfTS>_a}Ve z9gbebJ(?nO7}bqHcbO{qI;%o%XnUy2Rz<#o0R((ig}Mwin5*0tDo(YB+1)!rQEv;l zbJ-5Y8Q8*(snIaSWf|nVjfbY+rojT!4KU8H1URz|puH5&ZRy<}a^sLwU)%w%`!0fj z$d&MZln*r7bqKT-)7bBYs4cQ{SI^!2v(DO>f39wyjC(RWOIgXZE8GdozL=Ai2<@!i z)I1;i`?4|Qhp-)U&!58Y{Ue1jyTY#u_c3&?M~2Zx;hOzfxaSk=ipVFmM8>Hl?ycWQ zm{=d)n=D@bjMr`+&A;5Zn_p+LoA3Q1f?rk_!JFsr;=gs@$=9RI3&Z%^SgLD=@o(0L z@R_*Z^GRtiUxs^9hGYlxMv0ADnT$-^l3>2RIGBHhTG|cwt+*l6wmyjGp5q!{R9jRN zR33`M&ppC%4^b*uXZr185Z`L}BdQjWOZBpAXzUA~ioWz*PwS=*qfeV8(5rLSQ=OKR=-HI@^iDGa8sL3j zYB}19Ryo|0N{{-}=W2WDOVsrv{uI^@q}wON&?BkCXx>C?`V`xl5^c}_^v}>){$P^p z4}CWWgDGl2X|Uj*Ru_W>|E#MIhP9tUVAA|hkUt3p^~q}7rOXVt;;GKDJm80aYN3BF z!DrQ~<6Sr#ueO}g3{5Tu>&!o!{o!+kN8JU`#RpOc-~bn2XIwL!vGT3EjfQ6u%-N-Yd7suPCa4QCyi|^YRK!HfgjozCN zJzX}yI={2xp0ho`xGa~f+i--eugoS%OA^T!%k7YOHy)g~^DzGA8}S;sT(U-fNnCx> zkQ=F}gV%CN=I{|>94B6Z`KQPA0+N>Tg-dX~jO%>Hfrt4=a;)Y(`sXDpk2Ur9`+@Hy zTi20pi>{(?o+lpf!ic=Yi-@#Oa|z0^AiB~F^H7l}Q17aB&x8G8P^WrvFSnzDe@^R_ zCl83(N_IoD1$8i@OCzGOk@sFxaJrfMd(hpE39P-F{F!|BQRAk_8Jkn~{_& z_1rhhQcfYXkXwW?7Jtf;ure&H9mnF6#UP8nG5bz)(b%qp@pmZEh>RJhNIDO*5@NPM zO@&;H97!xJmys1}|M=(sF8BQV`=>hkXV3z5=y%){mKvJ_9v2OhoK(SNg(^r4+d;@R zRmj`i9Y!0ff#Z8MkU6ylU*it2BC8|hcCmo{@gv}L1mlv3MB^^3hvX;Yq1SINuxZIU z=r}17_V@e^hVVEZ8Aq{eJAg}hN3cu7bIM|tgVW+oplP=S!evo{qyZBlFFsbduCnL3 zd$Se_`Db^p`MCDy9L}rG7sTFgcK3Qh?yTMSIJ z7=!Pfg!_0s*^BwBiEz#S{%hKg#Cj^`2OlvH{mA+V6YIl!b8lC-klo+9htI-umW#6@ z`R2joTso zThv};44r?4Tt1XD%1l3m-x7k%zWQMPEV37}Q8}nIR7-5@bO)J5IF5<+F;ya)Ui;}| zsw+N1&pTeHhWBe}YTrEia&ZhbtNV>MKYfVKe=vc5T^3G zZ>(v^vRLX@ZcqJJazY>LxBhlTn7?InxH5e2`Ff#0BpphJlY!}wG$uswPgb)0Q#?Eb z+)f39cXTjZjtB-Idq5H#0z>wt!}I4CA!=(Du>7+b`sa9jR!t@+p>N?H=pEkxzF24e zc^ylZf7U+o;C}Vb)MxoZ{^|R6sNkPgK5b<;2VA9L@^ZT9Ln*y&Z6HfN&|Ai0syIYn z#^NXZgPttugSKp+PIsBxmu50IP>@Y>D4@$qW*6&EbD=YHO^V9j?k)0v@(TBVrb;Wh z%Qma}8y2|7T(o&>ts*P`O;xtozKx8<2*xFHBwFDd#$MbpGZbSeRfr=4uUkbidfr0) z^5=sbiz()x*Ct;eL2jlb`-MKa)ykhccIGX4_4W7 z%&R1uxwB-C^$B7%-v%7k&ww1eCZHF$Up#M=nq=On9pbP>1G(WQ*Mxf8+sb0g`8JLD zXG9Tc-|hn^Uwi}oa~5=Hf+n`$EBdG0N|e}uzaKVMc9J(_^t3u6cfUtGmc|qL>DE25xaLgg1#|g+! zXo3D2imDanX&cuL;V+?w4nAi*kRz}CGyi02|voe8vFlMAA9=#eScW~nL0`xE;a2B+VN&^*_MMo9aSN)w<

8(`~NKkyQTg5R@5!GG1~J3;L2 z#XsxpUv0>3!Uv058*{v z<{_)k5Y-2zi~US`s6$xaj@p6RjCzfFho8~Ml8N;(@i?}-$yNFxCW-EsRnlymO1dI6 ziI!dtqxJ`u(suE&)a#`^Z9XQ5R+tZ_7xTN(%bDG1c{5|$Ya&NKRBoWdV(e*!UJ`Y| z>r|zwwD!^lTBc}BN2$D$YU7ykSmqi9QE!h}x&^fkuUAjt=&;qXG%eAdW{l^AKGtuN z_b-0m%|W>^vnmJ1xTnKZRIdf;f`3-eNr$+;%{hbV>7e*14ceroLFWZ&(64nSyd56~ ziNm`HUda5jp*|B7&_74xb1L0oKhW@Wuv)YGr+=QpcIKaJSA7%w_DB9{WSGfy>ampA z<;d`iFlBzrv>Vj6_Na7QzNd7q*&tjiR!?U?sG}^VU~4t5JJYq;YR0UXmX{km*%W#)CXO|7!y=-DVHFcGwFnNe1%O?adzl4qQ zb#VNUI&3_UBi2My(-iFye=^%CS)ue=sBiwzh2^DUQNvXbE#CptdOelMJ1k}IA94N8 ze%QGq3V&yDO^?lH=4nXL6Tv&BX31je7A%#fUp z7LgC00||>+XwV@Mr=-OF=u+Z%VeCKt`CmSN>wnW6T)tVuW7{cEVKN`&xc@}!xg|LI zSqb?kEf&L9H65@#2Vf!L!EfLoh>REwttQWaYcr;Tni`%(_Q(eYu3ZbOq&R1EajTGD z4xYOShHl7%s_mNuZ=4>x6;x~b!)DcapzO317B%sJzAZLGf3-J|Q57$YnYc0n&ghr? ztP8&~_@7wUeB7(j6W=N2<2%fJTt~YAOTc|6mxOj!KYjl!_5&M3ehAy8zX<1LU4${^ zMXu|8yfZx{R%?@g9oTU62zj?7t966z&N zeHYHpq8uVacpH=`DunNcWkGZZFF%OP+a&bIv=F`v^531}F@Hq8JP^V^M}0`awMNO9 zlcEZcB|iuG@{^I1J0E$uW~c$EUZ_u~hdBN`j;lhkKBjZHX376;HEq8)k!~MaL7NP# zpi;bE+Ym}G*e{{BpZC%J@#qBy0%?)bAiANlD{XS4D^0ZNL-XT+dd=KGmCHxc;fE7x z`HT&8o!Et*aos?ZE&I^z^rbZ7D9|y#y_DW>7DP8rjHN!P#duxTmZL{iV(ErEBk6iS z5c*jEedmAqXUwo%nD8|Ryk(hiHWb%$9BlMYt!<6|sj(cNZ++6CS(|h?^dJpJ#HI=U z=`%@><8Uv*@BLF{W-P=k$^hnxbMfqLmVdH!Ma)0r^)435@;w?HQwK3O~@_xlEM(E=)eWaE5Hy4l=M$#qM>`Hd!NK4+G zurFaTf^muS|1S8RR8bn=VIRhj3VvogIXt3s;hrD=l zX`)=%dOaDu_v0G1v~*bY&W$^cb07UZ&qM2#>F};^I*e0GhdYncKyok*rkzfN(N!8; z`@&G@xT`g3a!gD@wZh@lwS92rs0nH4HjvzCVM?}qNrCm-GC)>$Nr=B>Q%#OjP~kon z3?@tW84#14Z``EZ0Yc2Sjq5|2-xd>1ucf4V)j$6E-#&lKd}C&`C(L-X4Lr+zVeTMr z@GqGG$}OjY-q}_V>?a4uU|qQ2Vh{U|^KePU2DW)y!2rYlaDN4^paeKpwiNrMCLR>N<-j9{f%A5d^Jgp|knpcyy^2Hjf>ce6ZU(cL6~ zDSIFz)(iSwhyhz=U!l(aUO#^RF8rZ|waA%xfL8p7YT z588>O?QVN zKWGu%7aK$CKMkXE>;vfP$voXPx--pK)S33YZ9tz*5Yw)D9<;aJaH_g8fo{>k>*`5# zy2OLlR~S$emuJ!zTgCMGt!L8xnt}Avl6~|hDqbUy?vP68{PGwYuxdEntS6>b*nR=k z>{H|UEApc`kk=s>LXka`zB~gO64Swaaif1;v`dFi-W|DHO6kbt#=T3`B98eS(>%j8 z;9M;Q|NJ&H6#V;y{>(prjA5mBV%MMk85_`vyf;gNQ&DC=-+wktkNWAKT6I$`QjkA1 z5BWoSIGd`4{GlbtAFBS&AHp?b0{<4-aK!}qL$Syoia`F*ZsZSjLH>{$@`uX4^M^(v ze@N#K_(S=~A3BBnp=jg}u^9O?{*cbb|K#7UQq?ovi~OMl$RE;1{?KmZ4-G~BkotH2 zkm7g#&@AK+wMYJt0rH0&kv}vK`9m|2KlB0lLyNxihhnFh!Gu5H4@Dw>s4wz|rXhc* z`Y-rH&HjJLA7b7)H6;fkY;!^PP7Wk^Bty1q1~_5Ny-RiDBIl<=LzmN_VVe%LAq`;J z5xA1yNr<Gu{ zL=Acn#9z3Jd_Cmj+P4Vi725~%HQj^x{e6RZr>4QY14_FIzRyGvjbQ#xXJpQ!4lWJm z!=%ByFP^cTz9pD%g=&v_uo=(V_6_EhaC{k#JArD6?aHV-F~R)8XBX+Kod@ZBX*oSh z%jv4{gEa1S5WO^RKK1^#hd$33Ld_2O)39zfwBE1-T|1@&-BP7b&pBDrXsvbBue%*h zynT@JUDr{i(g}3jv~_grVSPH_?nCM72upe(^pQ06^bWdBGluR)jXbr3j)<_Lp9buu zEvMVjFKsO87Hs!LZKa38;}7ncc;S=jYj{J<9C0XWuM^Y;WT)h z-RPS?o=wa@J?9Z}qh6b1{^_lR9HZ~^w?D?P(&>6Mu>A957ZI0!yF&2aZ@>8G@DZ^; z{j*n@Y zh3@ne@^4rD!oT(X1OBbFB)en_@^9xO|CYtbpYdg1@dnx@^2?1|MnvCZ;gWumLmVw z6#2JO4B|B!zxzvLr1)+FIC`L}ZT7ymZlP@a`t-~@qx%l3g*9j}t41vT<- zpUVD(f4k?8__u8C*o4jhVji1q$?{s}pOKfcVWeIz#2n56i^oZz*gpfl?n;N#yc=iG zAsuFx7r?apX;3#Wj{EXU%%$*`gxFbY5Dv?T9oKbWXHt=RgVV^r-G%(yk9~9@8u_u-Mw z*9jG1|6Xik^RqpKvqQa}ykz4Ae!^j`Hn`?v37*H2fa^#T@cJcTV&5_Fb3`=ZOi4@| zs}{sZB1>-p%C&P4Z-b?VW)OdF0Ir*r2Jsch#8q-a-!%{7^-%51 zaNR5_d`J+#6WO^jsEm*x{>fbA(I?{P4+Zgw2eA#c6;+ym+}r->$vAF3>JYXuZt-^C_av?xD8^-20`sbUE>0r4w9Ufm!gDE{6 z$$-;oa6_620a-F)`#tac@mvb&Nr(^n=ans~!2FZ7KOPpz+1)+zAI~u6pRr1}K(tv! z$PaqA>Gw1LG^(0P7$28?{-K8VVU4L_MYgNjNR3Kc7d>klR^;l1DMGGeF^j3yx#7hu zetL`Yi*p>5OOn=Tl_&%aD)BhIvE;&#j&$LAPkO~#!RCmqA-(=`t8~U*wzp{su6GWU z?(SSya55r5Dym5+dGaK-1n#Aluo%I(#Ipr$Hy9%sYyTcTp0k6Z(LR7@sc=b$>w$OWBqN?a1CL#+NXeGP zgt&Q-)k#6*T8R=D?5@F$G|qxg*QZHN?>~;Rl}x#QL2|frs8Hw3kBM;{&duVFM;~18 z6OR0$Fq~KIBTjgX??F9wqJQqgvpw75yO&ht3dtd5>J>;ZY{iL?HJG5BitlxOLB3yx z^RatyzLm$bu-oJ3&x)5<DjQ(q z%q7V07z1r@*uz?x5_qNA!l@Q`2Iw^0+OA*$zd4)2RX-!J$khYKp`#$7kieU*d!b<1 zc(^>?4OV{i^<+4ky98dm z@`bSvwu9pITv)hZ2VB4D4|6sKfwk{lphJ~~b?=OM`*+~q&Ox?m{<=V(iAQCi9--{l z2l9hFk->+WxharugXJKfK)zcTau6Z|`Bt&W;!8w^LP#L*hB_A<$e%{3#UMv_PaqGd z9aVvRST-{LZX!$fMj(IVdLVxnbsE(h+m7LQLmbx|bsgK7)Nv2!=79551rE>w4^Gnt zlp|hm2??O%{b$iv5mA(@nki*?=UY&{umG4lXz z*~6W-+b^f_L)_`f=pJ-?Sgn*$Gdkd7t+f0e@^h`CsX3}8Uf(!pMi=Hq(G7OU)_v2T z8eluqxQ}N9|7<=n3t~MR{j*m^I$XoE%xwd`tems|q@p-lx z{gZj<5C1G%(t`^P3lpAcKgO|=`6ny2oEp~&72j;fjUTVVoz4=GU;Q)Tkk{d}?dBG> zLH_M1qDIq!V@NZo*(Bm5Uw>}Qv`M16QoqsE=(?uo%mg{^~olBZ`ZiPBzIA{#OCAOHM6;GaFF ztb>)ayx^^ZHymlQ1{P9haQe=_{qSwLz`u1t{%xkQ1Z2p+Z8fAntVaHACh~8GBLB8J zCks6Mrob%Z-)3ui3jEtkZmv+AuuI_I4n+QKCGu}?7U@ENXJ~dljy~^~7>Cau1#g?X0fX`yTd>W@9jCY!tTR z`rDsE4_+mVF*8#V?)7A}8m=db6z<8sW_L)=!JJkT*Xd5d^^;RrAFlC;6!K5JH)%C~ zQ;E;}0G`PhwH|c=&4*F{{{m22Go7j1FTQQ@dY^U80s~)F*y&xd&XB$ z*RgT5^2;eY`RgefkJl!NJLsp}8T5Q=B&|mNZTiG*G+~hyok~?`mV*kFhGRq z>{(=q+KSIH=Al3Q^LTIVKlIP3{sV;P+7JJX!nKt*bicy<*G}B8{wX=@b=ak)ZBeCe zuFV9?oWlA5S<#juABqx`+Z3~ydenXhzW?;JStN2RCd!$`X@k!d>x&gi^u>lH84)GY zx?V=q*DRh~iM}o!cGX1Yx^j#@15q>7m6j&LBg1LPE{z_Aut zaG<6fM$BjmJ4UqSOs8I!#K!gKLJKx>kI#m1eKU@5Z^?UZZ$)=*?j>K&ZGKBh z&JxejmWXmaIg$2qEy`Ma27@y6oLz76K77<2Rd zTayK?aJ~cEMvZMvD)jY9=Db$I7}oYjT6wYssn5U14ZW>RM(vM=--hXu*Qo=ERsY4L zYrNe*{`r5vKkJ@&LhGz8Fl^`+Sg>sqR6lnCS>`10>(vTo-X9Hv^E4o8)^O;3(FP0^ z0YsMvKuBmm(0?-tmQNiHpRIQ3^Ghx?zEez z)WEqPdRaKmsrD0OW_AtdSW|3L2HOhltZuZE!}GIKSU>hP3fs{?e+u_>R$_k&woMf7 zldH8vs^z65T(hS{tZNsndm@t|51)N`tj~st_2K7;`!Ijn;&do)<`}|<>V@!knuYKS zo8Y>ig~9ym9zpzJRL0vt-W$u!$$|Xw_JRERx&Zzua&0f4Lys*C;7yMO@Q@$C??&}O z2JWGX06rTv9$B^rkbzr*{b!CKmk-qo^*%3v-+^QMp#pH6A4&t;ncidDMa3H0v0oyM zT7Qn}3_nNjMJCW$&w{B-)*N~wIhragwWY33+i69mn2w#TLdRCMq4oE*=zT2{x?H+~ znp#Wg-1_}gf8YvQSM5NLu2?}|lxtBda!EQ<&4kX^za*Xi%$I^Rl8!)qe&S1;-t9-v z%OdFlHz{@LVM493o$0}u62U*ag=E5Ji$?#HW;gn006x2N7KXw}ESY~c{e$NbV4EL4 z$C!uy@XzU+@Wg9;5BmFO*W8|}|K^`*+4nh*fU7X-_2Pf?PmCW~#kES_XW}6``I{$A z@8wMI-G9J$T6LFaF%_X#&9nFk^gGLM%gf@+N)PdU_jvOY3y1JKI{DI;#zxfWMXA)Q zl{QT}X@|UpZ7fD;bFXNr*3vHpN)A!dqdi;m@dLZ@ANncqEJiRc@%-#cJU@GhH1=Ib zj3G}UjtpuIi;{~53w2Is9iE?^LOz7#k&#JZWKrK%S6*Q14Z7PK@Iu9g@R|Xz^$=ts;t(HywmLp-73yWG(R;xdQip7NLJmLXRl7wi`E$ zId=xj7Qc%*?t6XawTT>yx$HWQR8B7y;!00uO4|4DLHxUz z{Go6Dh{xj+HKKU(E_dgo7P)^r0+gb9kfTKdNQ)=)NL;1(AOHL>_fK)F4lpLA3%uQI z2_s_&SY+8l=)6wws<0CrTBroBid{frtO^@icLnaY2JZFLf^*IEKu=}_+l)CFARi8W zawoyJvrgb2vIYjVnhT3>+rj;7GeDU&KS!)h!}*hYck!jGcJiSnJ9)1!VSHFa7~fGTj9+>e*U+NYhXwQf zu}r~rKFuBl@?-o0`5HVc<+2^l%Z|c1SuFRXZVnCPV_gFIz{!F9F4Xy^coqwuzhYG% zz^{51z^kJ=qdq;wT=qGh<$|BtgyS}#+J5g}csGC#OS(lT?M|bfCofQi#TV%6qp37+ zPB`6oW)W4&i=oo4!)V2f0BW3TL!HmIll}uboMUHa2UxDtxY; zM^&R*;`6L8iapCpaPG|juaAGQ6W&>|cUAXrOn)rdv+F>Ijz9f#FxEGQ+~Stw8EI=@ zwiEo*OI9QFWBkYxUMR`lrJbcuYf@<|mpydUvktPHeeGo|rg|x;$yof@&T1_STB0P& z-v5=qzW6+UJtdb<$W6p|#IFnXXX(@XbHk;Z`eqk|9xBARu%#iji=|(Dj}^R7StPZ7 zna;aM%?zJ3n3sQ4<$x?4?l`t^d5y{kyu*k*9+$r*C1?ihLhESKb;-$^!f_a+xL z+7SGGk}NaZ%w0V{845>i5|5vw$-RhF$A2}3oBXpcTgi0ep1FP@-}c9e*2#j$sw-_KuR zo{4)+Md;nXoWH_;FMA~JhknoFpdd97X3Wii;OD6@>%$?inw|#kRj^o)l_8srn zcc||t;FxY7xLJ=h$OENau(DE%sQ0%ZtsYM&rDV4U(24S8t^mX!;BU zo3S#`Ol%ADYumz%VNJlpR~@R}cY&Ht?V$J8_Rx>(03I8)wY^4eJuyp~@$KhidwZ>bi}Cn$&Wr&V_HO>c(q^*2KJ5`W|jVfnUY z2!HEX5PxMt5Wn0gh>yc{Mk~AGx*+u+ehGh6ad-Wh@X>{o&OCw$LY z8x+V#prTOQP(Iieho4EraS5nh*v7OM-?N_IdWY^>l|j27xJ1tky+k*LrPEBy2pV9v zlxi-DrPXdD>DSK4KlsekX?@z$m@0KTVWTc>?rKgKyjVpSU$&)PNj!a3vx?4LJD#5D zv6?<`*QFy)UXzBjHK%^BYow2+`_UD-QFJwG5?=Qy?@!}`qiMR%AewsCoG!z5CYkaCX%RK<4h{tmp6Ll;wv6|70ce&qL0KKxJJbY*>&4dxs~(D%)(>JNF2v z;PZ+3r#6;4sAkGT2+I@B^*Rh@9dh8%x;)TLlaULn4?`F})7bp&ZwK&sSQZNGoKVJ4 zVDq<+7Ea(am+^%8XN%Y-GNavRXvgQ7bZo-`I-yBtSucf7G8R)UoZBJ4Z-dPsZ#CKA zH7#YAp_wc|dXo=OIKzKEltwG;Gm4syv!+{=zuD9$DHV;KRf%!YjVkYzNZ(xvDG&`1 zOLJaT@}{#d@q@C?@+?L$E^+Ut8}2Ptk-pxTjxp2?<0^tY`<7DVV)0a{A3fLs#L`KhBvrK|^!+k!>zP`jKIR%*=L5%^UnY-BE=Ug4Rgoun2AcWDV$#qfhDa9% zkvYBEk^$@0IY-G(&ScIrds8+y;^-6G5}PFQ|5Cuoim} z^W!T}KU<0U@sm>oU)dS=e>N5Cw;QS?9-1V7%I+<!6yq=U;B$|>*v!$dl+L$;>+v3_=Q*vBoMgqU-MRu15kztsD zYyNU6$#+;luG}6)4v!g0d}?PA^`$~wA2f?NW_KVe=MLm-3o~N=+MLj7A*5yLByun* zf?Tm)L#}m6C1*{>kTti)63zZ(@p#;}Ry1&+9ToI*XTMr`p;aIJJztBOS&RLjR^d2S zo<8-pI4>OEseXdz#|qcrx<)N<9=+yoz~B8##A{BMw-N8`3L05xbp$gO zy2$MVreJpI6yY2Q5YFMfA?2%FUNez#QHHpK$L07PBfQQKQHgT`o_eD8Zv2Tw#_VBz4qRkigDx_w9eJ$U zKb7Ur3TNF`Z)KSW2eRhw@yvPv<|nLc$DF-PnC5L~cK?wJlZMS_3;aD<^!!XVPBov^ zKOezXuAk4gKIzO_N@^7~DK2aSy`dPHAH&YK-o!2=3iigZ>C>E<$zM0IunnGUoL4V) z4(A07`R)wFLvMVBHDCRV%nIy4BS$|YOrzD$d%ORppQp2w@S%QQKRlh< zSb(UXXTo%pihkuREIp4sh}p_4N48fkmbX(1oGR`wMgO@<-q*!YIWx9}a#(8}Ws{~g zyh+m|Jo!it+jnMB$rXKEV=U>VnB3z?ah*ds@}djt_R9c;d3oBQ;t+2|%2&tuR_x3+ zzqXVM96?^9M_r;2_(d8@kV9RNR~eAgm{GFYPa<-xJV}N;9V_bR^})sPw$2u!N~N%T z=2Y6RvIhFNUxp{PhNOCKCEWbtmPcBXvyf0%24%+%fqk2eU?z=)w-dgAe6vcDb}x>0 zy`@9?ILCVQJ!?xxe*G&6oV1-dHEHf)-EA9rb!Ih`4~mC4c?{gIjDW@Zg`~~u%S0_* zK*oJ*SuHs9Pzz|{&N@BZ|#Vr=UIpue;W1E z)-7UTDgJ-By45rsfcPVspvuK{>e->Fhwh*<$wL1*!9&wnhWfcesJZs88QPL`-!Dh+ z*fA~-KB6V zKnE@%FQeL+!+;J>Fu&PIs6N~Ivwr>;=x0h$Them76LGZYMG6LSBAsnSDmNLCK|ZZW zU{5J=UT8t8Go|GI73^a+r7f{fH6?F#&51I)7nys00%^XbKRIQxm=rEaAWPN;kb1M> zB;eI_Qk$GgVq?R}pdZZ0cYiS>zV_ziMwlguWkKZR#4z%uX9PL-D4k5`nMjs4DI#B& z1e3_C+T$pChjc2@7xh!{oS%ySY;;=BpX;S?yRu~dQ%o{nvLczMqaVEdU~W@9C%ehI8s)QWTE#~Gc!?Qx-^=*f^=xPG zESBATJA1TsFpJJnvn}1cSj4CftVm+Y!gISY*~Z>%*`F4&=;q!m<6;)mZ@rL3X#&`t zfeV>VMi;iC^DRZ5yf-`He^W8*Wh`qt5chi`?l#7<{x5qm=P%P(g}*mD^{O|M;Jko2 zQRjvAS>n2wy9VB(j&99Tz|KlJ+|H7Lpr67h=x4jlGFaMC2Da&zV4~88aDxX_xBq5( zYIXtPr%Xlv`S4f{u|xfI!Drd@#~K>f{xF@=E{nd&RKm%|RBFFVtDmQP93)xzEc;MD zWo;+JUUQE>2s^F+tm&lnpPGFi`%g*4MGw{9 z3W9rA1Pye1?>}b-YyGF)%y<5?;hq1C`PhGI*AEH)Q_xSre+v5fxAjBs`6i4zOWolX zc0XF>r~u!MPB8e_%~ar9Rnb)X^L7~&gAAgotf6|Fg~&OJ9_6&i^&Sn&Sw;pwNdZOL2o zpQq7(%F%z;KMx}82QTu#Z7n&rD~=Su^PicvTK_rIS?fRZyKDWYsjkIG{&QYr$vgjf zowOS@mJA)QJ^s*teoFf3_hLQwIhVrcuSnspLsIx<-xU6QP%`gnlfrZJsdmv zCUV1O30xO*{L-eVd8NOaZyl}XHA69X4>29rAoZH8=5-S>M+mX^Sv*g0!8}~lTfM4y zeggB1P9yds^6~frey0Ynt418hIRO>NP+w=?Vh&#yu$=g6HrlnCnJmd?{cJa~0)x3M zXH*7j^dHLB=q9r4OWtgG3k!B6+lc7NYVp`wmHT^b7a?D1Vtv_jNRVa%}cqPCF=xOzbjOPndp?dSr{pE$tw!7}LI zRYNB_pw=$UA%cEhGU+esXR|(?z)@lc-?2_0=;cZ5w_=0OFQI-rZRm3{2A^G-a%~Tm zK2J-ipr7~3-Tsh%eptUP$NH@f>gV^UpTB&9_1m7PpTkf;|APAY{lBq(`>E>Zd%g+d zOJ5b-AHdvNE9$APY&}diO9J#W4gkc#soShR0`vQ zU(xzmmSFQuJhhryP8-9euw#rl?71L?X+L!q`6+Nv;NPBGmDH{0Zt+<^{|o%5P`~~E=;w#^+u1LI$aK`t9MsRIIdQ+$ zZ~usXy8bWv*|6Of>$jgqe!|^f7xCgP8~7Z*RBrJ$g_oX4;it<|xTZLnkBUs<_6QxP zB>rYm5-;7A$V28QaH%YTn>FpMZ#Lv>~1YuIDvehZTX(j0hMsc&^w7$7b&wIAJpfQV)fS`YDV;j>5&lP}tWp6t2z-gker9 zxE;ly|2a9FPPc(^sGs}VET(&VZl>?`GqKtgs{6Xbh&8=Iy5!k!`YH4(dmNKV1-*T* zpSBr$MEx9|?FPsnrC%jE%kcCrie7Ag!|n6t?}?kiLwE9LvU9zc)%ael(x8Jqc=kPN03_^zdNS%_xTAoBf}sqE*x$< zhQPOnJ^o>SLeJUP$?x+M)K{^;%CH@3=qn=rO_pA6R517LRW9C*=d z8tj(erUKs<@9$2NibCO3NGKd_5(4_BTFx~%c*1#E7WFlA1HC*6qfrA47)R(LwpneIx(tkM}KD{US-0uhn9REtk|4>JK2X_J2 zHr*hoU@++45o*z&`>*_S8MwmRvrqTqI^K}THy9I{tOrs5=t3Ncl6XsuNRg|Nn4fU4 zrBuvMD9x3UpC`8^7rjhK$+iweHp!BlyVi>sdQKz_3O{oES_m0`E{R<89YYFIwD}3$ z4KhgC<*$iJ#-B*ntyW~qR~?Dt^-dqk literal 0 HcmV?d00001 diff --git a/tests/epanet-nrtestsuite/benchmark/epanet-2012/Example_2/example2.rpt b/tests/epanet-nrtestsuite/benchmark/epanet-2012/Example_2/example2.rpt new file mode 100644 index 0000000..6f3512c --- /dev/null +++ b/tests/epanet-nrtestsuite/benchmark/epanet-2012/Example_2/example2.rpt @@ -0,0 +1,5176 @@ + Page 1 Fri Oct 13 13:50:29 2017 + + ****************************************************************** + * E P A N E T * + * Hydraulic and Water Quality * + * Analysis for Pipe Networks * + * Version 2.00.12 * + ****************************************************************** + + Analysis begun Fri Oct 13 13:50:29 2017 + + + Hydraulic Status: + ----------------------------------------------------------------------- + 0:00:00: Balanced after 5 trials + 0:00:00: Tank 26 is filling at 56.70 ft + + 1:00:00: Balanced after 3 trials + + 2:00:00: Balanced after 2 trials + + 3:00:00: Balanced after 1 trials + + 4:00:00: Balanced after 2 trials + + 5:00:00: Balanced after 3 trials + + 6:00:00: Balanced after 3 trials + + 7:00:00: Balanced after 4 trials + 7:00:00: Tank 26 is emptying at 64.78 ft + + 8:00:00: Balanced after 1 trials + + 9:00:00: Balanced after 2 trials + + 10:00:00: Balanced after 2 trials + + 11:00:00: Balanced after 2 trials + + 12:00:00: Balanced after 4 trials + 12:00:00: Tank 26 is filling at 56.72 ft + + 13:00:00: Balanced after 3 trials + + 14:00:00: Balanced after 3 trials + + 15:00:00: Balanced after 3 trials + + 16:00:00: Balanced after 2 trials + + 17:00:00: Balanced after 4 trials + 17:00:00: Tank 26 is emptying at 65.13 ft + + 18:00:00: Balanced after 3 trials + + 19:00:00: Balanced after 2 trials + + 20:00:00: Balanced after 2 trials + + 21:00:00: Balanced after 2 trials + + 22:00:00: Balanced after 2 trials + + 23:00:00: Balanced after 2 trials + + 24:00:00: Balanced after 4 trials + 24:00:00: Tank 26 is filling at 56.20 ft + + 25:00:00: Balanced after 2 trials + + 26:00:00: Balanced after 2 trials + + 27:00:00: Balanced after 3 trials + + 28:00:00: Balanced after 2 trials + + 29:00:00: Balanced after 3 trials + + 30:00:00: Balanced after 2 trials + + 31:00:00: Balanced after 5 trials + 31:00:00: Tank 26 is emptying at 64.88 ft + + 32:00:00: Balanced after 4 trials + + 33:00:00: Balanced after 2 trials + + 34:00:00: Balanced after 2 trials + + 35:00:00: Balanced after 2 trials + + 36:00:00: Balanced after 2 trials + + 37:00:00: Balanced after 4 trials + 37:00:00: Tank 26 is filling at 56.52 ft + + 38:00:00: Balanced after 3 trials + + 39:00:00: Balanced after 3 trials + + 40:00:00: Balanced after 2 trials + + 41:00:00: Balanced after 2 trials + + 42:00:00: Balanced after 3 trials + 42:00:00: Tank 26 is emptying at 64.83 ft + + 43:00:00: Balanced after 3 trials + + 44:00:00: Balanced after 2 trials + + 45:00:00: Balanced after 2 trials + + 46:00:00: Balanced after 2 trials + + 47:00:00: Balanced after 2 trials + + 48:00:00: Balanced after 2 trials + + 49:00:00: Balanced after 4 trials + 49:00:00: Tank 26 is filling at 56.48 ft + + 50:00:00: Balanced after 3 trials + + 51:00:00: Balanced after 3 trials + + 52:00:00: Balanced after 2 trials + + 53:00:00: Balanced after 2 trials + + 54:00:00: Balanced after 1 trials + + 55:00:00: Balanced after 3 trials + + + Node Results at 0:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Fluoride + Node gpm ft psi mg/L + -------------------------------------------------------- + 1 -666.62 309.88 112.61 1.00 + 2 10.08 305.22 88.92 1.00 + 3 17.64 304.59 105.98 1.00 + 4 10.08 304.17 105.80 1.00 + 5 10.08 304.13 88.45 1.00 + 6 6.30 302.10 76.74 1.00 + 7 5.04 297.62 59.63 1.00 + 8 11.34 297.61 81.29 1.00 + 9 17.64 297.00 50.69 1.00 + 10 6.30 297.61 72.63 1.00 + 11 43.82 295.97 48.08 1.00 + 12 20.16 293.57 36.21 1.00 + 13 2.52 292.86 35.90 1.00 + 14 2.52 292.54 40.10 1.00 + 15 2.52 292.35 44.35 1.00 + 16 25.20 292.38 61.69 1.00 + 17 25.20 292.33 48.67 1.00 + 18 25.20 292.33 83.34 1.00 + 19 6.30 292.34 61.67 1.00 + 20 23.94 292.51 53.08 1.00 + 21 20.16 292.49 61.74 1.00 + 22 12.60 292.49 40.07 1.00 + 23 10.08 291.91 26.83 1.00 + 24 13.86 292.22 44.29 1.00 + 25 7.56 291.77 26.76 1.00 + 27 10.08 291.75 70.09 1.00 + 28 0.00 291.74 78.75 1.00 + 29 8.82 291.74 78.75 1.00 + 30 3.78 291.74 70.08 1.00 + 31 21.42 291.76 44.09 1.00 + 32 21.42 292.33 79.00 1.00 + 33 1.89 292.49 48.74 1.00 + 34 1.89 292.49 44.41 1.00 + 35 0.00 291.74 78.75 1.00 + 36 1.26 291.74 78.75 1.00 + 26 259.92 291.70 24.57 1.00 Tank + + + Link Results at 0:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 1 666.62 1.89 1.94 + 2 548.36 1.56 1.35 + 3 108.18 0.69 0.48 + 4 90.54 0.58 0.35 + 5 80.46 0.23 0.04 + 6 618.74 1.76 1.69 + 7 612.44 1.74 1.66 + 8 17.64 0.05 0.00 + 9 589.76 1.67 1.55 + 10 6.30 0.04 0.00 + 11 572.12 1.62 1.46 + 12 528.30 1.50 1.26 + 13 508.14 1.44 1.18 + 14 418.27 1.19 0.82 + 15 355.27 1.01 0.61 + 16 87.35 0.56 0.33 + 17 15.97 0.10 0.01 + 18 38.76 0.25 0.07 + 19 29.52 0.08 0.01 + 20 4.32 0.01 0.00 + 21 23.40 0.15 0.03 + 22 60.48 0.17 0.02 + 23 18.34 0.12 0.02 + 24 -1.82 0.01 0.00 + 25 18.20 0.12 0.02 + 26 322.92 0.92 0.51 + 27 336.78 0.96 0.55 + 28 312.84 0.89 0.48 + 29 259.92 0.74 0.34 + 30 45.36 0.13 0.01 + 31 23.94 0.15 0.03 + 32 13.86 0.09 0.01 + 34 2.57 0.02 0.00 + 35 3.78 0.02 0.00 + 36 1.89 0.01 0.00 + 37 -17.10 0.11 0.02 + 38 2.47 0.02 0.00 + 39 3.78 0.02 0.00 + 40 1.31 0.01 0.00 + 41 1.26 0.01 0.00 + + + Node Results at 1:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Fluoride + Node gpm ft psi mg/L + -------------------------------------------------------- + 1 -666.62 311.83 113.45 0.98 + 2 8.32 307.16 89.76 0.98 + 3 14.56 306.54 106.83 0.98 + 4 8.32 306.11 106.64 1.00 + 5 8.32 306.07 89.29 0.98 + 6 5.20 303.98 77.55 0.98 + 7 4.16 299.37 60.39 1.00 + 8 9.36 299.37 82.05 1.00 + 9 14.56 298.72 51.44 1.00 + 10 5.20 299.36 73.39 1.00 + 11 36.17 297.64 48.81 1.00 + 12 16.64 295.03 36.84 1.00 + 13 2.08 294.25 36.51 1.00 + 14 2.08 293.89 40.68 1.00 + 15 2.08 293.67 44.92 1.00 + 16 20.80 293.73 62.28 1.00 + 17 20.80 293.67 49.25 1.00 + 18 20.80 293.67 83.92 1.00 + 19 5.20 293.68 62.26 1.00 + 20 19.76 293.87 53.67 1.00 + 21 16.64 293.85 62.33 1.00 + 22 10.40 293.85 40.67 1.00 + 23 8.32 293.07 27.33 1.00 + 24 11.44 293.49 44.84 1.00 + 25 6.24 292.87 27.24 1.00 + 27 8.32 292.85 70.56 1.00 + 28 0.00 292.85 79.23 1.00 + 29 7.28 292.85 79.23 1.00 + 30 3.12 292.85 70.56 1.00 + 31 17.68 292.86 44.57 1.00 + 32 17.68 293.67 79.58 1.00 + 33 1.56 293.85 49.33 1.00 + 34 1.56 293.85 45.00 1.00 + 35 0.00 292.85 79.23 1.00 + 36 1.04 292.85 79.23 1.00 + 26 330.93 292.76 25.03 1.00 Tank + + + Link Results at 1:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 1 666.62 1.89 1.94 + 2 551.15 1.56 1.37 + 3 107.16 0.68 0.47 + 4 92.60 0.59 0.36 + 5 84.28 0.24 0.04 + 6 627.10 1.78 1.74 + 7 621.90 1.76 1.71 + 8 14.56 0.04 0.00 + 9 603.18 1.71 1.62 + 10 5.20 0.03 0.00 + 11 588.62 1.67 1.54 + 12 552.45 1.57 1.37 + 13 535.81 1.52 1.30 + 14 442.98 1.26 0.91 + 15 390.98 1.11 0.72 + 16 90.76 0.58 0.35 + 17 -5.48 0.03 0.00 + 18 44.44 0.28 0.09 + 19 18.17 0.05 0.00 + 20 -2.63 0.01 0.00 + 21 25.51 0.16 0.03 + 22 49.92 0.14 0.02 + 23 15.14 0.10 0.01 + 24 -1.50 0.01 0.00 + 25 15.02 0.10 0.01 + 26 382.93 1.09 0.70 + 27 394.37 1.12 0.74 + 28 374.61 1.06 0.67 + 29 330.93 0.94 0.53 + 30 37.44 0.11 0.01 + 31 19.76 0.13 0.02 + 32 11.44 0.07 0.01 + 34 1.79 0.01 0.00 + 35 3.12 0.02 0.00 + 36 1.56 0.01 0.00 + 37 -20.31 0.13 0.02 + 38 2.37 0.02 0.00 + 39 3.12 0.02 0.00 + 40 0.75 0.00 0.00 + 41 1.04 0.01 0.00 + + + Node Results at 2:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Fluoride + Node gpm ft psi mg/L + -------------------------------------------------------- + 1 -666.62 313.47 114.16 1.02 + 2 7.76 308.80 90.47 1.02 + 3 13.58 308.19 107.54 1.02 + 4 7.76 307.75 107.35 0.98 + 5 7.76 307.71 90.00 1.02 + 6 4.85 305.61 78.26 1.02 + 7 3.88 300.95 61.07 0.98 + 8 8.73 300.95 82.74 1.00 + 9 13.58 300.29 52.12 0.98 + 10 4.85 300.95 74.07 1.00 + 11 33.74 299.20 49.48 0.98 + 12 15.52 296.52 37.49 0.98 + 13 1.94 295.72 37.14 0.98 + 14 1.94 295.34 41.31 0.98 + 15 1.94 295.11 45.54 0.99 + 16 19.40 295.18 62.91 1.00 + 17 19.40 295.12 49.88 1.00 + 18 19.40 295.12 84.55 1.00 + 19 4.85 295.14 62.89 1.00 + 20 18.43 295.33 54.30 1.00 + 21 15.52 295.31 62.96 1.00 + 22 9.70 295.31 41.30 1.00 + 23 7.76 294.45 27.93 1.00 + 24 10.67 294.91 45.46 1.00 + 25 5.82 294.23 27.83 1.00 + 27 7.76 294.22 71.16 1.00 + 28 0.00 294.22 79.82 1.00 + 29 6.79 294.22 79.82 1.00 + 30 2.91 294.22 71.16 1.00 + 31 16.49 294.23 45.16 1.00 + 32 16.49 295.12 80.21 1.00 + 33 1.45 295.31 49.96 1.00 + 34 1.45 295.31 45.63 1.00 + 35 0.00 294.22 79.82 1.00 + 36 0.97 294.22 79.82 1.00 + 26 353.53 294.11 25.61 1.00 Tank + + + Link Results at 2:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 1 666.62 1.89 1.94 + 2 552.04 1.57 1.37 + 3 106.83 0.68 0.47 + 4 93.25 0.60 0.37 + 5 85.49 0.24 0.04 + 6 629.76 1.79 1.75 + 7 624.91 1.77 1.72 + 8 13.58 0.04 0.00 + 9 607.45 1.72 1.64 + 10 4.85 0.03 0.00 + 11 593.87 1.68 1.57 + 12 560.14 1.59 1.41 + 13 544.62 1.54 1.34 + 14 450.95 1.28 0.94 + 15 402.45 1.14 0.76 + 16 91.73 0.59 0.36 + 17 -12.19 0.08 0.01 + 18 46.13 0.29 0.10 + 19 14.55 0.04 0.00 + 20 -4.85 0.01 0.00 + 21 26.19 0.17 0.03 + 22 46.56 0.13 0.01 + 23 14.12 0.09 0.01 + 24 -1.40 0.01 0.00 + 25 14.01 0.09 0.01 + 26 402.03 1.14 0.76 + 27 412.70 1.17 0.80 + 28 394.27 1.12 0.74 + 29 353.53 1.00 0.60 + 30 34.92 0.10 0.01 + 31 18.43 0.12 0.02 + 32 10.67 0.07 0.01 + 34 1.67 0.01 0.00 + 35 2.91 0.02 0.00 + 36 1.45 0.01 0.00 + 37 -21.34 0.14 0.02 + 38 2.21 0.01 0.00 + 39 2.91 0.02 0.00 + 40 0.70 0.00 0.00 + 41 0.97 0.01 0.00 + + + Node Results at 3:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Fluoride + Node gpm ft psi mg/L + -------------------------------------------------------- + 1 -666.62 314.91 114.79 1.05 + 2 7.76 310.25 91.10 1.05 + 3 13.58 309.63 108.17 1.05 + 4 7.76 309.19 107.98 1.02 + 5 7.76 309.15 90.63 1.04 + 6 4.85 307.05 78.88 1.04 + 7 3.88 302.39 61.70 1.02 + 8 8.73 302.39 83.36 1.00 + 9 13.58 301.74 52.75 1.02 + 10 4.85 302.39 74.70 1.00 + 11 33.74 300.64 50.11 1.02 + 12 15.52 297.96 38.11 1.02 + 13 1.94 297.16 37.77 1.02 + 14 1.94 296.78 41.94 1.02 + 15 1.94 296.56 46.17 1.01 + 16 19.40 296.63 63.53 0.98 + 17 19.40 296.57 50.51 1.00 + 18 19.40 296.57 85.17 1.00 + 19 4.85 296.58 63.51 1.00 + 20 18.43 296.77 54.93 1.00 + 21 15.52 296.75 63.59 1.00 + 22 9.70 296.76 41.92 1.00 + 23 7.76 295.90 28.55 0.98 + 24 10.67 296.36 46.08 1.00 + 25 5.82 295.68 28.46 0.98 + 27 7.76 295.67 71.78 1.00 + 28 0.00 295.66 80.45 1.00 + 29 6.79 295.66 80.45 1.00 + 30 2.91 295.66 71.78 1.00 + 31 16.49 295.67 45.79 1.00 + 32 16.49 296.57 80.84 1.00 + 33 1.45 296.75 50.59 1.00 + 34 1.45 296.75 46.26 1.00 + 35 0.00 295.66 80.45 1.00 + 36 0.97 295.66 80.45 1.00 + 26 353.53 295.56 26.24 1.00 Tank + + + Link Results at 3:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 1 666.62 1.89 1.94 + 2 552.04 1.57 1.37 + 3 106.83 0.68 0.47 + 4 93.25 0.60 0.37 + 5 85.49 0.24 0.04 + 6 629.76 1.79 1.75 + 7 624.91 1.77 1.72 + 8 13.58 0.04 0.00 + 9 607.45 1.72 1.64 + 10 4.85 0.03 0.00 + 11 593.87 1.68 1.57 + 12 560.14 1.59 1.41 + 13 544.62 1.54 1.34 + 14 450.95 1.28 0.94 + 15 402.45 1.14 0.76 + 16 91.72 0.59 0.36 + 17 -12.18 0.08 0.01 + 18 46.13 0.29 0.10 + 19 14.55 0.04 0.00 + 20 -4.85 0.01 0.00 + 21 26.19 0.17 0.03 + 22 46.56 0.13 0.01 + 23 14.12 0.09 0.01 + 24 -1.40 0.01 0.00 + 25 14.01 0.09 0.01 + 26 402.03 1.14 0.76 + 27 412.70 1.17 0.80 + 28 394.27 1.12 0.74 + 29 353.53 1.00 0.60 + 30 34.92 0.10 0.01 + 31 18.43 0.12 0.02 + 32 10.67 0.07 0.01 + 34 1.67 0.01 0.00 + 35 2.91 0.02 0.00 + 36 1.45 0.01 0.00 + 37 -21.34 0.14 0.02 + 38 2.21 0.01 0.00 + 39 2.91 0.02 0.00 + 40 0.70 0.00 0.00 + 41 0.97 0.01 0.00 + + + Node Results at 4:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Fluoride + Node gpm ft psi mg/L + -------------------------------------------------------- + 1 -666.62 316.70 115.56 0.99 + 2 7.12 312.03 91.87 0.99 + 3 12.46 311.43 108.94 0.99 + 4 7.12 310.98 108.75 1.05 + 5 7.12 310.93 91.40 0.99 + 6 4.45 308.82 79.65 0.99 + 7 3.56 304.11 62.44 1.04 + 8 8.01 304.11 84.11 1.00 + 9 12.46 303.45 53.49 1.04 + 10 4.45 304.11 75.44 1.00 + 11 30.95 302.33 50.84 1.04 + 12 14.24 299.57 38.81 1.04 + 13 1.78 298.74 38.45 1.04 + 14 1.78 298.35 42.62 1.04 + 15 1.78 298.11 46.84 1.04 + 16 17.80 298.20 64.22 1.02 + 17 17.80 298.14 51.19 0.99 + 18 17.80 298.14 85.85 1.00 + 19 4.45 298.15 64.19 1.00 + 20 16.91 298.34 55.61 1.00 + 21 14.24 298.32 64.27 1.00 + 22 8.90 298.32 42.60 1.00 + 23 7.12 297.38 29.20 1.02 + 24 9.79 297.89 46.75 1.03 + 25 5.34 297.14 29.09 1.02 + 27 7.12 297.13 72.42 1.00 + 28 0.00 297.13 81.08 1.00 + 29 6.23 297.13 81.08 1.00 + 30 2.67 297.13 72.42 1.00 + 31 15.13 297.13 46.42 0.99 + 32 15.13 298.14 81.52 1.00 + 33 1.34 298.32 51.27 1.00 + 34 1.34 298.32 46.94 1.00 + 35 0.00 297.13 81.08 1.00 + 36 0.89 297.13 81.08 1.00 + 26 379.35 297.00 26.87 1.00 Tank + + + Link Results at 4:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 1 666.62 1.89 1.94 + 2 553.06 1.57 1.38 + 3 106.45 0.68 0.47 + 4 93.99 0.60 0.37 + 5 86.87 0.25 0.04 + 6 632.80 1.80 1.77 + 7 628.35 1.78 1.74 + 8 12.46 0.04 0.00 + 9 612.33 1.74 1.66 + 10 4.45 0.03 0.00 + 11 599.87 1.70 1.60 + 12 568.92 1.61 1.45 + 13 554.68 1.57 1.38 + 14 460.46 1.31 0.98 + 15 415.96 1.18 0.81 + 16 92.44 0.59 0.36 + 17 -19.46 0.12 0.02 + 18 47.79 0.31 0.11 + 19 10.53 0.03 0.00 + 20 -7.27 0.02 0.00 + 21 26.85 0.17 0.04 + 22 42.72 0.12 0.01 + 23 12.95 0.08 0.01 + 24 -1.29 0.01 0.00 + 25 12.86 0.08 0.01 + 26 423.85 1.20 0.84 + 27 433.64 1.23 0.88 + 28 416.73 1.18 0.81 + 29 379.35 1.08 0.68 + 30 32.04 0.09 0.01 + 31 16.91 0.11 0.02 + 32 9.79 0.06 0.01 + 34 1.53 0.01 0.00 + 35 2.67 0.02 0.00 + 36 1.34 0.01 0.00 + 37 -22.40 0.14 0.03 + 38 2.03 0.01 0.00 + 39 2.67 0.02 0.00 + 40 0.64 0.00 0.00 + 41 0.89 0.01 0.00 + + + Node Results at 5:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Fluoride + Node gpm ft psi mg/L + -------------------------------------------------------- + 1 -666.62 317.01 115.70 0.64 + 2 9.52 312.34 92.01 0.64 + 3 16.66 311.72 109.07 0.67 + 4 9.52 311.30 108.89 0.99 + 5 9.52 311.26 91.54 0.70 + 6 5.95 309.21 79.82 0.70 + 7 4.76 304.68 62.69 0.99 + 8 10.71 304.68 84.35 1.00 + 9 16.66 304.05 53.75 0.99 + 10 5.95 304.68 75.69 1.00 + 11 41.39 303.01 51.13 0.99 + 12 19.04 300.54 39.23 1.00 + 13 2.38 299.82 38.92 1.00 + 14 2.38 299.48 43.10 1.00 + 15 2.38 299.28 47.35 1.00 + 16 23.80 299.32 64.70 1.04 + 17 23.80 299.27 51.68 1.03 + 18 23.80 299.27 86.34 1.00 + 19 5.95 299.28 64.68 0.98 + 20 22.61 299.45 56.09 0.98 + 21 19.04 299.43 64.75 1.00 + 22 11.90 299.43 43.08 1.00 + 23 9.52 298.79 29.81 1.04 + 24 13.09 299.13 47.29 1.02 + 25 7.14 298.63 29.74 1.04 + 27 9.52 298.61 73.06 0.99 + 28 0.00 298.61 81.72 1.00 + 29 8.33 298.61 81.72 1.00 + 30 3.57 298.61 73.06 1.00 + 31 20.23 298.62 47.07 1.00 + 32 20.23 299.27 82.01 1.00 + 33 1.78 299.43 51.75 1.00 + 34 1.78 299.43 47.42 1.00 + 35 0.00 298.61 81.72 1.00 + 36 1.19 298.61 81.72 1.00 + 26 282.52 298.55 27.54 1.00 Tank + + + Link Results at 5:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 1 666.62 1.89 1.94 + 2 549.25 1.56 1.36 + 3 107.86 0.69 0.48 + 4 91.20 0.58 0.35 + 5 81.68 0.23 0.04 + 6 621.40 1.76 1.71 + 7 615.45 1.75 1.68 + 8 16.66 0.05 0.00 + 9 594.03 1.69 1.57 + 10 5.95 0.04 0.00 + 11 577.37 1.64 1.49 + 12 535.99 1.52 1.30 + 13 516.95 1.47 1.21 + 14 426.52 1.21 0.85 + 15 367.02 1.04 0.64 + 16 88.04 0.56 0.33 + 17 9.54 0.06 0.01 + 18 40.33 0.26 0.08 + 19 26.07 0.07 0.00 + 20 2.27 0.01 0.00 + 21 23.91 0.15 0.03 + 22 57.12 0.16 0.02 + 23 17.32 0.11 0.02 + 24 -1.72 0.01 0.00 + 25 17.19 0.11 0.02 + 26 342.02 0.97 0.56 + 27 355.11 1.01 0.61 + 28 332.50 0.94 0.54 + 29 282.52 0.80 0.40 + 30 42.84 0.12 0.01 + 31 22.61 0.14 0.03 + 32 13.09 0.08 0.01 + 34 2.05 0.01 0.00 + 35 3.57 0.02 0.00 + 36 1.78 0.01 0.00 + 37 -17.96 0.11 0.02 + 38 2.71 0.02 0.00 + 39 3.57 0.02 0.00 + 40 0.86 0.01 0.00 + 41 1.19 0.01 0.00 + + + Node Results at 6:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Fluoride + Node gpm ft psi mg/L + -------------------------------------------------------- + 1 -430.53 306.69 111.22 0.46 + 2 10.24 304.61 88.66 0.46 + 3 17.92 304.32 105.86 0.47 + 4 10.24 304.16 105.79 0.64 + 5 10.24 304.15 88.46 0.53 + 6 6.40 303.31 77.26 0.53 + 7 5.12 301.50 61.31 0.69 + 8 11.52 301.50 82.98 1.00 + 9 17.92 301.26 52.54 0.69 + 10 6.40 301.50 74.31 1.00 + 11 44.52 300.88 50.21 0.69 + 12 20.48 300.09 39.04 0.70 + 13 2.56 299.87 38.94 0.70 + 14 2.56 299.79 43.24 0.74 + 15 2.56 299.75 47.56 0.82 + 16 25.60 299.63 64.83 1.00 + 17 25.60 299.61 51.83 1.03 + 18 25.60 299.61 86.49 1.00 + 19 6.40 299.61 64.83 1.02 + 20 24.32 299.76 56.22 1.03 + 21 20.48 299.74 64.88 1.00 + 22 12.80 299.74 43.22 1.00 + 23 10.24 299.72 30.21 0.99 + 24 14.08 299.74 47.55 0.92 + 25 7.68 299.71 30.20 0.99 + 27 10.24 299.69 73.52 1.02 + 28 0.00 299.68 82.19 1.00 + 29 8.96 299.68 82.19 1.00 + 30 3.84 299.68 73.52 1.00 + 31 21.76 299.70 47.53 1.04 + 32 21.76 299.61 82.16 1.00 + 33 1.92 299.74 51.88 1.00 + 34 1.92 299.73 47.55 1.00 + 35 0.00 299.68 82.19 1.00 + 36 1.28 299.68 82.19 1.00 + 26 17.37 299.71 28.04 1.00 Tank + + + Link Results at 6:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 1 430.53 1.22 0.87 + 2 348.35 0.99 0.58 + 3 71.93 0.46 0.23 + 4 54.01 0.34 0.13 + 5 43.77 0.12 0.01 + 6 381.89 1.08 0.69 + 7 375.49 1.07 0.67 + 8 17.92 0.05 0.00 + 9 352.45 1.00 0.60 + 10 6.40 0.04 0.00 + 11 334.53 0.95 0.54 + 12 290.01 0.82 0.42 + 13 269.53 0.76 0.36 + 14 206.32 0.59 0.22 + 15 142.32 0.40 0.11 + 16 60.65 0.39 0.17 + 17 44.31 0.28 0.09 + 18 19.91 0.13 0.02 + 19 38.61 0.11 0.01 + 20 13.01 0.04 0.00 + 21 15.15 0.10 0.01 + 22 61.44 0.17 0.02 + 23 18.63 0.12 0.02 + 24 -1.85 0.01 0.00 + 25 18.49 0.12 0.02 + 26 81.37 0.23 0.04 + 27 95.45 0.27 0.05 + 28 71.13 0.20 0.03 + 29 17.37 0.05 0.00 + 30 46.08 0.13 0.01 + 31 24.32 0.16 0.03 + 32 14.08 0.09 0.01 + 34 2.20 0.01 0.00 + 35 3.84 0.02 0.00 + 36 1.92 0.01 0.00 + 37 -8.75 0.06 0.00 + 38 2.92 0.02 0.00 + 39 3.84 0.02 0.00 + 40 0.92 0.01 0.00 + 41 1.28 0.01 0.00 + + + Node Results at 7:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Fluoride + Node gpm ft psi mg/L + -------------------------------------------------------- + 1 0.00 299.34 108.04 0.35 + 2 5.36 299.34 86.37 0.35 + 3 9.38 299.34 103.71 0.46 + 4 5.36 299.34 103.71 0.47 + 5 5.36 299.34 86.38 0.42 + 6 3.35 299.35 75.55 0.52 + 7 2.68 299.36 60.39 0.52 + 8 6.03 299.36 82.05 1.00 + 9 9.38 299.37 51.72 0.52 + 10 3.35 299.36 73.39 1.00 + 11 23.30 299.38 49.56 0.53 + 12 10.72 299.44 38.76 0.69 + 13 1.34 299.47 38.77 0.69 + 14 1.34 299.49 43.11 0.69 + 15 1.34 299.53 47.46 0.69 + 16 13.40 299.44 64.75 0.71 + 17 13.40 299.44 51.75 1.00 + 18 13.40 299.43 86.41 1.02 + 19 3.35 299.43 64.75 1.02 + 20 12.73 299.49 56.11 1.00 + 21 10.72 299.48 64.77 1.00 + 22 6.70 299.48 43.10 1.00 + 23 5.36 299.67 30.19 0.79 + 24 7.37 299.57 47.47 0.70 + 25 4.02 299.73 30.21 0.92 + 27 5.36 299.72 73.54 1.04 + 28 0.00 299.72 82.21 1.00 + 29 4.69 299.72 82.21 0.99 + 30 2.01 299.72 73.54 1.00 + 31 11.39 299.73 47.54 1.00 + 32 11.39 299.43 82.08 0.99 + 33 1.00 299.48 51.77 1.00 + 34 1.00 299.48 47.44 1.00 + 35 0.00 299.72 82.21 1.00 + 36 0.67 299.72 82.21 1.00 + 26 -216.26 299.78 28.07 1.00 Tank + + + Link Results at 7:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 1 -0.00 0.00 0.00 + 2 -10.06 0.03 0.00 + 3 4.70 0.03 0.00 + 4 -4.68 0.03 0.00 + 5 -10.04 0.03 0.00 + 6 -25.46 0.07 0.00 + 7 -28.81 0.08 0.01 + 8 9.38 0.03 0.00 + 9 -40.87 0.12 0.01 + 10 3.35 0.02 0.00 + 11 -50.25 0.14 0.02 + 12 -73.55 0.21 0.03 + 13 -84.27 0.24 0.04 + 14 -105.59 0.30 0.06 + 15 -139.09 0.39 0.11 + 16 19.98 0.13 0.02 + 17 34.96 0.22 0.06 + 18 1.07 0.01 0.00 + 19 22.64 0.06 0.00 + 20 9.24 0.03 0.00 + 21 5.50 0.04 0.00 + 22 32.16 0.09 0.01 + 23 9.75 0.06 0.01 + 24 -0.97 0.01 0.00 + 25 9.68 0.06 0.01 + 26 -182.76 0.52 0.18 + 27 -175.39 0.50 0.16 + 28 -188.12 0.53 0.19 + 29 -216.26 0.61 0.24 + 30 24.12 0.07 0.00 + 31 12.73 0.08 0.01 + 32 7.37 0.05 0.00 + 34 1.15 0.01 0.00 + 35 2.01 0.01 0.00 + 36 1.00 0.01 0.00 + 37 -2.15 0.01 0.00 + 38 1.53 0.01 0.00 + 39 2.01 0.01 0.00 + 40 0.48 0.00 0.00 + 41 0.67 0.00 0.00 + + + Node Results at 8:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Fluoride + Node gpm ft psi mg/L + -------------------------------------------------------- + 1 0.00 298.46 107.66 0.35 + 2 5.36 298.46 85.99 0.35 + 3 9.38 298.46 103.32 0.46 + 4 5.36 298.46 103.32 0.54 + 5 5.36 298.46 85.99 0.42 + 6 3.35 298.46 75.16 0.52 + 7 2.68 298.48 60.00 0.53 + 8 6.03 298.48 81.67 1.00 + 9 9.38 298.48 51.34 0.53 + 10 3.35 298.48 73.00 1.00 + 11 23.30 298.50 49.18 0.53 + 12 10.72 298.56 38.37 0.69 + 13 1.34 298.58 38.38 0.73 + 14 1.34 298.61 42.73 0.99 + 15 1.34 298.64 47.07 1.00 + 16 13.40 298.55 64.37 0.70 + 17 13.40 298.55 51.37 0.71 + 18 13.40 298.55 86.03 1.03 + 19 3.35 298.55 64.37 1.03 + 20 12.73 298.60 55.72 0.98 + 21 10.72 298.59 64.39 1.00 + 22 6.70 298.59 42.72 1.00 + 23 5.36 298.79 29.81 1.00 + 24 7.37 298.68 47.09 1.00 + 25 4.02 298.84 29.83 1.00 + 27 5.36 298.84 73.16 1.00 + 28 0.00 298.84 81.82 1.00 + 29 4.69 298.84 81.82 1.02 + 30 2.01 298.84 73.16 1.00 + 31 11.39 298.84 47.16 1.00 + 32 11.39 298.55 81.70 1.00 + 33 1.00 298.59 51.39 1.00 + 34 1.00 298.59 47.05 1.00 + 35 0.00 298.84 81.82 1.00 + 36 0.67 298.84 81.82 1.00 + 26 -216.26 298.89 27.68 1.00 Tank + + + Link Results at 8:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 1 -0.00 0.00 0.00 + 2 -10.06 0.03 0.00 + 3 4.70 0.03 0.00 + 4 -4.68 0.03 0.00 + 5 -10.04 0.03 0.00 + 6 -25.46 0.07 0.00 + 7 -28.81 0.08 0.01 + 8 9.38 0.03 0.00 + 9 -40.87 0.12 0.01 + 10 3.35 0.02 0.00 + 11 -50.25 0.14 0.02 + 12 -73.55 0.21 0.03 + 13 -84.27 0.24 0.04 + 14 -105.59 0.30 0.06 + 15 -139.09 0.39 0.11 + 16 19.98 0.13 0.02 + 17 34.96 0.22 0.06 + 18 1.07 0.01 0.00 + 19 22.64 0.06 0.00 + 20 9.24 0.03 0.00 + 21 5.50 0.04 0.00 + 22 32.16 0.09 0.01 + 23 9.75 0.06 0.01 + 24 -0.97 0.01 0.00 + 25 9.68 0.06 0.01 + 26 -182.76 0.52 0.18 + 27 -175.39 0.50 0.16 + 28 -188.12 0.53 0.19 + 29 -216.26 0.61 0.24 + 30 24.12 0.07 0.00 + 31 12.73 0.08 0.01 + 32 7.37 0.05 0.00 + 34 1.15 0.01 0.00 + 35 2.01 0.01 0.00 + 36 1.00 0.01 0.00 + 37 -2.15 0.01 0.00 + 38 1.53 0.01 0.00 + 39 2.01 0.01 0.00 + 40 0.48 0.00 0.00 + 41 0.67 0.00 0.00 + + + Node Results at 9:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Fluoride + Node gpm ft psi mg/L + -------------------------------------------------------- + 1 0.00 296.44 106.78 0.35 + 2 10.72 296.44 85.12 0.35 + 3 18.76 296.43 102.45 0.46 + 4 10.72 296.44 102.45 0.64 + 5 10.72 296.44 85.12 0.42 + 6 6.70 296.46 74.30 0.52 + 7 5.36 296.52 59.15 0.53 + 8 12.06 296.52 80.82 1.00 + 9 18.76 296.54 50.50 0.53 + 10 6.70 296.52 72.15 1.00 + 11 46.61 296.58 48.35 0.60 + 12 21.44 296.80 37.61 0.96 + 13 2.68 296.89 37.65 1.00 + 14 2.68 296.99 42.02 1.00 + 15 2.68 297.10 46.41 1.00 + 16 26.80 296.78 63.60 0.70 + 17 26.80 296.78 50.60 0.71 + 18 26.80 296.77 85.26 1.00 + 19 6.70 296.77 63.60 1.04 + 20 25.46 296.96 55.01 0.70 + 21 21.44 296.93 63.67 0.99 + 22 13.40 296.93 42.00 0.99 + 23 10.72 297.63 29.31 1.00 + 24 14.74 297.25 46.47 1.00 + 25 8.04 297.84 29.39 1.00 + 27 10.72 297.81 72.71 1.00 + 28 0.00 297.81 81.38 1.00 + 29 9.38 297.81 81.38 1.04 + 30 4.02 297.81 72.71 1.00 + 31 22.78 297.83 46.72 0.96 + 32 22.78 296.77 80.93 1.00 + 33 2.01 296.93 50.67 1.00 + 34 2.01 296.93 46.33 1.00 + 35 0.00 297.81 81.38 1.00 + 36 1.34 297.81 81.38 1.00 + 26 -432.53 298.01 27.30 1.00 Tank + + + Link Results at 9:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 1 -0.00 0.00 0.00 + 2 -20.12 0.06 0.00 + 3 9.40 0.06 0.01 + 4 -9.36 0.06 0.01 + 5 -20.08 0.06 0.00 + 6 -50.92 0.14 0.02 + 7 -57.62 0.16 0.02 + 8 18.76 0.05 0.00 + 9 -81.74 0.23 0.04 + 10 6.70 0.04 0.00 + 11 -100.50 0.29 0.06 + 12 -147.11 0.42 0.12 + 13 -168.55 0.48 0.15 + 14 -211.18 0.60 0.23 + 15 -278.18 0.79 0.39 + 16 39.96 0.26 0.08 + 17 69.92 0.45 0.22 + 18 2.15 0.01 0.00 + 19 45.27 0.13 0.01 + 20 18.47 0.05 0.00 + 21 11.01 0.07 0.01 + 22 64.32 0.18 0.03 + 23 19.50 0.12 0.02 + 24 -1.94 0.01 0.00 + 25 19.36 0.12 0.02 + 26 -365.53 1.04 0.64 + 27 -350.79 1.00 0.59 + 28 -376.25 1.07 0.67 + 29 -432.53 1.23 0.87 + 30 48.24 0.14 0.02 + 31 25.46 0.16 0.03 + 32 14.74 0.09 0.01 + 34 2.31 0.01 0.00 + 35 4.02 0.03 0.00 + 36 2.01 0.01 0.00 + 37 -4.31 0.03 0.00 + 38 3.05 0.02 0.00 + 39 4.02 0.03 0.00 + 40 0.97 0.01 0.00 + 41 1.34 0.01 0.00 + + + Node Results at 10:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Fluoride + Node gpm ft psi mg/L + -------------------------------------------------------- + 1 0.00 291.41 104.60 0.35 + 2 19.68 291.41 82.94 0.35 + 3 34.44 291.39 100.26 0.46 + 4 19.68 291.41 100.27 0.64 + 5 19.68 291.42 82.94 0.51 + 6 12.30 291.48 72.14 0.52 + 7 9.84 291.65 57.05 0.56 + 8 22.14 291.65 78.71 0.98 + 9 34.44 291.70 48.40 0.69 + 10 12.30 291.64 70.04 1.00 + 11 85.56 291.83 46.29 0.69 + 12 39.36 292.52 35.76 1.00 + 13 4.92 292.80 35.88 1.00 + 14 4.92 293.09 40.34 1.00 + 15 4.92 293.45 44.82 1.00 + 16 49.20 292.45 61.72 0.70 + 17 49.20 292.45 48.73 1.00 + 18 49.20 292.42 83.38 0.72 + 19 12.30 292.42 61.71 1.04 + 20 46.74 293.00 53.30 0.72 + 21 39.36 292.92 61.93 1.03 + 22 24.60 292.92 40.26 1.04 + 23 19.68 295.08 28.20 1.00 + 24 27.06 293.90 45.02 1.00 + 25 14.76 295.71 28.47 1.00 + 27 19.68 295.64 71.77 0.99 + 28 0.00 295.62 80.43 1.00 + 29 17.22 295.62 80.43 1.00 + 30 7.38 295.62 71.76 1.00 + 31 41.82 295.68 45.79 1.00 + 32 41.82 292.42 79.04 1.02 + 33 3.69 292.92 48.93 1.00 + 34 3.69 292.92 44.59 1.00 + 35 0.00 295.62 80.43 1.00 + 36 2.46 295.62 80.43 1.00 + 26 -794.04 296.24 26.54 1.00 Tank + + + Link Results at 10:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 1 0.00 0.00 0.00 + 2 -36.94 0.10 0.01 + 3 17.26 0.11 0.02 + 4 -17.18 0.11 0.02 + 5 -36.86 0.10 0.01 + 6 -93.48 0.27 0.05 + 7 -105.78 0.30 0.06 + 8 34.44 0.10 0.00 + 9 -150.06 0.43 0.12 + 10 12.30 0.08 0.00 + 11 -184.50 0.52 0.18 + 12 -270.06 0.77 0.36 + 13 -309.42 0.88 0.47 + 14 -387.69 1.10 0.71 + 15 -510.69 1.45 1.19 + 16 73.35 0.47 0.24 + 17 128.37 0.82 0.66 + 18 3.94 0.03 0.00 + 19 83.11 0.24 0.04 + 20 33.91 0.10 0.01 + 21 20.21 0.13 0.02 + 22 118.08 0.33 0.08 + 23 35.80 0.23 0.06 + 24 -3.56 0.02 0.00 + 25 35.54 0.23 0.06 + 26 -671.04 1.90 1.97 + 27 -643.98 1.83 1.82 + 28 -690.72 1.96 2.08 + 29 -794.04 2.25 2.69 + 30 88.56 0.25 0.05 + 31 46.74 0.30 0.10 + 32 27.06 0.17 0.04 + 34 4.24 0.03 0.00 + 35 7.38 0.05 0.00 + 36 3.69 0.02 0.00 + 37 -7.91 0.05 0.00 + 38 5.60 0.04 0.00 + 39 7.38 0.05 0.00 + 40 1.78 0.01 0.00 + 41 2.46 0.02 0.00 + + + Node Results at 11:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Fluoride + Node gpm ft psi mg/L + -------------------------------------------------------- + 1 0.00 292.14 104.92 0.35 + 2 7.76 292.14 83.25 0.35 + 3 13.58 292.13 100.58 0.41 + 4 7.76 292.14 100.58 0.64 + 5 7.76 292.14 83.25 0.52 + 6 4.85 292.15 72.43 0.52 + 7 3.88 292.18 57.27 1.00 + 8 8.73 292.18 78.94 1.03 + 9 13.58 292.19 48.61 1.00 + 10 4.85 292.18 70.27 1.00 + 11 33.74 292.21 46.45 1.00 + 12 15.52 292.34 35.68 1.00 + 13 1.94 292.39 35.70 1.00 + 14 1.94 292.44 40.05 1.00 + 15 1.94 292.50 44.41 1.00 + 16 19.40 292.32 61.67 1.00 + 17 19.40 292.32 48.67 1.00 + 18 19.40 292.32 83.33 1.00 + 19 4.85 292.32 61.67 1.00 + 20 18.43 292.42 53.05 1.00 + 21 15.52 292.41 61.70 0.72 + 22 9.70 292.41 40.04 0.70 + 23 7.76 292.79 27.21 1.00 + 24 10.67 292.58 44.45 1.00 + 25 5.82 292.90 27.26 1.00 + 27 7.76 292.89 70.58 1.00 + 28 0.00 292.89 79.25 1.00 + 29 6.79 292.89 79.25 1.00 + 30 2.91 292.89 70.58 1.00 + 31 16.49 292.90 44.59 1.00 + 32 16.49 292.32 79.00 0.79 + 33 1.45 292.41 48.71 1.00 + 34 1.45 292.41 44.37 1.00 + 35 0.00 292.89 79.25 1.00 + 36 0.97 292.89 79.25 1.00 + 26 -313.10 293.00 25.13 1.00 Tank + + + Link Results at 11:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 1 0.00 0.00 0.00 + 2 -14.57 0.04 0.00 + 3 6.81 0.04 0.00 + 4 -6.77 0.04 0.00 + 5 -14.53 0.04 0.00 + 6 -36.86 0.10 0.01 + 7 -41.71 0.12 0.01 + 8 13.58 0.04 0.00 + 9 -59.17 0.17 0.02 + 10 4.85 0.03 0.00 + 11 -72.75 0.21 0.03 + 12 -106.49 0.30 0.07 + 13 -122.01 0.35 0.08 + 14 -152.87 0.43 0.13 + 15 -201.37 0.57 0.21 + 16 28.92 0.18 0.04 + 17 50.62 0.32 0.12 + 18 1.55 0.01 0.00 + 19 32.77 0.09 0.01 + 20 13.37 0.04 0.00 + 21 7.97 0.05 0.00 + 22 46.56 0.13 0.01 + 23 14.12 0.09 0.01 + 24 -1.40 0.01 0.00 + 25 14.01 0.09 0.01 + 26 -264.60 0.75 0.35 + 27 -253.93 0.72 0.33 + 28 -272.36 0.77 0.37 + 29 -313.10 0.89 0.48 + 30 34.92 0.10 0.01 + 31 18.43 0.12 0.02 + 32 10.67 0.07 0.01 + 34 1.67 0.01 0.00 + 35 2.91 0.02 0.00 + 36 1.45 0.01 0.00 + 37 -3.12 0.02 0.00 + 38 2.21 0.01 0.00 + 39 2.91 0.02 0.00 + 40 0.70 0.00 0.00 + 41 0.97 0.01 0.00 + + + Node Results at 12:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Fluoride + Node gpm ft psi mg/L + -------------------------------------------------------- + 1 -555.52 305.13 110.55 0.35 + 2 7.36 301.81 87.44 0.42 + 3 12.88 301.36 104.58 0.41 + 4 7.36 301.06 104.45 0.81 + 5 7.36 301.03 87.11 0.52 + 6 4.60 299.55 75.63 0.52 + 7 3.68 296.29 59.05 1.00 + 8 8.28 296.28 80.72 0.79 + 9 12.88 295.83 50.19 1.00 + 10 4.60 296.28 72.05 1.00 + 11 32.00 295.07 47.69 1.00 + 12 14.72 293.25 36.07 1.00 + 13 1.84 292.71 35.84 1.00 + 14 1.84 292.46 40.06 1.00 + 15 1.84 292.31 44.33 1.00 + 16 18.40 292.35 61.68 1.00 + 17 18.40 292.31 48.67 1.00 + 18 18.40 292.31 83.33 1.00 + 19 4.60 292.32 61.67 1.00 + 20 17.48 292.45 53.06 1.00 + 21 14.72 292.43 61.72 0.72 + 22 9.20 292.43 40.05 0.70 + 23 7.36 291.92 26.83 1.00 + 24 10.12 292.19 44.28 1.00 + 25 5.52 291.79 26.77 1.00 + 27 7.36 291.78 70.10 1.00 + 28 0.00 291.77 78.76 1.00 + 29 6.44 291.77 78.76 1.00 + 30 2.76 291.77 70.10 1.00 + 31 15.64 291.78 44.10 1.00 + 32 15.64 292.31 79.00 0.85 + 33 1.38 292.43 48.72 1.00 + 34 1.38 292.43 44.38 1.00 + 35 0.00 291.77 78.76 1.00 + 36 0.92 291.77 78.76 1.00 + 26 258.56 291.72 24.58 1.00 Tank + + + Link Results at 12:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 1 555.52 1.58 1.39 + 2 458.61 1.30 0.97 + 3 89.55 0.57 0.34 + 4 76.67 0.49 0.26 + 5 69.31 0.20 0.03 + 6 520.56 1.48 1.23 + 7 515.96 1.46 1.21 + 8 12.88 0.04 0.00 + 9 499.40 1.42 1.14 + 10 4.60 0.03 0.00 + 11 486.52 1.38 1.09 + 12 454.52 1.29 0.96 + 13 439.80 1.25 0.90 + 14 363.40 1.03 0.63 + 15 317.40 0.90 0.49 + 16 74.57 0.48 0.24 + 17 0.87 0.01 0.00 + 18 35.52 0.23 0.06 + 19 17.99 0.05 0.00 + 20 -0.41 0.00 0.00 + 21 20.65 0.13 0.02 + 22 44.16 0.13 0.01 + 23 13.39 0.09 0.01 + 24 -1.33 0.01 0.00 + 25 13.29 0.08 0.01 + 26 304.56 0.86 0.46 + 27 314.68 0.89 0.48 + 28 297.20 0.84 0.44 + 29 258.56 0.73 0.34 + 30 33.12 0.09 0.01 + 31 17.48 0.11 0.02 + 32 10.12 0.06 0.01 + 34 1.58 0.01 0.00 + 35 2.76 0.02 0.00 + 36 1.38 0.01 0.00 + 37 -16.05 0.10 0.01 + 38 2.10 0.01 0.00 + 39 2.76 0.02 0.00 + 40 0.66 0.00 0.00 + 41 0.92 0.01 0.00 + + + Node Results at 13:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Fluoride + Node gpm ft psi mg/L + -------------------------------------------------------- + 1 -694.40 315.17 114.90 0.17 + 2 5.44 310.14 91.05 0.17 + 3 9.52 309.50 108.11 0.35 + 4 5.44 308.99 107.89 0.35 + 5 5.44 308.94 90.53 0.21 + 6 3.40 306.59 78.68 0.21 + 7 2.72 301.37 61.25 0.38 + 8 6.12 301.37 82.92 0.69 + 9 9.52 300.62 52.26 0.46 + 10 3.40 301.37 74.25 1.00 + 11 23.65 299.34 49.55 0.52 + 12 10.88 296.12 37.31 0.55 + 13 1.36 295.13 36.89 0.75 + 14 1.36 294.65 41.01 0.92 + 15 1.36 294.34 45.21 0.99 + 16 13.60 294.54 62.63 1.00 + 17 13.60 294.46 49.60 1.00 + 18 13.60 294.46 84.26 1.00 + 19 3.40 294.48 62.60 0.70 + 20 12.92 294.65 54.01 1.00 + 21 10.88 294.64 62.67 1.00 + 22 6.80 294.64 41.01 1.00 + 23 5.44 293.33 27.44 1.00 + 24 7.48 294.04 45.08 1.00 + 25 4.08 292.98 27.29 1.00 + 27 5.44 292.98 70.62 1.00 + 28 0.00 292.98 79.28 1.00 + 29 4.76 292.98 79.28 1.00 + 30 2.04 292.98 70.62 1.00 + 31 11.56 292.98 44.62 1.00 + 32 11.56 294.46 79.93 1.00 + 33 1.02 294.64 49.67 1.00 + 34 1.02 294.64 45.34 1.00 + 35 0.00 292.98 79.28 1.00 + 36 0.68 292.98 79.28 1.00 + 26 474.91 292.78 25.03 1.00 Tank + + + Link Results at 13:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 1 694.40 1.97 2.10 + 2 579.27 1.64 1.50 + 3 109.69 0.70 0.50 + 4 100.17 0.64 0.42 + 5 94.73 0.27 0.05 + 6 668.56 1.90 1.95 + 7 665.16 1.89 1.94 + 8 9.52 0.03 0.00 + 9 652.92 1.85 1.87 + 10 3.40 0.02 0.00 + 11 643.40 1.83 1.82 + 12 619.75 1.76 1.70 + 13 608.87 1.73 1.64 + 14 510.75 1.45 1.19 + 15 476.75 1.35 1.05 + 16 96.76 0.62 0.39 + 17 -41.00 0.26 0.08 + 18 53.63 0.34 0.13 + 19 -0.97 0.00 0.00 + 20 -14.57 0.04 0.00 + 21 29.53 0.19 0.04 + 22 32.64 0.09 0.01 + 23 9.90 0.06 0.01 + 24 -0.98 0.01 0.00 + 25 9.82 0.06 0.01 + 26 508.91 1.44 1.18 + 27 516.39 1.46 1.21 + 28 503.47 1.43 1.16 + 29 474.91 1.35 1.04 + 30 24.48 0.07 0.00 + 31 12.92 0.08 0.01 + 32 7.48 0.05 0.00 + 34 1.17 0.01 0.00 + 35 2.04 0.01 0.00 + 36 1.02 0.01 0.00 + 37 -26.13 0.17 0.03 + 38 1.55 0.01 0.00 + 39 2.04 0.01 0.00 + 40 0.49 0.00 0.00 + 41 0.68 0.00 0.00 + + + Node Results at 14:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Fluoride + Node gpm ft psi mg/L + -------------------------------------------------------- + 1 -694.40 313.86 114.33 0.17 + 2 11.44 308.83 90.49 0.17 + 3 20.02 308.15 107.52 0.17 + 4 11.44 307.71 107.33 0.17 + 5 11.44 307.67 89.98 0.20 + 6 7.15 305.50 78.21 0.21 + 7 5.72 300.74 60.98 0.24 + 8 12.87 300.73 82.65 0.53 + 9 20.02 300.08 52.03 0.26 + 10 7.15 300.73 73.98 1.00 + 11 49.74 299.01 49.40 0.22 + 12 22.88 296.53 37.49 0.21 + 13 2.86 295.81 37.18 0.22 + 14 2.86 295.47 41.37 0.27 + 15 2.86 295.30 45.63 0.39 + 16 28.60 295.28 62.95 0.53 + 17 28.60 295.24 49.93 0.99 + 18 28.60 295.24 84.60 1.00 + 19 7.15 295.24 62.93 1.00 + 20 27.17 295.44 54.35 1.00 + 21 22.88 295.41 63.01 1.00 + 22 14.30 295.41 41.34 1.00 + 23 11.44 294.90 28.12 0.44 + 24 15.73 295.17 45.57 0.42 + 25 8.58 294.77 28.07 0.47 + 27 11.44 294.75 71.38 1.00 + 28 0.00 294.74 80.05 1.00 + 29 10.01 294.74 80.05 1.00 + 30 4.29 294.74 71.38 1.00 + 31 24.31 294.76 45.39 1.00 + 32 24.31 295.24 80.26 0.70 + 33 2.14 295.41 50.01 1.00 + 34 2.14 295.41 45.67 1.00 + 35 0.00 294.74 80.05 1.00 + 36 1.43 294.74 80.05 1.00 + 26 232.82 294.72 25.88 0.99 Tank + + + Link Results at 14:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 1 694.40 1.97 2.10 + 2 569.73 1.62 1.45 + 3 113.23 0.72 0.53 + 4 93.21 0.59 0.37 + 5 81.77 0.23 0.04 + 6 640.06 1.82 1.80 + 7 632.91 1.80 1.77 + 8 20.02 0.06 0.00 + 9 607.17 1.72 1.64 + 10 7.15 0.05 0.00 + 11 587.15 1.67 1.54 + 12 537.41 1.52 1.30 + 13 514.53 1.46 1.20 + 14 421.13 1.19 0.83 + 15 349.63 0.99 0.59 + 16 90.54 0.58 0.35 + 17 26.72 0.17 0.04 + 18 38.16 0.24 0.07 + 19 36.27 0.10 0.01 + 20 7.67 0.02 0.00 + 21 23.79 0.15 0.03 + 22 68.64 0.19 0.03 + 23 20.81 0.13 0.02 + 24 -2.07 0.01 0.00 + 25 20.66 0.13 0.02 + 26 304.32 0.86 0.46 + 27 320.05 0.91 0.50 + 28 292.88 0.83 0.42 + 29 232.82 0.66 0.28 + 30 51.48 0.15 0.02 + 31 27.17 0.17 0.04 + 32 15.73 0.10 0.01 + 34 2.46 0.02 0.00 + 35 4.29 0.03 0.00 + 36 2.14 0.01 0.00 + 37 -16.64 0.11 0.02 + 38 3.26 0.02 0.00 + 39 4.29 0.03 0.00 + 40 1.03 0.01 0.00 + 41 1.43 0.01 0.00 + + + Node Results at 15:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Fluoride + Node gpm ft psi mg/L + -------------------------------------------------------- + 1 -694.40 318.40 116.30 0.13 + 2 4.88 313.37 92.45 0.13 + 3 8.54 312.73 109.51 0.13 + 4 4.88 312.22 109.29 0.17 + 5 4.88 312.17 91.93 0.14 + 6 3.05 309.80 80.08 0.14 + 7 2.44 304.53 62.63 0.19 + 8 5.49 304.53 84.29 0.53 + 9 8.54 303.78 53.63 0.19 + 10 3.05 304.53 75.62 1.00 + 11 21.22 302.48 50.90 0.19 + 12 9.76 299.18 38.64 0.21 + 13 1.22 298.17 38.20 0.21 + 14 1.22 297.68 42.32 0.23 + 15 1.22 297.35 46.51 0.24 + 16 12.20 297.58 63.95 0.21 + 17 12.20 297.50 50.91 0.64 + 18 12.20 297.50 85.58 1.00 + 19 3.05 297.52 63.92 1.00 + 20 11.59 297.67 55.32 1.00 + 21 9.76 297.66 63.98 1.00 + 22 6.10 297.66 42.32 1.00 + 23 4.88 296.27 28.71 0.24 + 24 6.71 297.02 46.37 0.24 + 25 3.66 295.89 28.55 0.22 + 27 4.88 295.89 71.88 1.00 + 28 0.00 295.89 80.54 1.00 + 29 4.27 295.89 80.55 1.00 + 30 1.83 295.89 71.88 1.00 + 31 10.37 295.89 45.88 0.57 + 32 10.37 297.50 81.24 0.92 + 33 0.92 297.66 50.98 1.00 + 34 0.92 297.66 46.65 1.00 + 35 0.00 295.89 80.54 0.99 + 36 0.61 295.89 80.54 1.00 + 26 497.50 295.67 26.29 0.98 Tank + + + Link Results at 15:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 1 694.40 1.97 2.10 + 2 580.17 1.65 1.50 + 3 109.35 0.70 0.49 + 4 100.81 0.64 0.42 + 5 95.93 0.27 0.05 + 6 671.22 1.90 1.97 + 7 668.17 1.90 1.95 + 8 8.54 0.02 0.00 + 9 657.19 1.86 1.89 + 10 3.05 0.02 0.00 + 11 648.65 1.84 1.85 + 12 627.43 1.78 1.74 + 13 617.67 1.75 1.69 + 14 519.91 1.47 1.23 + 15 489.41 1.39 1.10 + 16 96.54 0.62 0.39 + 17 -46.52 0.30 0.10 + 18 54.49 0.35 0.14 + 19 -4.23 0.01 0.00 + 20 -16.43 0.05 0.00 + 21 29.85 0.19 0.04 + 22 29.28 0.08 0.01 + 23 8.88 0.06 0.00 + 24 -0.88 0.01 0.00 + 25 8.81 0.06 0.00 + 26 528.00 1.50 1.26 + 27 534.71 1.52 1.29 + 28 523.12 1.48 1.24 + 29 497.50 1.41 1.13 + 30 21.96 0.06 0.00 + 31 11.59 0.07 0.01 + 32 6.71 0.04 0.00 + 34 1.05 0.01 0.00 + 35 1.83 0.01 0.00 + 36 0.92 0.01 0.00 + 37 -26.80 0.17 0.04 + 38 1.39 0.01 0.00 + 39 1.83 0.01 0.00 + 40 0.44 0.00 0.00 + 41 0.61 0.00 0.00 + + + Node Results at 16:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Fluoride + Node gpm ft psi mg/L + -------------------------------------------------------- + 1 -694.40 321.96 117.84 0.13 + 2 2.48 316.93 94.00 0.13 + 3 4.34 316.31 111.06 0.13 + 4 2.48 315.77 110.83 0.13 + 5 2.48 315.71 93.47 0.14 + 6 1.55 313.27 81.58 0.14 + 7 1.24 307.81 64.05 0.14 + 8 2.79 307.81 85.71 0.53 + 9 4.34 307.02 55.04 0.14 + 10 1.55 307.81 77.05 1.00 + 11 10.78 305.64 52.27 0.14 + 12 4.96 302.01 39.87 0.14 + 13 0.62 300.88 39.38 0.14 + 14 0.62 300.31 43.47 0.14 + 15 0.62 299.91 47.62 0.23 + 16 6.20 300.31 65.13 0.19 + 17 6.20 300.22 52.09 0.27 + 18 6.20 300.23 86.76 1.00 + 19 1.55 300.25 65.10 0.60 + 20 5.89 300.31 56.46 0.39 + 21 4.96 300.31 65.13 1.00 + 22 3.10 300.31 43.46 1.00 + 23 2.48 298.51 29.68 0.26 + 24 3.41 299.49 47.44 0.24 + 25 1.86 298.01 29.47 0.26 + 27 2.48 298.01 72.80 0.96 + 28 0.00 298.01 81.47 1.00 + 29 2.17 298.01 81.47 1.00 + 30 0.93 298.01 72.80 1.00 + 31 5.27 298.01 46.80 0.40 + 32 5.27 300.23 82.43 1.00 + 33 0.46 300.31 52.13 1.00 + 34 0.46 300.31 47.80 1.00 + 35 0.00 298.01 81.47 1.01 + 36 0.31 298.01 81.47 1.00 + 26 594.34 297.70 27.17 0.96 Tank + + + Link Results at 16:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 1 694.40 1.97 2.10 + 2 584.06 1.66 1.52 + 3 107.86 0.69 0.48 + 4 103.52 0.66 0.45 + 5 101.04 0.29 0.06 + 6 682.62 1.94 2.03 + 7 681.07 1.93 2.02 + 8 4.34 0.01 0.00 + 9 675.49 1.92 1.99 + 10 1.55 0.01 0.00 + 11 671.15 1.90 1.97 + 12 660.37 1.87 1.91 + 13 655.41 1.86 1.88 + 14 560.47 1.59 1.41 + 15 544.97 1.55 1.34 + 16 94.31 0.60 0.37 + 17 -68.89 0.44 0.21 + 18 57.45 0.37 0.15 + 19 -17.64 0.05 0.00 + 20 -23.84 0.07 0.00 + 21 30.66 0.20 0.05 + 22 14.88 0.04 0.00 + 23 4.51 0.03 0.00 + 24 -0.45 0.00 0.00 + 25 4.48 0.03 0.00 + 26 609.84 1.73 1.65 + 27 613.25 1.74 1.67 + 28 607.36 1.72 1.64 + 29 594.34 1.69 1.57 + 30 11.16 0.03 0.00 + 31 5.89 0.04 0.00 + 32 3.41 0.02 0.00 + 34 0.53 0.00 0.00 + 35 0.93 0.01 0.00 + 36 0.46 0.00 0.00 + 37 -29.11 0.19 0.04 + 38 0.71 0.00 0.00 + 39 0.93 0.01 0.00 + 40 0.22 0.00 0.00 + 41 0.31 0.00 0.00 + + + Node Results at 17:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Fluoride + Node gpm ft psi mg/L + -------------------------------------------------------- + 1 -104.16 300.34 108.47 0.13 + 2 6.24 300.19 86.74 0.13 + 3 10.92 300.17 104.06 0.13 + 4 6.24 300.16 104.06 0.13 + 5 6.24 300.16 86.73 0.13 + 6 3.90 300.12 75.88 0.13 + 7 3.12 300.04 60.68 0.13 + 8 7.02 300.04 82.34 0.53 + 9 10.92 300.03 52.01 0.13 + 10 3.90 300.04 73.68 1.00 + 11 27.13 300.02 49.84 0.13 + 12 12.48 300.02 39.00 0.14 + 13 1.56 300.02 39.00 0.14 + 14 1.56 300.02 43.34 0.14 + 15 1.56 300.03 47.67 0.15 + 16 15.60 299.95 64.97 0.14 + 17 15.60 299.94 51.97 0.28 + 18 15.60 299.94 86.63 1.00 + 19 3.90 299.94 64.97 0.21 + 20 14.82 300.01 56.33 0.22 + 21 12.48 300.00 64.99 1.00 + 22 7.80 300.00 43.33 1.00 + 23 6.24 300.08 30.37 0.15 + 24 8.58 300.04 47.68 0.15 + 25 4.68 300.10 30.38 0.15 + 27 6.24 300.10 73.70 0.57 + 28 0.00 300.09 82.37 1.00 + 29 5.46 300.09 82.37 1.00 + 30 2.34 300.09 73.70 1.00 + 31 13.26 300.10 47.71 0.25 + 32 13.26 299.94 82.30 0.53 + 33 1.17 300.00 52.00 1.00 + 34 1.17 300.00 47.66 1.00 + 35 0.00 300.09 82.37 1.01 + 36 0.78 300.09 82.37 1.00 + 26 -147.61 300.13 28.22 0.93 Tank + + + Link Results at 17:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 1 104.16 0.30 0.06 + 2 78.91 0.22 0.04 + 3 19.01 0.12 0.02 + 4 8.09 0.05 0.00 + 5 1.85 0.01 0.00 + 6 74.52 0.21 0.03 + 7 70.62 0.20 0.03 + 8 10.92 0.03 0.00 + 9 56.58 0.16 0.02 + 10 3.90 0.02 0.00 + 11 45.66 0.13 0.01 + 12 18.53 0.05 0.00 + 13 6.05 0.02 0.00 + 14 -26.14 0.07 0.00 + 15 -65.14 0.18 0.03 + 16 30.63 0.20 0.05 + 17 33.33 0.21 0.05 + 18 7.42 0.05 0.00 + 19 25.14 0.07 0.00 + 20 9.54 0.03 0.00 + 21 7.62 0.05 0.00 + 22 37.44 0.11 0.01 + 23 11.35 0.07 0.01 + 24 -1.13 0.01 0.00 + 25 11.27 0.07 0.01 + 26 -108.61 0.31 0.07 + 27 -100.03 0.28 0.06 + 28 -114.85 0.33 0.07 + 29 -147.61 0.42 0.12 + 30 28.08 0.08 0.01 + 31 14.82 0.09 0.01 + 32 8.58 0.05 0.00 + 34 1.34 0.01 0.00 + 35 2.34 0.01 0.00 + 36 1.17 0.01 0.00 + 37 -3.72 0.02 0.00 + 38 1.78 0.01 0.00 + 39 2.34 0.01 0.00 + 40 0.56 0.00 0.00 + 41 0.78 0.00 0.00 + + + Node Results at 18:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Fluoride + Node gpm ft psi mg/L + -------------------------------------------------------- + 1 0.00 299.38 108.06 0.15 + 2 2.96 299.38 86.39 0.13 + 3 5.18 299.38 103.72 0.13 + 4 2.96 299.38 103.72 0.13 + 5 2.96 299.38 86.39 0.13 + 6 1.85 299.38 75.56 0.13 + 7 1.48 299.39 60.40 0.13 + 8 3.33 299.39 82.06 0.53 + 9 5.18 299.39 51.73 0.13 + 10 1.85 299.39 73.40 1.00 + 11 12.87 299.39 49.57 0.13 + 12 5.92 299.41 38.74 0.13 + 13 0.74 299.42 38.75 0.14 + 14 0.74 299.43 43.08 0.15 + 15 0.74 299.44 47.42 0.15 + 16 7.40 299.41 64.74 0.14 + 17 7.40 299.41 51.74 0.29 + 18 7.40 299.41 86.40 1.00 + 19 1.85 299.41 64.74 0.24 + 20 7.03 299.43 56.08 0.24 + 21 5.92 299.43 64.75 1.00 + 22 3.70 299.43 43.08 1.00 + 23 2.96 299.49 30.11 0.93 + 24 4.07 299.45 47.43 0.68 + 25 2.22 299.51 30.12 0.93 + 27 2.96 299.51 73.45 0.32 + 28 0.00 299.51 82.11 1.00 + 29 2.59 299.51 82.11 1.00 + 30 1.11 299.51 73.45 1.00 + 31 6.29 299.51 47.45 0.24 + 32 6.29 299.41 82.07 0.71 + 33 0.56 299.43 51.75 1.00 + 34 0.56 299.43 47.41 1.00 + 35 0.00 299.51 82.11 1.03 + 36 0.37 299.51 82.11 1.00 + 26 -119.43 299.52 27.96 0.93 Tank + + + Link Results at 18:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 1 -0.00 0.00 0.00 + 2 -5.56 0.02 0.00 + 3 2.60 0.02 0.00 + 4 -2.58 0.02 0.00 + 5 -5.54 0.02 0.00 + 6 -14.06 0.04 0.00 + 7 -15.91 0.05 0.00 + 8 5.18 0.01 0.00 + 9 -22.57 0.06 0.00 + 10 1.85 0.01 0.00 + 11 -27.75 0.08 0.01 + 12 -40.62 0.12 0.01 + 13 -46.54 0.13 0.01 + 14 -58.31 0.17 0.02 + 15 -76.81 0.22 0.04 + 16 11.03 0.07 0.01 + 17 19.31 0.12 0.02 + 18 0.59 0.00 0.00 + 19 12.50 0.04 0.00 + 20 5.10 0.01 0.00 + 21 3.04 0.02 0.00 + 22 17.76 0.05 0.00 + 23 5.39 0.03 0.00 + 24 -0.53 0.00 0.00 + 25 5.34 0.03 0.00 + 26 -100.93 0.29 0.06 + 27 -96.86 0.27 0.05 + 28 -103.89 0.29 0.06 + 29 -119.43 0.34 0.08 + 30 13.32 0.04 0.00 + 31 7.03 0.04 0.00 + 32 4.07 0.03 0.00 + 34 0.64 0.00 0.00 + 35 1.11 0.01 0.00 + 36 0.56 0.00 0.00 + 37 -1.19 0.01 0.00 + 38 0.84 0.01 0.00 + 39 1.11 0.01 0.00 + 40 0.27 0.00 0.00 + 41 0.37 0.00 0.00 + + + Node Results at 19:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Fluoride + Node gpm ft psi mg/L + -------------------------------------------------------- + 1 0.00 298.60 107.72 0.15 + 2 5.36 298.60 86.05 0.13 + 3 9.38 298.60 103.39 0.13 + 4 5.36 298.60 103.39 0.13 + 5 5.36 298.60 86.05 0.13 + 6 3.35 298.61 75.22 0.13 + 7 2.68 298.62 60.07 0.13 + 8 6.03 298.62 81.73 0.59 + 9 9.38 298.63 51.40 0.13 + 10 3.35 298.62 73.06 0.98 + 11 23.30 298.64 49.24 0.13 + 12 10.72 298.70 38.43 0.14 + 13 1.34 298.73 38.45 0.15 + 14 1.34 298.75 42.79 0.93 + 15 1.34 298.79 47.14 0.93 + 16 13.40 298.70 64.43 0.14 + 17 13.40 298.70 51.43 0.31 + 18 13.40 298.69 86.09 1.00 + 19 3.35 298.69 64.43 0.24 + 20 12.73 298.75 55.79 0.24 + 21 10.72 298.74 64.45 1.00 + 22 6.70 298.74 42.78 1.00 + 23 5.36 298.93 29.87 0.93 + 24 7.37 298.83 47.15 0.93 + 25 4.02 298.99 29.89 0.93 + 27 5.36 298.98 73.22 0.21 + 28 0.00 298.98 81.89 1.00 + 29 4.69 298.98 81.89 1.00 + 30 2.01 298.98 73.22 1.00 + 31 11.39 298.99 47.22 0.20 + 32 11.39 298.69 81.76 0.88 + 33 1.00 298.74 51.45 1.00 + 34 1.00 298.74 47.12 1.00 + 35 0.00 298.98 81.89 1.03 + 36 0.67 298.98 81.89 1.00 + 26 -216.26 299.04 27.75 0.93 Tank + + + Link Results at 19:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 1 -0.00 0.00 0.00 + 2 -10.06 0.03 0.00 + 3 4.70 0.03 0.00 + 4 -4.68 0.03 0.00 + 5 -10.04 0.03 0.00 + 6 -25.46 0.07 0.00 + 7 -28.81 0.08 0.01 + 8 9.38 0.03 0.00 + 9 -40.87 0.12 0.01 + 10 3.35 0.02 0.00 + 11 -50.25 0.14 0.02 + 12 -73.55 0.21 0.03 + 13 -84.27 0.24 0.04 + 14 -105.59 0.30 0.06 + 15 -139.09 0.39 0.11 + 16 19.98 0.13 0.02 + 17 34.96 0.22 0.06 + 18 1.07 0.01 0.00 + 19 22.64 0.06 0.00 + 20 9.24 0.03 0.00 + 21 5.50 0.04 0.00 + 22 32.16 0.09 0.01 + 23 9.75 0.06 0.01 + 24 -0.97 0.01 0.00 + 25 9.68 0.06 0.01 + 26 -182.76 0.52 0.18 + 27 -175.39 0.50 0.16 + 28 -188.12 0.53 0.19 + 29 -216.26 0.61 0.24 + 30 24.12 0.07 0.00 + 31 12.73 0.08 0.01 + 32 7.37 0.05 0.00 + 34 1.15 0.01 0.00 + 35 2.01 0.01 0.00 + 36 1.00 0.01 0.00 + 37 -2.15 0.01 0.00 + 38 1.53 0.01 0.00 + 39 2.01 0.01 0.00 + 40 0.48 0.00 0.00 + 41 0.67 0.00 0.00 + + + Node Results at 20:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Fluoride + Node gpm ft psi mg/L + -------------------------------------------------------- + 1 0.00 296.75 106.92 0.15 + 2 10.08 296.75 85.25 0.13 + 3 17.64 296.75 102.58 0.13 + 4 10.08 296.75 102.59 0.13 + 5 10.08 296.76 85.25 0.13 + 6 6.30 296.77 74.43 0.13 + 7 5.04 296.82 59.29 0.13 + 8 11.34 296.82 80.95 0.69 + 9 17.64 296.84 50.63 0.13 + 10 6.30 296.82 72.28 1.00 + 11 43.82 296.88 48.48 0.14 + 12 20.16 297.08 37.73 0.91 + 13 2.52 297.16 37.77 0.93 + 14 2.52 297.24 42.13 0.93 + 15 2.52 297.34 46.51 0.93 + 16 25.20 297.06 63.72 0.14 + 17 25.20 297.06 50.72 0.15 + 18 25.20 297.05 85.38 0.89 + 19 6.30 297.05 63.72 0.24 + 20 23.94 297.21 55.12 0.17 + 21 20.16 297.19 63.78 1.00 + 22 12.60 297.19 42.11 1.00 + 23 10.08 297.82 29.39 0.93 + 24 13.86 297.48 46.57 0.93 + 25 7.56 298.00 29.46 0.93 + 27 10.08 297.98 72.78 0.24 + 28 0.00 297.97 81.45 1.00 + 29 8.82 297.97 81.45 0.44 + 30 3.78 297.97 72.78 1.00 + 31 21.42 297.99 46.79 0.93 + 32 21.42 297.05 81.05 0.88 + 33 1.89 297.19 50.78 1.00 + 34 1.89 297.19 46.45 1.00 + 35 0.00 297.97 81.45 1.00 + 36 1.26 297.97 81.45 1.00 + 26 -406.70 298.15 27.36 0.93 Tank + + + Link Results at 20:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 1 -0.00 0.00 0.00 + 2 -18.92 0.05 0.00 + 3 8.84 0.06 0.00 + 4 -8.80 0.06 0.00 + 5 -18.88 0.05 0.00 + 6 -47.88 0.14 0.01 + 7 -54.18 0.15 0.02 + 8 17.64 0.05 0.00 + 9 -76.86 0.22 0.04 + 10 6.30 0.04 0.00 + 11 -94.50 0.27 0.05 + 12 -138.32 0.39 0.11 + 13 -158.48 0.45 0.14 + 14 -198.57 0.56 0.21 + 15 -261.57 0.74 0.34 + 16 37.57 0.24 0.07 + 17 65.75 0.42 0.19 + 18 2.02 0.01 0.00 + 19 42.57 0.12 0.01 + 20 17.37 0.05 0.00 + 21 10.35 0.07 0.01 + 22 60.48 0.17 0.02 + 23 18.34 0.12 0.02 + 24 -1.82 0.01 0.00 + 25 18.20 0.12 0.02 + 26 -343.70 0.98 0.57 + 27 -329.84 0.94 0.53 + 28 -353.78 1.00 0.60 + 29 -406.70 1.15 0.78 + 30 45.36 0.13 0.01 + 31 23.94 0.15 0.03 + 32 13.86 0.09 0.01 + 34 2.17 0.01 0.00 + 35 3.78 0.02 0.00 + 36 1.89 0.01 0.00 + 37 -4.05 0.03 0.00 + 38 2.87 0.02 0.00 + 39 3.78 0.02 0.00 + 40 0.91 0.01 0.00 + 41 1.26 0.01 0.00 + + + Node Results at 21:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Fluoride + Node gpm ft psi mg/L + -------------------------------------------------------- + 1 0.00 294.41 105.90 0.15 + 2 12.48 294.41 84.24 0.13 + 3 21.84 294.41 101.57 0.13 + 4 12.48 294.41 101.57 0.13 + 5 12.48 294.42 84.24 0.13 + 6 7.80 294.44 73.42 0.13 + 7 6.24 294.52 58.29 0.13 + 8 14.04 294.52 79.95 0.96 + 9 21.84 294.54 49.63 0.13 + 10 7.80 294.51 71.28 1.04 + 11 54.26 294.59 47.49 0.14 + 12 24.96 294.89 36.78 0.93 + 13 3.12 295.01 36.84 0.93 + 14 3.12 295.14 41.22 0.93 + 15 3.12 295.29 45.62 0.93 + 16 31.20 294.86 62.77 0.14 + 17 31.20 294.86 49.77 0.91 + 18 31.20 294.85 84.43 0.31 + 19 7.80 294.85 62.76 0.19 + 20 29.64 295.10 54.21 0.15 + 21 24.96 295.06 62.86 0.95 + 22 15.60 295.06 41.19 0.97 + 23 12.48 295.99 28.59 0.93 + 24 17.16 295.48 45.71 0.93 + 25 9.36 296.26 28.71 0.93 + 27 12.48 296.23 72.03 0.93 + 28 0.00 296.22 80.69 1.00 + 29 10.92 296.22 80.69 0.21 + 30 4.68 296.22 72.02 1.00 + 31 26.52 296.25 46.04 0.93 + 32 26.52 294.85 80.09 0.87 + 33 2.34 295.06 49.86 1.00 + 34 2.34 295.06 45.52 1.00 + 35 0.00 296.22 80.69 0.99 + 36 1.56 296.22 80.69 1.00 + 26 -503.54 296.49 26.64 0.93 Tank + + + Link Results at 21:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 1 -0.00 0.00 0.00 + 2 -23.43 0.07 0.00 + 3 10.95 0.07 0.01 + 4 -10.89 0.07 0.01 + 5 -23.37 0.07 0.00 + 6 -59.28 0.17 0.02 + 7 -67.08 0.19 0.03 + 8 21.84 0.06 0.00 + 9 -95.16 0.27 0.05 + 10 7.80 0.05 0.00 + 11 -117.00 0.33 0.08 + 12 -171.26 0.49 0.16 + 13 -196.22 0.56 0.20 + 14 -245.85 0.70 0.31 + 15 -323.85 0.92 0.51 + 16 46.52 0.30 0.10 + 17 81.40 0.52 0.29 + 18 2.50 0.02 0.00 + 19 52.70 0.15 0.02 + 20 21.50 0.06 0.00 + 21 12.82 0.08 0.01 + 22 74.88 0.21 0.03 + 23 22.71 0.14 0.03 + 24 -2.25 0.01 0.00 + 25 22.53 0.14 0.03 + 26 -425.54 1.21 0.85 + 27 -408.38 1.16 0.78 + 28 -438.02 1.24 0.89 + 29 -503.54 1.43 1.16 + 30 56.16 0.16 0.02 + 31 29.64 0.19 0.04 + 32 17.16 0.11 0.02 + 34 2.69 0.02 0.00 + 35 4.68 0.03 0.00 + 36 2.34 0.01 0.00 + 37 -5.02 0.03 0.00 + 38 3.55 0.02 0.00 + 39 4.68 0.03 0.00 + 40 1.13 0.01 0.00 + 41 1.56 0.01 0.00 + + + Node Results at 22:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Fluoride + Node gpm ft psi mg/L + -------------------------------------------------------- + 1 0.00 293.18 105.37 0.15 + 2 9.52 293.18 83.70 0.13 + 3 16.66 293.17 101.03 0.13 + 4 9.52 293.18 101.04 0.13 + 5 9.52 293.18 83.70 0.13 + 6 5.95 293.19 72.88 0.13 + 7 4.76 293.24 57.73 0.14 + 8 10.71 293.24 79.40 0.83 + 9 16.66 293.25 49.07 0.93 + 10 5.95 293.24 70.73 0.96 + 11 41.39 293.29 46.92 0.93 + 12 19.04 293.47 36.17 0.93 + 13 2.38 293.54 36.20 0.93 + 14 2.38 293.61 40.56 0.93 + 15 2.38 293.71 44.94 0.93 + 16 23.80 293.45 62.16 0.93 + 17 23.80 293.45 49.16 0.91 + 18 23.80 293.44 83.82 0.91 + 19 5.95 293.44 62.15 0.18 + 20 22.61 293.59 53.55 0.93 + 21 19.04 293.57 62.21 0.31 + 22 11.90 293.57 40.54 0.24 + 23 9.52 294.13 27.79 0.93 + 24 13.09 293.83 44.99 0.93 + 25 7.14 294.30 27.86 0.93 + 27 9.52 294.28 71.18 0.93 + 28 0.00 294.27 79.85 1.00 + 29 8.33 294.27 79.85 0.93 + 30 3.57 294.27 71.18 1.00 + 31 20.23 294.29 45.19 0.93 + 32 20.23 293.44 79.48 0.37 + 33 1.78 293.57 49.21 1.00 + 34 1.78 293.57 44.88 1.00 + 35 0.00 294.27 79.85 1.00 + 36 1.19 294.27 79.85 1.00 + 26 -384.11 294.44 25.75 0.93 Tank + + + Link Results at 22:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 1 0.00 0.00 0.00 + 2 -17.87 0.05 0.00 + 3 8.35 0.05 0.00 + 4 -8.31 0.05 0.00 + 5 -17.83 0.05 0.00 + 6 -45.22 0.13 0.01 + 7 -51.17 0.15 0.02 + 8 16.66 0.05 0.00 + 9 -72.59 0.21 0.03 + 10 5.95 0.04 0.00 + 11 -89.25 0.25 0.05 + 12 -130.64 0.37 0.10 + 13 -149.68 0.42 0.12 + 14 -187.54 0.53 0.19 + 15 -247.04 0.70 0.31 + 16 35.48 0.23 0.06 + 17 62.10 0.40 0.17 + 18 1.91 0.01 0.00 + 19 40.20 0.11 0.01 + 20 16.40 0.05 0.00 + 21 9.78 0.06 0.01 + 22 57.12 0.16 0.02 + 23 17.32 0.11 0.02 + 24 -1.72 0.01 0.00 + 25 17.19 0.11 0.02 + 26 -324.61 0.92 0.51 + 27 -311.52 0.88 0.48 + 28 -334.13 0.95 0.54 + 29 -384.11 1.09 0.70 + 30 42.84 0.12 0.01 + 31 22.61 0.14 0.03 + 32 13.09 0.08 0.01 + 34 2.05 0.01 0.00 + 35 3.57 0.02 0.00 + 36 1.78 0.01 0.00 + 37 -3.83 0.02 0.00 + 38 2.71 0.02 0.00 + 39 3.57 0.02 0.00 + 40 0.86 0.01 0.00 + 41 1.19 0.01 0.00 + + + Node Results at 23:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Fluoride + Node gpm ft psi mg/L + -------------------------------------------------------- + 1 0.00 291.47 104.63 0.15 + 2 10.08 291.47 82.96 0.13 + 3 17.64 291.46 100.29 0.13 + 4 10.08 291.47 100.29 0.13 + 5 10.08 291.47 82.96 0.13 + 6 6.30 291.49 72.14 0.13 + 7 5.04 291.54 57.00 0.93 + 8 11.34 291.54 78.66 0.21 + 9 17.64 291.55 48.34 0.93 + 10 6.30 291.53 69.99 0.68 + 11 43.82 291.59 46.18 0.93 + 12 20.16 291.79 35.44 0.93 + 13 2.52 291.87 35.47 0.93 + 14 2.52 291.95 39.84 0.93 + 15 2.52 292.06 44.22 0.93 + 16 25.20 291.77 61.43 0.93 + 17 25.20 291.77 48.43 0.91 + 18 25.20 291.76 83.09 0.91 + 19 6.30 291.76 61.42 0.14 + 20 23.94 291.93 52.83 0.93 + 21 20.16 291.90 61.49 0.22 + 22 12.60 291.90 39.82 0.15 + 23 10.08 292.53 27.09 0.93 + 24 13.86 292.19 44.28 0.93 + 25 7.56 292.71 27.17 0.93 + 27 10.08 292.69 70.49 0.93 + 28 0.00 292.69 79.16 1.00 + 29 8.82 292.69 79.16 0.93 + 30 3.78 292.68 70.49 1.00 + 31 21.42 292.70 44.50 0.93 + 32 21.42 291.76 78.76 0.29 + 33 1.89 291.90 48.49 0.99 + 34 1.89 291.90 44.15 1.00 + 35 0.00 292.69 79.16 1.00 + 36 1.26 292.69 79.16 1.00 + 26 -406.70 292.87 25.07 0.93 Tank + + + Link Results at 23:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 1 -0.00 0.00 0.00 + 2 -18.92 0.05 0.00 + 3 8.84 0.06 0.00 + 4 -8.80 0.06 0.00 + 5 -18.88 0.05 0.00 + 6 -47.88 0.14 0.01 + 7 -54.18 0.15 0.02 + 8 17.64 0.05 0.00 + 9 -76.86 0.22 0.04 + 10 6.30 0.04 0.00 + 11 -94.50 0.27 0.05 + 12 -138.32 0.39 0.11 + 13 -158.48 0.45 0.14 + 14 -198.57 0.56 0.21 + 15 -261.57 0.74 0.34 + 16 37.57 0.24 0.07 + 17 65.75 0.42 0.19 + 18 2.02 0.01 0.00 + 19 42.57 0.12 0.01 + 20 17.37 0.05 0.00 + 21 10.35 0.07 0.01 + 22 60.48 0.17 0.02 + 23 18.34 0.12 0.02 + 24 -1.82 0.01 0.00 + 25 18.20 0.12 0.02 + 26 -343.70 0.98 0.57 + 27 -329.84 0.94 0.53 + 28 -353.78 1.00 0.60 + 29 -406.70 1.15 0.78 + 30 45.36 0.13 0.01 + 31 23.94 0.15 0.03 + 32 13.86 0.09 0.01 + 34 2.17 0.01 0.00 + 35 3.78 0.02 0.00 + 36 1.89 0.01 0.00 + 37 -4.05 0.03 0.00 + 38 2.87 0.02 0.00 + 39 3.78 0.02 0.00 + 40 0.91 0.01 0.00 + 41 1.26 0.01 0.00 + + + Node Results at 24:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Fluoride + Node gpm ft psi mg/L + -------------------------------------------------------- + 1 -381.92 297.99 107.45 0.15 + 2 4.80 296.33 85.07 0.13 + 3 8.40 296.11 102.30 0.13 + 4 4.80 295.95 102.24 0.13 + 5 4.80 295.94 84.90 0.13 + 6 3.00 295.19 73.75 0.13 + 7 2.40 293.55 57.87 0.93 + 8 5.40 293.55 79.53 0.19 + 9 8.40 293.32 49.10 0.93 + 10 3.00 293.55 70.87 0.53 + 11 20.87 292.94 46.77 0.93 + 12 9.60 292.01 35.53 0.93 + 13 1.20 291.73 35.41 0.93 + 14 1.20 291.60 39.69 0.93 + 15 1.20 291.53 43.99 0.93 + 16 12.00 291.55 61.33 0.93 + 17 12.00 291.53 48.32 0.91 + 18 12.00 291.53 82.99 0.91 + 19 3.00 291.53 61.32 0.14 + 20 11.40 291.60 52.69 0.93 + 21 9.60 291.59 61.35 0.24 + 22 6.00 291.59 39.69 0.17 + 23 4.80 291.31 26.57 0.93 + 24 6.60 291.46 43.96 0.93 + 25 3.60 291.24 26.54 0.93 + 27 4.80 291.24 69.86 0.93 + 28 0.00 291.24 78.53 0.99 + 29 4.20 291.24 78.53 0.93 + 30 1.80 291.24 69.86 1.00 + 31 10.20 291.24 43.87 0.93 + 32 10.20 291.53 78.65 0.78 + 33 0.90 291.59 48.35 1.03 + 34 0.90 291.59 44.02 1.00 + 35 0.00 291.24 78.53 1.00 + 36 0.60 291.24 78.53 1.00 + 26 188.25 291.20 24.35 0.93 Tank + + + Link Results at 24:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 1 381.92 1.08 0.69 + 2 315.71 0.90 0.49 + 3 61.41 0.39 0.17 + 4 53.01 0.34 0.13 + 5 48.21 0.14 0.02 + 6 359.12 1.02 0.62 + 7 356.12 1.01 0.61 + 8 8.40 0.02 0.00 + 9 345.32 0.98 0.58 + 10 3.00 0.02 0.00 + 11 336.92 0.96 0.55 + 12 316.05 0.90 0.49 + 13 306.45 0.87 0.46 + 14 253.33 0.72 0.32 + 15 223.33 0.63 0.26 + 16 51.92 0.33 0.12 + 17 -2.72 0.02 0.00 + 18 25.35 0.16 0.03 + 19 10.63 0.03 0.00 + 20 -1.37 0.00 0.00 + 21 14.57 0.09 0.01 + 22 28.80 0.08 0.01 + 23 8.73 0.06 0.00 + 24 -0.87 0.01 0.00 + 25 8.67 0.06 0.00 + 26 218.25 0.62 0.25 + 27 224.85 0.64 0.26 + 28 213.45 0.61 0.24 + 29 188.25 0.53 0.19 + 30 21.60 0.06 0.00 + 31 11.40 0.07 0.01 + 32 6.60 0.04 0.00 + 34 1.03 0.01 0.00 + 35 1.80 0.01 0.00 + 36 0.90 0.01 0.00 + 37 -11.57 0.07 0.01 + 38 1.37 0.01 0.00 + 39 1.80 0.01 0.00 + 40 0.43 0.00 0.00 + 41 0.60 0.00 0.00 + + + Node Results at 25:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Fluoride + Node gpm ft psi mg/L + -------------------------------------------------------- + 1 -638.85 309.19 112.31 0.15 + 2 8.80 304.88 88.77 0.15 + 3 15.40 304.30 105.86 0.13 + 4 8.80 303.91 105.69 0.13 + 5 8.80 303.87 88.34 0.15 + 6 5.50 301.97 76.68 0.15 + 7 4.40 297.76 59.69 0.13 + 8 9.90 297.76 81.36 0.13 + 9 15.40 297.17 50.77 0.13 + 10 5.50 297.76 72.69 0.53 + 11 38.26 296.20 48.18 0.13 + 12 17.60 293.89 36.35 0.93 + 13 2.20 293.20 36.05 0.93 + 14 2.20 292.88 40.24 0.93 + 15 2.20 292.69 44.50 0.93 + 16 22.00 292.74 61.85 0.93 + 17 22.00 292.69 48.83 0.93 + 18 22.00 292.69 83.49 0.91 + 19 5.50 292.70 61.83 0.14 + 20 20.90 292.86 53.23 0.93 + 21 17.60 292.84 61.89 0.94 + 22 11.00 292.84 40.23 0.93 + 23 8.80 292.21 26.96 0.93 + 24 12.10 292.55 44.43 0.93 + 25 6.60 292.05 26.89 0.93 + 27 8.80 292.04 70.21 0.93 + 28 0.00 292.03 78.88 1.02 + 29 7.70 292.03 78.88 0.93 + 30 3.30 292.03 70.21 1.00 + 31 18.70 292.05 44.22 0.93 + 32 18.70 292.69 79.16 0.19 + 33 1.65 292.84 48.89 1.04 + 34 1.65 292.84 44.56 1.00 + 35 0.00 292.03 78.88 1.00 + 36 1.10 292.03 78.88 1.00 + 26 283.79 291.97 24.69 0.93 Tank + + + Link Results at 25:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 1 638.85 1.81 1.80 + 2 526.87 1.49 1.26 + 3 103.17 0.66 0.44 + 4 87.77 0.56 0.33 + 5 78.97 0.22 0.04 + 6 597.05 1.69 1.59 + 7 591.55 1.68 1.56 + 8 15.40 0.04 0.00 + 9 571.75 1.62 1.46 + 10 5.50 0.04 0.00 + 11 556.35 1.58 1.39 + 12 518.09 1.47 1.22 + 13 500.49 1.42 1.14 + 14 413.33 1.17 0.80 + 15 358.33 1.02 0.62 + 16 84.96 0.54 0.31 + 17 5.24 0.03 0.00 + 18 39.67 0.25 0.08 + 19 22.91 0.06 0.00 + 20 0.91 0.00 0.00 + 21 23.29 0.15 0.03 + 22 52.80 0.15 0.02 + 23 16.01 0.10 0.01 + 24 -1.59 0.01 0.00 + 25 15.89 0.10 0.01 + 26 338.79 0.96 0.56 + 27 350.89 1.00 0.59 + 28 329.99 0.94 0.53 + 29 283.79 0.81 0.40 + 30 39.60 0.11 0.01 + 31 20.90 0.13 0.02 + 32 12.10 0.08 0.01 + 34 1.89 0.01 0.00 + 35 3.30 0.02 0.00 + 36 1.65 0.01 0.00 + 37 -17.79 0.11 0.02 + 38 2.51 0.02 0.00 + 39 3.30 0.02 0.00 + 40 0.79 0.01 0.00 + 41 1.10 0.01 0.00 + + + Node Results at 26:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Fluoride + Node gpm ft psi mg/L + -------------------------------------------------------- + 1 -638.85 310.62 112.93 0.12 + 2 8.24 306.31 89.39 0.12 + 3 14.42 305.74 106.48 0.13 + 4 8.24 305.34 106.30 0.15 + 5 8.24 305.30 88.96 0.12 + 6 5.15 303.38 77.29 0.12 + 7 4.12 299.13 60.29 0.15 + 8 9.27 299.13 81.95 0.13 + 9 14.42 298.54 51.36 0.15 + 10 5.15 299.13 73.28 0.53 + 11 35.82 297.55 48.77 0.15 + 12 16.48 295.17 36.90 0.13 + 13 2.06 294.46 36.60 0.13 + 14 2.06 294.13 40.79 0.13 + 15 2.06 293.93 45.03 0.13 + 16 20.60 293.98 62.39 0.64 + 17 20.60 293.93 49.37 0.93 + 18 20.60 293.93 84.03 0.91 + 19 5.15 293.94 62.37 0.93 + 20 19.57 294.11 53.78 0.93 + 21 16.48 294.10 62.44 0.94 + 22 10.30 294.10 40.77 0.93 + 23 8.24 293.40 27.47 0.13 + 24 11.33 293.77 44.96 0.13 + 25 6.18 293.23 27.40 0.14 + 27 8.24 293.21 70.72 0.93 + 28 0.00 293.21 79.38 1.04 + 29 7.21 293.21 79.38 0.93 + 30 3.09 293.21 70.72 1.00 + 31 17.51 293.22 44.73 0.93 + 32 17.51 293.93 79.70 0.14 + 33 1.54 294.09 49.44 1.00 + 34 1.54 294.09 45.10 1.00 + 35 0.00 293.21 79.38 1.00 + 36 1.03 293.21 79.38 1.00 + 26 306.38 293.13 25.19 0.93 Tank + + + Link Results at 26:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 1 638.85 1.81 1.80 + 2 527.76 1.50 1.26 + 3 102.85 0.66 0.44 + 4 88.43 0.56 0.33 + 5 80.19 0.23 0.04 + 6 599.71 1.70 1.60 + 7 594.56 1.69 1.57 + 8 14.42 0.04 0.00 + 9 576.02 1.63 1.48 + 10 5.15 0.03 0.00 + 11 561.60 1.59 1.42 + 12 525.77 1.49 1.25 + 13 509.29 1.44 1.18 + 14 420.87 1.19 0.83 + 15 369.37 1.05 0.65 + 16 86.36 0.55 0.32 + 17 -1.90 0.01 0.00 + 18 41.68 0.27 0.08 + 19 19.18 0.05 0.00 + 20 -1.42 0.00 0.00 + 21 24.08 0.15 0.03 + 22 49.44 0.14 0.02 + 23 14.99 0.10 0.01 + 24 -1.49 0.01 0.00 + 25 14.88 0.09 0.01 + 26 357.88 1.02 0.61 + 27 369.21 1.05 0.65 + 28 349.64 0.99 0.59 + 29 306.38 0.87 0.46 + 30 37.08 0.11 0.01 + 31 19.57 0.12 0.02 + 32 11.33 0.07 0.01 + 34 1.77 0.01 0.00 + 35 3.09 0.02 0.00 + 36 1.54 0.01 0.00 + 37 -18.93 0.12 0.02 + 38 2.35 0.01 0.00 + 39 3.09 0.02 0.00 + 40 0.74 0.00 0.00 + 41 1.03 0.01 0.00 + + + Node Results at 27:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Fluoride + Node gpm ft psi mg/L + -------------------------------------------------------- + 1 -638.85 313.11 114.01 0.10 + 2 5.84 308.80 90.47 0.10 + 3 10.22 308.24 107.56 0.10 + 4 5.84 307.82 107.38 0.12 + 5 5.84 307.78 90.03 0.10 + 6 3.65 305.79 78.34 0.10 + 7 2.92 301.37 61.26 0.12 + 8 6.57 301.37 82.92 0.13 + 9 10.22 300.75 52.32 0.12 + 10 3.65 301.37 74.26 0.59 + 11 25.39 299.68 49.69 0.12 + 12 11.68 297.02 37.70 0.12 + 13 1.46 296.21 37.35 0.12 + 14 1.46 295.82 41.52 0.13 + 15 1.46 295.57 45.74 0.14 + 16 14.60 295.70 63.13 0.14 + 17 14.60 295.64 50.11 0.13 + 18 14.60 295.64 84.77 0.91 + 19 3.65 295.65 63.11 0.93 + 20 13.87 295.81 54.51 0.93 + 21 11.68 295.80 63.18 0.94 + 22 7.30 295.80 41.51 0.93 + 23 5.84 294.80 28.08 0.15 + 24 8.03 295.34 45.64 0.15 + 25 4.38 294.54 27.96 0.15 + 27 5.84 294.53 71.29 0.93 + 28 0.00 294.53 79.96 0.99 + 29 5.11 294.53 79.96 0.93 + 30 2.19 294.53 71.29 1.00 + 31 12.41 294.53 45.29 0.93 + 32 12.41 295.64 80.44 0.93 + 33 1.09 295.80 50.18 0.70 + 34 1.09 295.80 45.84 1.00 + 35 0.00 294.53 79.96 1.00 + 36 0.73 294.53 79.96 1.00 + 26 403.22 294.38 25.73 0.91 Tank + + + Link Results at 27:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 1 638.85 1.81 1.80 + 2 531.59 1.51 1.28 + 3 101.42 0.65 0.43 + 4 91.20 0.58 0.35 + 5 85.36 0.24 0.04 + 6 611.11 1.73 1.66 + 7 607.46 1.72 1.64 + 8 10.22 0.03 0.00 + 9 594.32 1.69 1.57 + 10 3.65 0.02 0.00 + 11 584.10 1.66 1.52 + 12 558.71 1.58 1.40 + 13 547.03 1.55 1.35 + 14 456.50 1.29 0.96 + 15 420.00 1.19 0.83 + 16 89.07 0.57 0.34 + 17 -29.21 0.19 0.04 + 18 47.89 0.31 0.11 + 19 4.08 0.01 0.00 + 20 -10.52 0.03 0.00 + 21 26.58 0.17 0.04 + 22 35.04 0.10 0.01 + 23 10.62 0.07 0.01 + 24 -1.06 0.01 0.00 + 25 10.55 0.07 0.01 + 26 439.72 1.25 0.90 + 27 447.75 1.27 0.93 + 28 433.88 1.23 0.88 + 29 403.22 1.14 0.77 + 30 26.28 0.07 0.00 + 31 13.87 0.09 0.01 + 32 8.03 0.05 0.00 + 34 1.26 0.01 0.00 + 35 2.19 0.01 0.00 + 36 1.09 0.01 0.00 + 37 -22.93 0.15 0.03 + 38 1.66 0.01 0.00 + 39 2.19 0.01 0.00 + 40 0.53 0.00 0.00 + 41 0.73 0.00 0.00 + + + Node Results at 28:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Fluoride + Node gpm ft psi mg/L + -------------------------------------------------------- + 1 -638.85 314.12 114.45 0.08 + 2 7.04 309.81 90.91 0.08 + 3 12.32 309.25 108.00 0.08 + 4 7.04 308.84 107.82 0.10 + 5 7.04 308.80 90.47 0.09 + 6 4.40 306.84 78.79 0.09 + 7 3.52 302.51 61.75 0.11 + 8 7.92 302.51 83.41 0.13 + 9 12.32 301.90 52.82 0.11 + 10 4.40 302.51 74.75 0.69 + 11 30.61 300.87 50.21 0.11 + 12 14.08 298.35 38.28 0.11 + 13 1.76 297.59 37.95 0.11 + 14 1.76 297.24 42.13 0.11 + 15 1.76 297.02 46.37 0.16 + 16 17.60 297.10 63.74 0.12 + 17 17.60 297.04 50.71 0.15 + 18 17.60 297.04 85.38 0.91 + 19 4.40 297.05 63.72 0.93 + 20 16.72 297.22 55.13 0.13 + 21 14.08 297.21 63.79 0.94 + 22 8.80 297.21 42.12 0.93 + 23 7.04 296.37 28.76 0.17 + 24 9.68 296.82 46.29 0.17 + 25 5.28 296.15 28.66 0.17 + 27 7.04 296.14 71.99 0.93 + 28 0.00 296.14 80.65 0.99 + 29 6.16 296.14 80.65 0.93 + 30 2.64 296.14 71.99 1.00 + 31 14.96 296.15 45.99 0.14 + 32 14.96 297.04 81.04 0.93 + 33 1.32 297.21 50.79 0.70 + 34 1.32 297.21 46.46 1.00 + 35 0.00 296.14 80.65 1.00 + 36 0.88 296.14 80.65 1.00 + 26 354.80 296.03 26.45 0.89 Tank + + + Link Results at 28:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 1 638.85 1.81 1.80 + 2 529.67 1.50 1.27 + 3 102.14 0.65 0.43 + 4 89.82 0.57 0.34 + 5 82.78 0.23 0.04 + 6 605.41 1.72 1.63 + 7 601.01 1.70 1.60 + 8 12.32 0.03 0.00 + 9 585.17 1.66 1.53 + 10 4.40 0.03 0.00 + 11 572.85 1.63 1.47 + 12 542.24 1.54 1.33 + 13 528.16 1.50 1.26 + 14 438.01 1.24 0.89 + 15 394.01 1.12 0.73 + 16 88.39 0.56 0.33 + 17 -16.23 0.10 0.01 + 18 45.26 0.29 0.10 + 19 11.44 0.03 0.00 + 20 -6.16 0.02 0.00 + 21 25.52 0.16 0.03 + 22 42.24 0.12 0.01 + 23 12.81 0.08 0.01 + 24 -1.27 0.01 0.00 + 25 12.71 0.08 0.01 + 26 398.80 1.13 0.75 + 27 408.48 1.16 0.78 + 28 391.76 1.11 0.73 + 29 354.80 1.01 0.60 + 30 31.68 0.09 0.01 + 31 16.72 0.11 0.02 + 32 9.68 0.06 0.01 + 34 1.52 0.01 0.00 + 35 2.64 0.02 0.00 + 36 1.32 0.01 0.00 + 37 -21.12 0.13 0.02 + 38 2.00 0.01 0.00 + 39 2.64 0.02 0.00 + 40 0.64 0.00 0.00 + 41 0.88 0.01 0.00 + + + Node Results at 29:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Fluoride + Node gpm ft psi mg/L + -------------------------------------------------------- + 1 -624.96 314.07 114.42 0.11 + 2 8.48 309.93 90.96 0.11 + 3 14.84 309.38 108.06 0.10 + 4 8.48 309.00 107.89 0.08 + 5 8.48 308.96 90.54 0.11 + 6 5.30 307.13 78.92 0.11 + 7 4.24 303.08 62.00 0.09 + 8 9.54 303.08 83.66 0.13 + 9 14.84 302.52 53.09 0.09 + 10 5.30 303.08 75.00 0.81 + 11 36.87 301.58 50.52 0.09 + 12 16.96 299.34 38.71 0.09 + 13 2.12 298.68 38.43 0.09 + 14 2.12 298.37 42.62 0.09 + 15 2.12 298.19 46.88 0.13 + 16 21.20 298.23 64.23 0.11 + 17 21.20 298.19 51.21 0.12 + 18 21.20 298.19 85.87 0.91 + 19 5.30 298.19 64.21 0.13 + 20 20.14 298.35 55.61 0.14 + 21 16.96 298.33 64.27 0.94 + 22 10.60 298.33 42.61 0.93 + 23 8.48 297.72 29.34 0.14 + 24 11.66 298.04 46.82 0.14 + 25 6.36 297.56 29.27 0.14 + 27 8.48 297.55 72.60 0.14 + 28 0.00 297.54 81.26 0.96 + 29 7.42 297.54 81.26 0.93 + 30 3.18 297.54 72.60 1.00 + 31 18.02 297.55 46.60 0.14 + 32 18.02 298.19 81.54 0.93 + 33 1.59 298.33 51.27 1.00 + 34 1.59 298.33 46.94 1.00 + 35 0.00 297.54 81.26 0.57 + 36 1.06 297.54 81.26 1.00 + 26 282.81 297.48 27.07 0.87 Tank + + + Link Results at 29:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 1 624.96 1.77 1.73 + 2 515.62 1.46 1.21 + 3 100.86 0.64 0.42 + 4 86.02 0.55 0.32 + 5 77.54 0.22 0.04 + 6 584.68 1.66 1.52 + 7 579.38 1.64 1.50 + 8 14.84 0.04 0.00 + 9 560.30 1.59 1.41 + 10 5.30 0.03 0.00 + 11 545.46 1.55 1.34 + 12 508.59 1.44 1.18 + 13 491.63 1.39 1.11 + 14 406.12 1.15 0.78 + 15 353.12 1.00 0.60 + 16 83.39 0.53 0.30 + 17 3.53 0.02 0.00 + 18 39.24 0.25 0.07 + 19 21.57 0.06 0.00 + 20 0.37 0.00 0.00 + 21 22.95 0.15 0.03 + 22 50.88 0.14 0.02 + 23 15.43 0.10 0.01 + 24 -1.53 0.01 0.00 + 25 15.31 0.10 0.01 + 26 335.81 0.95 0.55 + 27 347.47 0.99 0.58 + 28 327.33 0.93 0.52 + 29 282.81 0.80 0.40 + 30 38.16 0.11 0.01 + 31 20.14 0.13 0.02 + 32 11.66 0.07 0.01 + 34 1.83 0.01 0.00 + 35 3.18 0.02 0.00 + 36 1.59 0.01 0.00 + 37 -17.65 0.11 0.02 + 38 2.41 0.02 0.00 + 39 3.18 0.02 0.00 + 40 0.77 0.00 0.00 + 41 1.06 0.01 0.00 + + + Node Results at 30:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Fluoride + Node gpm ft psi mg/L + -------------------------------------------------------- + 1 -624.96 315.49 115.04 0.09 + 2 7.92 311.35 91.58 0.09 + 3 13.86 310.81 108.67 0.10 + 4 7.92 310.42 108.51 0.11 + 5 7.92 310.38 91.16 0.09 + 6 4.95 308.54 79.53 0.09 + 7 3.96 304.45 62.59 0.11 + 8 8.91 304.45 84.26 0.13 + 9 13.86 303.88 53.68 0.11 + 10 4.95 304.45 75.59 1.00 + 11 34.43 302.92 51.10 0.11 + 12 15.84 300.62 39.27 0.11 + 13 1.98 299.94 38.97 0.11 + 14 1.98 299.62 43.16 0.10 + 15 1.98 299.42 47.41 0.09 + 16 19.80 299.47 64.77 0.09 + 17 19.80 299.43 51.75 0.11 + 18 19.80 299.42 86.41 0.84 + 19 4.95 299.43 64.75 0.14 + 20 18.81 299.60 56.16 0.12 + 21 15.84 299.58 64.81 0.94 + 22 9.90 299.58 43.15 0.93 + 23 7.92 298.90 29.86 0.09 + 24 10.89 299.26 47.34 0.09 + 25 5.94 298.73 29.78 0.09 + 27 7.92 298.72 73.10 0.14 + 28 0.00 298.71 81.77 1.00 + 29 6.93 298.71 81.77 0.92 + 30 2.97 298.71 73.10 1.00 + 31 16.83 298.72 47.11 0.16 + 32 16.83 299.42 82.08 0.15 + 33 1.49 299.58 51.82 1.00 + 34 1.49 299.58 47.48 1.00 + 35 0.00 298.71 81.77 0.40 + 36 0.99 298.71 81.77 1.00 + 26 305.41 298.64 27.57 0.86 Tank + + + Link Results at 30:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 1 624.96 1.77 1.73 + 2 516.51 1.47 1.21 + 3 100.53 0.64 0.42 + 4 86.67 0.55 0.32 + 5 78.75 0.22 0.04 + 6 587.34 1.67 1.54 + 7 582.39 1.65 1.51 + 8 13.86 0.04 0.00 + 9 564.57 1.60 1.43 + 10 4.95 0.03 0.00 + 11 550.71 1.56 1.36 + 12 516.28 1.46 1.21 + 13 500.44 1.42 1.14 + 14 413.64 1.17 0.80 + 15 364.14 1.03 0.63 + 16 84.82 0.54 0.31 + 17 -3.64 0.02 0.00 + 18 41.26 0.26 0.08 + 19 17.82 0.05 0.00 + 20 -1.98 0.01 0.00 + 21 23.76 0.15 0.03 + 22 47.52 0.13 0.01 + 23 14.41 0.09 0.01 + 24 -1.43 0.01 0.00 + 25 14.30 0.09 0.01 + 26 354.91 1.01 0.60 + 27 365.80 1.04 0.64 + 28 346.99 0.98 0.58 + 29 305.41 0.87 0.46 + 30 35.64 0.10 0.01 + 31 18.81 0.12 0.02 + 32 10.89 0.07 0.01 + 34 1.70 0.01 0.00 + 35 2.97 0.02 0.00 + 36 1.49 0.01 0.00 + 37 -18.81 0.12 0.02 + 38 2.26 0.01 0.00 + 39 2.97 0.02 0.00 + 40 0.71 0.00 0.00 + 41 0.99 0.01 0.00 + + + Node Results at 31:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Fluoride + Node gpm ft psi mg/L + -------------------------------------------------------- + 1 -312.48 302.62 109.46 0.09 + 2 13.76 301.47 87.30 0.09 + 3 24.08 301.29 104.55 0.09 + 4 13.76 301.23 104.53 0.09 + 5 13.76 301.23 87.19 0.09 + 6 8.60 300.86 76.20 0.09 + 7 6.88 300.08 60.69 0.09 + 8 15.48 300.07 82.36 0.81 + 9 24.08 299.99 51.99 0.09 + 10 8.60 300.07 73.69 0.57 + 11 59.82 299.86 49.77 0.09 + 12 27.52 299.70 38.87 0.09 + 13 3.44 299.67 38.85 0.10 + 14 3.44 299.66 43.18 0.10 + 15 3.44 299.67 47.52 0.11 + 16 34.40 299.35 64.71 0.11 + 17 34.40 299.34 51.71 0.09 + 18 34.40 299.32 86.37 0.93 + 19 8.60 299.32 64.70 0.12 + 20 32.68 299.62 56.16 0.11 + 21 27.52 299.58 64.81 0.94 + 22 17.20 299.58 43.15 0.93 + 23 13.76 299.78 30.23 0.11 + 24 18.92 299.70 47.53 0.10 + 25 10.32 299.82 30.25 0.11 + 27 13.76 299.79 73.57 0.14 + 28 0.00 299.78 82.23 1.00 + 29 12.04 299.78 82.23 0.14 + 30 5.16 299.78 73.57 0.99 + 31 29.24 299.81 47.58 0.10 + 32 29.24 299.32 82.03 0.14 + 33 2.58 299.58 51.81 1.00 + 34 2.58 299.58 47.48 1.00 + 35 0.00 299.78 82.23 0.41 + 36 1.72 299.78 82.23 1.00 + 26 -242.70 299.88 28.11 0.84 Tank + + + Link Results at 31:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 1 312.48 0.89 0.48 + 2 243.49 0.69 0.30 + 3 55.23 0.35 0.14 + 4 31.15 0.20 0.05 + 5 17.39 0.05 0.00 + 6 247.12 0.70 0.31 + 7 238.52 0.68 0.29 + 8 24.08 0.07 0.00 + 9 207.56 0.59 0.22 + 10 8.60 0.05 0.00 + 11 183.48 0.52 0.18 + 12 123.66 0.35 0.09 + 13 96.14 0.27 0.05 + 14 23.12 0.07 0.00 + 15 -62.88 0.18 0.02 + 16 69.58 0.44 0.21 + 17 71.46 0.46 0.22 + 18 17.92 0.11 0.02 + 19 54.98 0.16 0.02 + 20 20.58 0.06 0.00 + 21 17.26 0.11 0.02 + 22 82.56 0.23 0.04 + 23 25.03 0.16 0.03 + 24 -2.49 0.02 0.00 + 25 24.85 0.16 0.03 + 26 -156.70 0.44 0.13 + 27 -137.78 0.39 0.10 + 28 -170.46 0.48 0.16 + 29 -242.70 0.69 0.30 + 30 61.92 0.18 0.02 + 31 32.68 0.21 0.05 + 32 18.92 0.12 0.02 + 34 2.96 0.02 0.00 + 35 5.16 0.03 0.00 + 36 2.58 0.02 0.00 + 37 -8.66 0.06 0.00 + 38 3.92 0.03 0.00 + 39 5.16 0.03 0.00 + 40 1.24 0.01 0.00 + 41 1.72 0.01 0.00 + + + Node Results at 32:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Fluoride + Node gpm ft psi mg/L + -------------------------------------------------------- + 1 0.00 297.77 107.36 0.08 + 2 8.96 297.77 85.69 0.08 + 3 15.68 297.76 103.02 0.09 + 4 8.96 297.77 103.02 0.09 + 5 8.96 297.77 85.69 0.09 + 6 5.60 297.78 74.87 0.09 + 7 4.48 297.82 59.72 0.09 + 8 10.08 297.82 81.38 0.93 + 9 15.68 297.84 51.06 0.09 + 10 5.60 297.82 72.72 0.22 + 11 38.95 297.86 48.90 0.09 + 12 17.92 298.03 38.14 0.09 + 13 2.24 298.09 38.17 0.09 + 14 2.24 298.16 42.53 0.10 + 15 2.24 298.24 46.90 0.84 + 16 22.40 298.01 64.13 0.09 + 17 22.40 298.01 51.13 0.11 + 18 22.40 298.00 85.79 0.10 + 19 5.60 298.00 64.13 0.11 + 20 21.28 298.14 55.52 0.10 + 21 17.92 298.12 64.18 0.21 + 22 11.20 298.12 42.52 0.13 + 23 8.96 298.62 29.73 0.84 + 24 12.32 298.35 46.95 0.84 + 25 6.72 298.77 29.80 0.84 + 27 8.96 298.75 73.12 0.09 + 28 0.00 298.75 81.78 1.00 + 29 7.84 298.75 81.78 0.14 + 30 3.36 298.75 73.12 1.03 + 31 19.04 298.76 47.13 0.11 + 32 19.04 298.00 81.46 0.28 + 33 1.68 298.12 51.18 1.00 + 34 1.68 298.12 46.85 1.00 + 35 0.00 298.75 81.78 0.95 + 36 1.12 298.75 81.78 1.00 + 26 -361.51 298.89 27.68 0.84 Tank + + + Link Results at 32:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 1 -0.00 0.00 0.00 + 2 -16.82 0.05 0.00 + 3 7.86 0.05 0.00 + 4 -7.82 0.05 0.00 + 5 -16.78 0.05 0.00 + 6 -42.56 0.12 0.01 + 7 -48.16 0.14 0.01 + 8 15.68 0.04 0.00 + 9 -68.32 0.19 0.03 + 10 5.60 0.04 0.00 + 11 -84.00 0.24 0.04 + 12 -122.95 0.35 0.08 + 13 -140.87 0.40 0.11 + 14 -176.51 0.50 0.17 + 15 -232.51 0.66 0.28 + 16 33.40 0.21 0.05 + 17 58.44 0.37 0.15 + 18 1.79 0.01 0.00 + 19 37.84 0.11 0.01 + 20 15.44 0.04 0.00 + 21 9.20 0.06 0.01 + 22 53.76 0.15 0.02 + 23 16.30 0.10 0.01 + 24 -1.62 0.01 0.00 + 25 16.18 0.10 0.01 + 26 -305.51 0.87 0.46 + 27 -293.19 0.83 0.42 + 28 -314.47 0.89 0.48 + 29 -361.51 1.03 0.63 + 30 40.32 0.11 0.01 + 31 21.28 0.14 0.02 + 32 12.32 0.08 0.01 + 34 1.93 0.01 0.00 + 35 3.36 0.02 0.00 + 36 1.68 0.01 0.00 + 37 -3.60 0.02 0.00 + 38 2.55 0.02 0.00 + 39 3.36 0.02 0.00 + 40 0.81 0.01 0.00 + 41 1.12 0.01 0.00 + + + Node Results at 33:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Fluoride + Node gpm ft psi mg/L + -------------------------------------------------------- + 1 0.00 295.85 106.53 0.08 + 2 10.72 295.85 84.86 0.08 + 3 18.76 295.84 102.19 0.09 + 4 10.72 295.85 102.19 0.09 + 5 10.72 295.85 84.86 0.09 + 6 6.70 295.87 74.04 0.09 + 7 5.36 295.93 58.90 0.09 + 8 12.06 295.92 80.56 0.13 + 9 18.76 295.94 50.24 0.09 + 10 6.70 295.92 71.89 0.16 + 11 46.61 295.98 48.09 0.09 + 12 21.44 296.21 37.35 0.84 + 13 2.68 296.30 37.39 0.84 + 14 2.68 296.39 41.77 0.84 + 15 2.68 296.51 46.15 0.84 + 16 26.80 296.18 63.34 0.09 + 17 26.80 296.18 50.34 0.75 + 18 26.80 296.18 85.00 0.13 + 19 6.70 296.18 63.34 0.09 + 20 25.46 296.36 54.75 0.10 + 21 21.44 296.34 63.41 0.20 + 22 13.40 296.34 41.74 0.12 + 23 10.72 297.04 29.05 0.84 + 24 14.74 296.66 46.21 0.84 + 25 8.04 297.24 29.14 0.84 + 27 10.72 297.22 72.46 0.84 + 28 0.00 297.21 81.12 1.00 + 29 9.38 297.21 81.12 0.09 + 30 4.02 297.21 72.45 1.00 + 31 22.78 297.23 46.46 0.84 + 32 22.78 296.17 80.67 0.75 + 33 2.01 296.34 50.41 1.00 + 34 2.01 296.34 46.08 1.00 + 35 0.00 297.21 81.12 0.95 + 36 1.34 297.21 81.12 1.00 + 26 -432.53 297.42 27.04 0.84 Tank + + + Link Results at 33:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 1 -0.00 0.00 0.00 + 2 -20.12 0.06 0.00 + 3 9.40 0.06 0.01 + 4 -9.36 0.06 0.01 + 5 -20.08 0.06 0.00 + 6 -50.92 0.14 0.02 + 7 -57.62 0.16 0.02 + 8 18.76 0.05 0.00 + 9 -81.74 0.23 0.04 + 10 6.70 0.04 0.00 + 11 -100.50 0.29 0.06 + 12 -147.11 0.42 0.12 + 13 -168.55 0.48 0.15 + 14 -211.18 0.60 0.23 + 15 -278.18 0.79 0.39 + 16 39.96 0.26 0.08 + 17 69.92 0.45 0.22 + 18 2.15 0.01 0.00 + 19 45.27 0.13 0.01 + 20 18.47 0.05 0.00 + 21 11.01 0.07 0.01 + 22 64.32 0.18 0.03 + 23 19.50 0.12 0.02 + 24 -1.94 0.01 0.00 + 25 19.36 0.12 0.02 + 26 -365.53 1.04 0.64 + 27 -350.79 1.00 0.59 + 28 -376.25 1.07 0.67 + 29 -432.53 1.23 0.87 + 30 48.24 0.14 0.02 + 31 25.46 0.16 0.03 + 32 14.74 0.09 0.01 + 34 2.31 0.01 0.00 + 35 4.02 0.03 0.00 + 36 2.01 0.01 0.00 + 37 -4.31 0.03 0.00 + 38 3.05 0.02 0.00 + 39 4.02 0.03 0.00 + 40 0.97 0.01 0.00 + 41 1.34 0.01 0.00 + + + Node Results at 34:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Fluoride + Node gpm ft psi mg/L + -------------------------------------------------------- + 1 0.00 294.52 105.95 0.08 + 2 8.96 294.52 84.29 0.09 + 3 15.68 294.52 101.62 0.09 + 4 8.96 294.52 101.62 0.09 + 5 8.96 294.53 84.29 0.09 + 6 5.60 294.54 73.46 0.09 + 7 4.48 294.58 58.31 0.09 + 8 10.08 294.58 79.98 0.12 + 9 15.68 294.59 49.65 0.09 + 10 5.60 294.58 71.31 0.13 + 11 38.95 294.62 47.50 0.18 + 12 17.92 294.78 36.74 0.84 + 13 2.24 294.85 36.76 0.84 + 14 2.24 294.91 41.13 0.84 + 15 2.24 295.00 45.50 0.84 + 16 22.40 294.77 62.73 0.12 + 17 22.40 294.77 49.73 0.82 + 18 22.40 294.76 84.39 0.11 + 19 5.60 294.76 62.72 0.09 + 20 21.28 294.89 54.12 0.83 + 21 17.92 294.88 62.77 0.17 + 22 11.20 294.88 41.11 0.09 + 23 8.96 295.38 28.33 0.84 + 24 12.32 295.10 45.54 0.84 + 25 6.72 295.52 28.39 0.84 + 27 8.96 295.51 71.71 0.84 + 28 0.00 295.50 80.38 1.00 + 29 7.84 295.50 80.38 0.12 + 30 3.36 295.50 71.71 0.99 + 31 19.04 295.52 45.72 0.84 + 32 19.04 294.76 80.06 0.11 + 33 1.68 294.87 49.78 0.86 + 34 1.68 294.87 45.44 0.99 + 35 0.00 295.50 80.38 0.95 + 36 1.12 295.50 80.38 1.00 + 26 -361.51 295.65 26.28 0.84 Tank + + + Link Results at 34:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 1 -0.00 0.00 0.00 + 2 -16.82 0.05 0.00 + 3 7.86 0.05 0.00 + 4 -7.82 0.05 0.00 + 5 -16.78 0.05 0.00 + 6 -42.56 0.12 0.01 + 7 -48.16 0.14 0.01 + 8 15.68 0.04 0.00 + 9 -68.32 0.19 0.03 + 10 5.60 0.04 0.00 + 11 -84.00 0.24 0.04 + 12 -122.95 0.35 0.08 + 13 -140.87 0.40 0.11 + 14 -176.51 0.50 0.17 + 15 -232.51 0.66 0.28 + 16 33.40 0.21 0.05 + 17 58.44 0.37 0.15 + 18 1.79 0.01 0.00 + 19 37.84 0.11 0.01 + 20 15.44 0.04 0.00 + 21 9.20 0.06 0.01 + 22 53.76 0.15 0.02 + 23 16.30 0.10 0.01 + 24 -1.62 0.01 0.00 + 25 16.18 0.10 0.01 + 26 -305.51 0.87 0.46 + 27 -293.19 0.83 0.42 + 28 -314.47 0.89 0.48 + 29 -361.51 1.03 0.63 + 30 40.32 0.11 0.01 + 31 21.28 0.14 0.02 + 32 12.32 0.08 0.01 + 34 1.93 0.01 0.00 + 35 3.36 0.02 0.00 + 36 1.68 0.01 0.00 + 37 -3.60 0.02 0.00 + 38 2.55 0.02 0.00 + 39 3.36 0.02 0.00 + 40 0.81 0.01 0.00 + 41 1.12 0.01 0.00 + + + Node Results at 35:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Fluoride + Node gpm ft psi mg/L + -------------------------------------------------------- + 1 0.00 293.31 105.43 0.08 + 2 7.76 293.31 83.76 0.09 + 3 13.58 293.31 101.09 0.09 + 4 7.76 293.31 101.09 0.11 + 5 7.76 293.31 83.76 0.09 + 6 4.85 293.32 72.93 0.09 + 7 3.88 293.35 57.78 0.09 + 8 8.73 293.35 79.45 0.11 + 9 13.58 293.36 49.12 0.84 + 10 4.85 293.35 70.78 0.13 + 11 33.74 293.38 46.96 0.84 + 12 15.52 293.51 36.18 0.84 + 13 1.94 293.56 36.21 0.84 + 14 1.94 293.61 40.56 0.84 + 15 1.94 293.67 44.92 0.84 + 16 19.40 293.50 62.18 0.84 + 17 19.40 293.50 49.18 0.82 + 18 19.40 293.49 83.84 0.82 + 19 4.85 293.49 62.17 0.09 + 20 18.43 293.59 53.55 0.84 + 21 15.52 293.58 62.21 0.18 + 22 9.70 293.58 40.55 0.10 + 23 7.76 293.96 27.72 0.84 + 24 10.67 293.75 44.96 0.84 + 25 5.82 294.08 27.76 0.84 + 27 7.76 294.06 71.09 0.84 + 28 0.00 294.06 79.75 1.00 + 29 6.79 294.06 79.75 0.84 + 30 2.91 294.06 71.09 1.00 + 31 16.49 294.07 45.09 0.84 + 32 16.49 293.49 79.51 0.13 + 33 1.45 293.58 49.21 0.21 + 34 1.45 293.58 44.88 1.04 + 35 0.00 294.06 79.75 0.95 + 36 0.97 294.06 79.75 1.00 + 26 -313.10 294.17 25.64 0.84 Tank + + + Link Results at 35:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 1 -0.00 0.00 0.00 + 2 -14.57 0.04 0.00 + 3 6.81 0.04 0.00 + 4 -6.77 0.04 0.00 + 5 -14.53 0.04 0.00 + 6 -36.86 0.10 0.01 + 7 -41.71 0.12 0.01 + 8 13.58 0.04 0.00 + 9 -59.17 0.17 0.02 + 10 4.85 0.03 0.00 + 11 -72.75 0.21 0.03 + 12 -106.49 0.30 0.07 + 13 -122.01 0.35 0.08 + 14 -152.87 0.43 0.13 + 15 -201.37 0.57 0.21 + 16 28.92 0.18 0.04 + 17 50.62 0.32 0.12 + 18 1.55 0.01 0.00 + 19 32.77 0.09 0.01 + 20 13.37 0.04 0.00 + 21 7.97 0.05 0.00 + 22 46.56 0.13 0.01 + 23 14.12 0.09 0.01 + 24 -1.40 0.01 0.00 + 25 14.01 0.09 0.01 + 26 -264.60 0.75 0.35 + 27 -253.93 0.72 0.33 + 28 -272.36 0.77 0.37 + 29 -313.10 0.89 0.48 + 30 34.92 0.10 0.01 + 31 18.43 0.12 0.02 + 32 10.67 0.07 0.01 + 34 1.67 0.01 0.00 + 35 2.91 0.02 0.00 + 36 1.45 0.01 0.00 + 37 -3.12 0.02 0.00 + 38 2.21 0.01 0.00 + 39 2.91 0.02 0.00 + 40 0.70 0.00 0.00 + 41 0.97 0.01 0.00 + + + Node Results at 36:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Fluoride + Node gpm ft psi mg/L + -------------------------------------------------------- + 1 0.00 291.91 104.82 0.08 + 2 8.32 291.91 83.16 0.09 + 3 14.56 291.91 100.49 0.09 + 4 8.32 291.91 100.49 0.11 + 5 8.32 291.91 83.16 0.09 + 6 5.20 291.93 72.33 0.09 + 7 4.16 291.96 57.18 0.84 + 8 9.36 291.96 78.84 0.09 + 9 14.56 291.97 48.52 0.84 + 10 5.20 291.96 70.18 0.13 + 11 36.17 292.00 46.36 0.84 + 12 16.64 292.14 35.59 0.84 + 13 2.08 292.20 35.62 0.84 + 14 2.08 292.25 39.97 0.84 + 15 2.08 292.33 44.34 0.84 + 16 20.80 292.12 61.58 0.84 + 17 20.80 292.12 48.58 0.82 + 18 20.80 292.12 83.24 0.82 + 19 5.20 292.12 61.58 0.10 + 20 19.76 292.24 52.96 0.84 + 21 16.64 292.22 61.62 0.18 + 22 10.40 292.22 39.96 0.10 + 23 8.32 292.66 27.15 0.84 + 24 11.44 292.42 44.38 0.84 + 25 6.24 292.78 27.20 0.84 + 27 8.32 292.77 70.53 0.84 + 28 0.00 292.77 79.19 0.43 + 29 7.28 292.77 79.19 0.84 + 30 3.12 292.77 70.53 1.00 + 31 17.68 292.78 44.53 0.84 + 32 17.68 292.12 78.91 0.11 + 33 1.56 292.22 48.62 0.23 + 34 1.56 292.22 44.29 1.00 + 35 0.00 292.77 79.19 0.95 + 36 1.04 292.77 79.19 1.00 + 26 -335.69 292.89 25.09 0.84 Tank + + + Link Results at 36:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 1 -0.00 0.00 0.00 + 2 -15.62 0.04 0.00 + 3 7.30 0.05 0.00 + 4 -7.26 0.05 0.00 + 5 -15.58 0.04 0.00 + 6 -39.52 0.11 0.01 + 7 -44.72 0.13 0.01 + 8 14.56 0.04 0.00 + 9 -63.44 0.18 0.02 + 10 5.20 0.03 0.00 + 11 -78.00 0.22 0.04 + 12 -114.17 0.32 0.07 + 13 -130.81 0.37 0.10 + 14 -163.90 0.46 0.14 + 15 -215.90 0.61 0.24 + 16 31.01 0.20 0.05 + 17 54.27 0.35 0.13 + 18 1.67 0.01 0.00 + 19 35.14 0.10 0.01 + 20 14.34 0.04 0.00 + 21 8.54 0.05 0.00 + 22 49.92 0.14 0.02 + 23 15.14 0.10 0.01 + 24 -1.50 0.01 0.00 + 25 15.02 0.10 0.01 + 26 -283.69 0.80 0.40 + 27 -272.25 0.77 0.37 + 28 -292.01 0.83 0.42 + 29 -335.69 0.95 0.55 + 30 37.44 0.11 0.01 + 31 19.76 0.13 0.02 + 32 11.44 0.07 0.01 + 34 1.79 0.01 0.00 + 35 3.12 0.02 0.00 + 36 1.56 0.01 0.00 + 37 -3.34 0.02 0.00 + 38 2.37 0.02 0.00 + 39 3.12 0.02 0.00 + 40 0.75 0.00 0.00 + 41 1.04 0.01 0.00 + + + Node Results at 37:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Fluoride + Node gpm ft psi mg/L + -------------------------------------------------------- + 1 -486.08 301.00 108.76 0.08 + 2 9.20 298.40 85.97 0.09 + 3 16.10 298.04 103.14 0.09 + 4 9.20 297.82 103.05 0.11 + 5 9.20 297.80 85.71 0.09 + 6 5.75 296.71 74.40 0.09 + 7 4.60 294.31 58.20 0.84 + 8 10.35 294.31 79.86 0.11 + 9 16.10 293.99 49.39 0.84 + 10 5.75 294.31 71.20 0.13 + 11 40.00 293.46 47.00 0.84 + 12 18.40 292.28 35.65 0.84 + 13 2.30 291.94 35.51 0.84 + 14 2.30 291.79 39.77 0.84 + 15 2.30 291.72 44.08 0.84 + 16 23.00 291.67 61.39 0.84 + 17 23.00 291.65 48.38 0.82 + 18 23.00 291.65 83.04 0.82 + 19 5.75 291.65 61.38 0.10 + 20 21.85 291.77 52.76 0.84 + 21 18.40 291.75 61.42 0.18 + 22 11.50 291.75 39.76 0.10 + 23 9.20 291.58 26.68 0.84 + 24 12.65 291.68 44.06 0.84 + 25 6.90 291.54 26.66 0.84 + 27 9.20 291.52 69.99 0.84 + 28 0.00 291.52 78.65 0.22 + 29 8.05 291.52 78.65 0.84 + 30 3.45 291.52 69.98 1.00 + 31 19.55 291.53 43.99 0.84 + 32 19.55 291.65 78.71 0.52 + 33 1.73 291.75 48.42 0.14 + 34 1.73 291.75 44.09 0.87 + 35 0.00 291.52 78.65 0.95 + 36 1.15 291.52 78.65 1.02 + 26 114.88 291.52 24.49 0.84 Tank + + + Link Results at 37:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 1 486.08 1.38 1.08 + 2 396.95 1.13 0.74 + 3 79.93 0.51 0.28 + 4 63.83 0.41 0.18 + 5 54.63 0.15 0.02 + 6 442.38 1.25 0.91 + 7 436.63 1.24 0.89 + 8 16.10 0.05 0.00 + 9 415.93 1.18 0.81 + 10 5.75 0.04 0.00 + 11 399.83 1.13 0.75 + 12 359.83 1.02 0.62 + 13 341.43 0.97 0.56 + 14 275.11 0.78 0.38 + 15 217.61 0.62 0.24 + 16 64.02 0.41 0.18 + 17 30.28 0.19 0.05 + 18 24.63 0.16 0.03 + 19 31.91 0.09 0.01 + 20 8.91 0.03 0.00 + 21 16.39 0.10 0.01 + 22 55.20 0.16 0.02 + 23 16.74 0.11 0.02 + 24 -1.66 0.01 0.00 + 25 16.61 0.11 0.02 + 26 172.38 0.49 0.16 + 27 185.03 0.52 0.18 + 28 163.18 0.46 0.14 + 29 114.88 0.33 0.07 + 30 41.40 0.12 0.01 + 31 21.85 0.14 0.02 + 32 12.65 0.08 0.01 + 34 1.98 0.01 0.00 + 35 3.45 0.02 0.00 + 36 1.73 0.01 0.00 + 37 -10.64 0.07 0.01 + 38 2.62 0.02 0.00 + 39 3.45 0.02 0.00 + 40 0.83 0.01 0.00 + 41 1.15 0.01 0.00 + + + Node Results at 38:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Fluoride + Node gpm ft psi mg/L + -------------------------------------------------------- + 1 -694.40 313.31 114.09 0.09 + 2 7.28 308.28 90.25 0.09 + 3 12.74 307.63 107.30 0.09 + 4 7.28 307.14 107.09 0.09 + 5 7.28 307.09 89.73 0.09 + 6 4.55 304.80 77.91 0.09 + 7 3.64 299.72 60.54 0.09 + 8 8.19 299.72 82.20 0.09 + 9 12.74 299.00 51.56 0.09 + 10 4.55 299.72 73.54 0.16 + 11 31.65 297.79 48.87 0.09 + 12 14.56 294.80 36.74 0.31 + 13 1.82 293.90 36.35 0.84 + 14 1.82 293.47 40.50 0.84 + 15 1.82 293.21 44.72 0.84 + 16 18.20 293.31 62.10 0.84 + 17 18.20 293.24 49.07 0.84 + 18 18.20 293.24 83.73 0.82 + 19 4.55 293.26 62.07 0.09 + 20 17.29 293.46 53.49 0.84 + 21 14.56 293.45 62.16 0.86 + 22 9.10 293.45 40.49 0.84 + 23 7.28 292.41 27.04 0.84 + 24 10.01 292.97 44.62 0.84 + 25 5.46 292.14 26.93 0.84 + 27 7.28 292.13 70.25 0.84 + 28 0.00 292.13 78.92 0.31 + 29 6.37 292.13 78.92 0.84 + 30 2.73 292.13 70.25 1.00 + 31 15.47 292.14 44.26 0.84 + 32 15.47 293.24 79.40 0.42 + 33 1.37 293.45 49.16 0.93 + 34 1.37 293.45 44.82 0.70 + 35 0.00 292.13 78.92 0.95 + 36 0.91 292.13 78.92 1.03 + 26 400.67 291.99 24.69 0.84 Tank + + + Link Results at 38:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 1 694.40 1.97 2.10 + 2 576.32 1.63 1.48 + 3 110.80 0.71 0.50 + 4 98.06 0.63 0.40 + 5 90.78 0.26 0.05 + 6 659.82 1.87 1.91 + 7 655.27 1.86 1.88 + 8 12.74 0.04 0.00 + 9 638.89 1.81 1.80 + 10 4.55 0.03 0.00 + 11 626.15 1.78 1.73 + 12 594.50 1.69 1.57 + 13 579.94 1.65 1.50 + 14 481.71 1.37 1.07 + 15 436.21 1.24 0.89 + 16 96.41 0.62 0.39 + 17 -21.79 0.14 0.02 + 18 50.10 0.32 0.12 + 19 10.12 0.03 0.00 + 20 -8.08 0.02 0.00 + 21 28.10 0.18 0.04 + 22 43.68 0.12 0.01 + 23 13.24 0.08 0.01 + 24 -1.32 0.01 0.00 + 25 13.15 0.08 0.01 + 26 446.17 1.27 0.92 + 27 456.18 1.29 0.96 + 28 438.89 1.25 0.90 + 29 400.67 1.14 0.76 + 30 32.76 0.09 0.01 + 31 17.29 0.11 0.02 + 32 10.01 0.06 0.01 + 34 1.57 0.01 0.00 + 35 2.73 0.02 0.00 + 36 1.37 0.01 0.00 + 37 -23.55 0.15 0.03 + 38 2.07 0.01 0.00 + 39 2.73 0.02 0.00 + 40 0.66 0.00 0.00 + 41 0.91 0.01 0.00 + + + Node Results at 39:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Fluoride + Node gpm ft psi mg/L + -------------------------------------------------------- + 1 -694.40 316.36 115.42 0.07 + 2 4.88 311.33 91.57 0.07 + 3 8.54 310.69 108.62 0.07 + 4 4.88 310.18 108.40 0.09 + 5 4.88 310.13 91.05 0.07 + 6 3.05 307.77 79.19 0.07 + 7 2.44 302.49 61.74 0.09 + 8 5.49 302.49 83.41 0.09 + 9 8.54 301.74 52.75 0.09 + 10 3.05 302.49 74.74 0.93 + 11 21.22 300.44 50.02 0.09 + 12 9.76 297.14 37.76 0.09 + 13 1.22 296.13 37.32 0.09 + 14 1.22 295.64 41.44 0.09 + 15 1.22 295.31 45.63 0.12 + 16 12.20 295.54 63.06 0.09 + 17 12.20 295.46 50.03 0.84 + 18 12.20 295.46 84.69 0.82 + 19 3.05 295.48 63.04 0.84 + 20 11.59 295.63 54.44 0.84 + 21 9.76 295.62 63.10 0.86 + 22 6.10 295.62 41.43 0.84 + 23 4.88 294.23 27.83 0.12 + 24 6.71 294.98 45.49 0.12 + 25 3.66 293.85 27.67 0.12 + 27 4.88 293.85 71.00 0.84 + 28 0.00 293.85 79.66 0.93 + 29 4.27 293.85 79.66 0.84 + 30 1.83 293.85 71.00 1.00 + 31 10.37 293.85 45.00 0.84 + 32 10.37 295.46 80.36 0.09 + 33 0.92 295.62 50.10 0.93 + 34 0.92 295.62 45.77 0.92 + 35 0.00 293.85 79.66 0.35 + 36 0.61 293.85 79.66 0.99 + 26 497.50 293.63 25.40 0.84 Tank + + + Link Results at 39:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 1 694.40 1.97 2.10 + 2 580.17 1.65 1.50 + 3 109.35 0.70 0.49 + 4 100.81 0.64 0.42 + 5 95.93 0.27 0.05 + 6 671.22 1.90 1.97 + 7 668.17 1.90 1.95 + 8 8.54 0.02 0.00 + 9 657.19 1.86 1.89 + 10 3.05 0.02 0.00 + 11 648.65 1.84 1.85 + 12 627.43 1.78 1.74 + 13 617.67 1.75 1.69 + 14 519.91 1.47 1.23 + 15 489.41 1.39 1.10 + 16 96.54 0.62 0.39 + 17 -46.52 0.30 0.10 + 18 54.49 0.35 0.14 + 19 -4.23 0.01 0.00 + 20 -16.43 0.05 0.00 + 21 29.85 0.19 0.04 + 22 29.28 0.08 0.01 + 23 8.88 0.06 0.00 + 24 -0.88 0.01 0.00 + 25 8.81 0.06 0.00 + 26 528.00 1.50 1.26 + 27 534.71 1.52 1.29 + 28 523.12 1.48 1.24 + 29 497.50 1.41 1.13 + 30 21.96 0.06 0.00 + 31 11.59 0.07 0.01 + 32 6.71 0.04 0.00 + 34 1.05 0.01 0.00 + 35 1.83 0.01 0.00 + 36 0.92 0.01 0.00 + 37 -26.80 0.17 0.04 + 38 1.39 0.01 0.00 + 39 1.83 0.01 0.00 + 40 0.44 0.00 0.00 + 41 0.61 0.00 0.00 + + + Node Results at 40:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Fluoride + Node gpm ft psi mg/L + -------------------------------------------------------- + 1 -694.40 318.06 116.15 0.07 + 2 5.44 313.02 92.30 0.07 + 3 9.52 312.38 109.36 0.07 + 4 5.44 311.88 109.14 0.07 + 5 5.44 311.82 91.78 0.07 + 6 3.40 309.48 79.93 0.07 + 7 2.72 304.25 62.50 0.07 + 8 6.12 304.25 84.17 0.09 + 9 9.52 303.50 53.51 0.07 + 10 3.40 304.25 75.50 0.93 + 11 23.65 302.23 50.79 0.07 + 12 10.88 299.00 38.56 0.07 + 13 1.36 298.01 38.14 0.07 + 14 1.36 297.54 42.26 0.07 + 15 1.36 297.22 46.46 0.14 + 16 13.60 297.42 63.88 0.09 + 17 13.60 297.35 50.85 0.14 + 18 13.60 297.35 85.51 0.82 + 19 3.40 297.36 63.85 0.84 + 20 12.92 297.53 55.26 0.84 + 21 10.88 297.52 63.92 0.86 + 22 6.80 297.52 42.26 0.84 + 23 5.44 296.21 28.69 0.15 + 24 7.48 296.92 46.33 0.14 + 25 4.08 295.87 28.54 0.15 + 27 5.44 295.86 71.87 0.84 + 28 0.00 295.86 80.53 0.93 + 29 4.76 295.86 80.53 0.84 + 30 2.04 295.86 71.87 1.00 + 31 11.56 295.87 45.87 0.84 + 32 11.56 297.35 81.18 0.84 + 33 1.02 297.52 50.92 0.93 + 34 1.02 297.52 46.59 1.00 + 35 0.00 295.86 80.53 0.35 + 36 0.68 295.86 80.53 0.99 + 26 474.91 295.66 26.28 0.81 Tank + + + Link Results at 40:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 1 694.40 1.97 2.10 + 2 579.27 1.64 1.50 + 3 109.69 0.70 0.50 + 4 100.17 0.64 0.42 + 5 94.73 0.27 0.05 + 6 668.56 1.90 1.95 + 7 665.16 1.89 1.94 + 8 9.52 0.03 0.00 + 9 652.92 1.85 1.87 + 10 3.40 0.02 0.00 + 11 643.40 1.83 1.82 + 12 619.75 1.76 1.70 + 13 608.87 1.73 1.64 + 14 510.75 1.45 1.19 + 15 476.75 1.35 1.05 + 16 96.76 0.62 0.39 + 17 -41.00 0.26 0.08 + 18 53.63 0.34 0.13 + 19 -0.97 0.00 0.00 + 20 -14.57 0.04 0.00 + 21 29.53 0.19 0.04 + 22 32.64 0.09 0.01 + 23 9.90 0.06 0.01 + 24 -0.98 0.01 0.00 + 25 9.82 0.06 0.01 + 26 508.91 1.44 1.18 + 27 516.39 1.46 1.21 + 28 503.47 1.43 1.16 + 29 474.91 1.35 1.04 + 30 24.48 0.07 0.00 + 31 12.92 0.08 0.01 + 32 7.48 0.05 0.00 + 34 1.17 0.01 0.00 + 35 2.04 0.01 0.00 + 36 1.02 0.01 0.00 + 37 -26.13 0.17 0.03 + 38 1.55 0.01 0.00 + 39 2.04 0.01 0.00 + 40 0.49 0.00 0.00 + 41 0.68 0.00 0.00 + + + Node Results at 41:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Fluoride + Node gpm ft psi mg/L + -------------------------------------------------------- + 1 -694.40 321.09 117.46 0.09 + 2 3.68 316.05 93.62 0.09 + 3 6.44 315.42 110.67 0.09 + 4 3.68 314.90 110.45 0.07 + 5 3.68 314.84 93.09 0.09 + 6 2.30 312.44 81.22 0.09 + 7 1.84 307.08 63.73 0.07 + 8 4.14 307.08 85.39 0.09 + 9 6.44 306.30 54.73 0.07 + 10 2.30 307.07 76.73 0.93 + 11 16.00 304.96 51.98 0.07 + 12 7.36 301.50 39.65 0.07 + 13 0.92 300.43 39.18 0.07 + 14 0.92 299.90 43.29 0.07 + 15 0.92 299.54 47.46 0.08 + 16 9.20 299.85 64.93 0.07 + 17 9.20 299.76 51.89 0.09 + 18 9.20 299.77 86.56 0.10 + 19 2.30 299.79 64.90 0.09 + 20 8.74 299.90 56.28 0.84 + 21 7.36 299.89 64.95 0.86 + 22 4.60 299.89 43.28 0.84 + 23 3.68 298.30 29.59 0.10 + 24 5.06 299.17 47.30 0.08 + 25 2.76 297.87 29.41 0.12 + 27 3.68 297.87 72.74 0.84 + 28 0.00 297.86 81.40 0.93 + 29 3.22 297.86 81.40 0.84 + 30 1.38 297.86 72.74 1.00 + 31 7.82 297.87 46.74 0.25 + 32 7.82 299.77 82.23 0.84 + 33 0.69 299.89 51.95 0.93 + 34 0.69 299.89 47.62 1.00 + 35 0.00 297.86 81.40 0.37 + 36 0.46 297.86 81.40 0.99 + 26 545.92 297.60 27.12 0.79 Tank + + + Link Results at 41:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 1 694.40 1.97 2.10 + 2 582.11 1.65 1.51 + 3 108.61 0.69 0.49 + 4 102.17 0.65 0.43 + 5 98.49 0.28 0.06 + 6 676.92 1.92 2.00 + 7 674.62 1.91 1.99 + 8 6.44 0.02 0.00 + 9 666.34 1.89 1.94 + 10 2.30 0.01 0.00 + 11 659.90 1.87 1.91 + 12 643.90 1.83 1.82 + 13 636.54 1.81 1.78 + 14 539.96 1.53 1.32 + 15 516.96 1.47 1.21 + 16 95.66 0.61 0.38 + 17 -57.94 0.37 0.15 + 18 56.10 0.36 0.14 + 19 -11.05 0.03 0.00 + 20 -20.25 0.06 0.00 + 21 30.37 0.19 0.05 + 22 22.08 0.06 0.00 + 23 6.70 0.04 0.00 + 24 -0.66 0.00 0.00 + 25 6.64 0.04 0.00 + 26 568.92 1.61 1.45 + 27 573.98 1.63 1.47 + 28 565.24 1.60 1.43 + 29 545.92 1.55 1.34 + 30 16.56 0.05 0.00 + 31 8.74 0.06 0.00 + 32 5.06 0.03 0.00 + 34 0.79 0.01 0.00 + 35 1.38 0.01 0.00 + 36 0.69 0.00 0.00 + 37 -28.07 0.18 0.04 + 38 1.05 0.01 0.00 + 39 1.38 0.01 0.00 + 40 0.33 0.00 0.00 + 41 0.46 0.00 0.00 + + + Node Results at 42:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Fluoride + Node gpm ft psi mg/L + -------------------------------------------------------- + 1 -138.88 300.62 108.59 0.09 + 2 4.08 300.37 86.82 0.09 + 3 7.14 300.33 104.13 0.09 + 4 4.08 300.31 104.13 0.09 + 5 4.08 300.31 86.79 0.09 + 6 2.55 300.21 75.92 0.09 + 7 2.04 300.00 60.66 0.09 + 8 4.59 300.00 82.33 0.09 + 9 7.14 299.98 51.99 0.09 + 10 2.55 300.00 73.66 0.13 + 11 17.74 299.94 49.80 0.09 + 12 8.16 299.86 38.94 0.09 + 13 1.02 299.84 38.93 0.09 + 14 1.02 299.83 43.26 0.09 + 15 1.02 299.83 47.59 0.09 + 16 10.20 299.80 64.91 0.07 + 17 10.20 299.80 51.91 0.15 + 18 10.20 299.80 86.57 0.84 + 19 2.55 299.80 64.91 0.09 + 20 9.69 299.83 56.25 0.09 + 21 8.16 299.82 64.92 0.86 + 22 5.10 299.82 43.25 0.84 + 23 4.08 299.83 30.26 0.08 + 24 5.61 299.83 47.59 0.09 + 25 3.06 299.83 30.26 0.08 + 27 4.08 299.83 73.59 0.84 + 28 0.00 299.82 82.25 0.93 + 29 3.57 299.82 82.25 0.84 + 30 1.53 299.82 73.58 1.00 + 31 8.67 299.83 47.59 0.12 + 32 8.67 299.80 82.24 0.09 + 33 0.77 299.82 51.92 0.93 + 34 0.77 299.82 47.59 1.00 + 35 0.00 299.82 82.25 0.35 + 36 0.51 299.82 82.25 0.95 + 26 -25.74 299.83 28.09 0.77 Tank + + + Link Results at 42:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 1 138.88 0.39 0.11 + 2 111.19 0.32 0.07 + 3 23.61 0.15 0.03 + 4 16.47 0.11 0.01 + 5 12.39 0.04 0.00 + 6 119.50 0.34 0.08 + 7 116.95 0.33 0.08 + 8 7.14 0.02 0.00 + 9 107.77 0.31 0.07 + 10 2.55 0.02 0.00 + 11 100.63 0.29 0.06 + 12 82.89 0.24 0.04 + 13 74.73 0.21 0.03 + 14 51.70 0.15 0.02 + 15 26.20 0.07 0.00 + 16 22.02 0.14 0.03 + 17 19.80 0.13 0.02 + 18 6.35 0.04 0.00 + 19 15.96 0.05 0.00 + 20 5.76 0.02 0.00 + 21 5.46 0.03 0.00 + 22 24.48 0.07 0.00 + 23 7.42 0.05 0.00 + 24 -0.74 0.00 0.00 + 25 7.37 0.05 0.00 + 26 -0.24 0.00 0.00 + 27 5.37 0.02 0.00 + 28 -4.32 0.01 0.00 + 29 -25.74 0.07 0.00 + 30 18.36 0.05 0.00 + 31 9.69 0.06 0.01 + 32 5.61 0.04 0.00 + 34 0.88 0.01 0.00 + 35 1.53 0.01 0.00 + 36 0.77 0.00 0.00 + 37 -2.91 0.02 0.00 + 38 1.16 0.01 0.00 + 39 1.53 0.01 0.00 + 40 0.37 0.00 0.00 + 41 0.51 0.00 0.00 + + + Node Results at 43:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Fluoride + Node gpm ft psi mg/L + -------------------------------------------------------- + 1 0.00 299.20 107.98 0.09 + 2 5.92 299.20 86.31 0.09 + 3 10.36 299.20 103.65 0.09 + 4 5.92 299.20 103.65 0.09 + 5 5.92 299.20 86.31 0.09 + 6 3.70 299.21 75.49 0.09 + 7 2.96 299.23 60.33 0.09 + 8 6.66 299.23 81.99 0.09 + 9 10.36 299.23 51.66 0.09 + 10 3.70 299.23 73.33 0.13 + 11 25.74 299.25 49.50 0.09 + 12 11.84 299.32 38.70 0.09 + 13 1.48 299.35 38.72 0.09 + 14 1.48 299.38 43.06 0.09 + 15 1.48 299.42 47.41 0.09 + 16 14.80 299.32 64.70 0.07 + 17 14.80 299.32 51.70 0.16 + 18 14.80 299.31 86.36 0.82 + 19 3.70 299.31 64.70 0.09 + 20 14.06 299.37 56.06 0.09 + 21 11.84 299.37 64.72 0.86 + 22 7.40 299.37 43.06 0.84 + 23 5.92 299.60 30.16 0.08 + 24 8.14 299.47 47.43 0.09 + 25 4.44 299.67 30.19 0.77 + 27 5.92 299.66 73.51 0.15 + 28 0.00 299.66 82.18 0.93 + 29 5.18 299.66 82.18 0.84 + 30 2.22 299.66 73.51 1.00 + 31 12.58 299.66 47.52 0.15 + 32 12.58 299.31 82.03 0.11 + 33 1.11 299.37 51.72 0.93 + 34 1.11 299.37 47.39 1.00 + 35 0.00 299.66 82.18 0.34 + 36 0.74 299.66 82.18 1.00 + 26 -238.86 299.73 28.05 0.77 Tank + + + Link Results at 43:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 1 -0.00 0.00 0.00 + 2 -11.11 0.03 0.00 + 3 5.19 0.03 0.00 + 4 -5.17 0.03 0.00 + 5 -11.09 0.03 0.00 + 6 -28.12 0.08 0.01 + 7 -31.82 0.09 0.01 + 8 10.36 0.03 0.00 + 9 -45.14 0.13 0.01 + 10 3.70 0.02 0.00 + 11 -55.50 0.16 0.02 + 12 -81.24 0.23 0.04 + 13 -93.08 0.26 0.05 + 14 -116.62 0.33 0.08 + 15 -153.62 0.44 0.13 + 16 22.07 0.14 0.03 + 17 38.61 0.25 0.07 + 18 1.19 0.01 0.00 + 19 25.00 0.07 0.00 + 20 10.20 0.03 0.00 + 21 6.08 0.04 0.00 + 22 35.52 0.10 0.01 + 23 10.77 0.07 0.01 + 24 -1.07 0.01 0.00 + 25 10.69 0.07 0.01 + 26 -201.86 0.57 0.21 + 27 -193.72 0.55 0.20 + 28 -207.78 0.59 0.22 + 29 -238.86 0.68 0.29 + 30 26.64 0.08 0.01 + 31 14.06 0.09 0.01 + 32 8.14 0.05 0.00 + 34 1.27 0.01 0.00 + 35 2.22 0.01 0.00 + 36 1.11 0.01 0.00 + 37 -2.38 0.02 0.00 + 38 1.69 0.01 0.00 + 39 2.22 0.01 0.00 + 40 0.53 0.00 0.00 + 41 0.74 0.00 0.00 + + + Node Results at 44:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Fluoride + Node gpm ft psi mg/L + -------------------------------------------------------- + 1 0.00 297.62 107.30 0.09 + 2 8.96 297.62 85.63 0.09 + 3 15.68 297.62 102.96 0.09 + 4 8.96 297.62 102.96 0.09 + 5 8.96 297.63 85.63 0.09 + 6 5.60 297.64 74.81 0.09 + 7 4.48 297.68 59.66 0.09 + 8 10.08 297.68 81.32 0.09 + 9 15.68 297.69 51.00 0.09 + 10 5.60 297.68 72.66 0.15 + 11 38.95 297.72 48.84 0.09 + 12 17.92 297.88 38.08 0.09 + 13 2.24 297.95 38.11 0.13 + 14 2.24 298.01 42.47 0.77 + 15 2.24 298.10 46.84 0.77 + 16 22.40 297.87 64.07 0.09 + 17 22.40 297.87 51.07 0.18 + 18 22.40 297.86 85.73 0.82 + 19 5.60 297.86 64.07 0.07 + 20 21.28 297.99 55.46 0.07 + 21 17.92 297.98 64.12 0.86 + 22 11.20 297.98 42.45 0.84 + 23 8.96 298.48 29.67 0.77 + 24 12.32 298.20 46.88 0.77 + 25 6.72 298.62 29.73 0.77 + 27 8.96 298.61 73.06 0.15 + 28 0.00 298.60 81.72 0.93 + 29 7.84 298.60 81.72 0.84 + 30 3.36 298.60 73.06 0.59 + 31 19.04 298.62 47.06 0.08 + 32 19.04 297.86 81.40 0.70 + 33 1.68 297.98 51.12 0.93 + 34 1.68 297.98 46.79 1.00 + 35 0.00 298.60 81.72 0.31 + 36 1.12 298.60 81.72 1.00 + 26 -361.51 298.75 27.62 0.77 Tank + + + Link Results at 44:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 1 0.00 0.00 0.00 + 2 -16.82 0.05 0.00 + 3 7.86 0.05 0.00 + 4 -7.82 0.05 0.00 + 5 -16.78 0.05 0.00 + 6 -42.56 0.12 0.01 + 7 -48.16 0.14 0.01 + 8 15.68 0.04 0.00 + 9 -68.32 0.19 0.03 + 10 5.60 0.04 0.00 + 11 -84.00 0.24 0.04 + 12 -122.95 0.35 0.08 + 13 -140.87 0.40 0.11 + 14 -176.51 0.50 0.17 + 15 -232.51 0.66 0.28 + 16 33.40 0.21 0.05 + 17 58.44 0.37 0.15 + 18 1.79 0.01 0.00 + 19 37.84 0.11 0.01 + 20 15.44 0.04 0.00 + 21 9.20 0.06 0.01 + 22 53.76 0.15 0.02 + 23 16.30 0.10 0.01 + 24 -1.62 0.01 0.00 + 25 16.18 0.10 0.01 + 26 -305.51 0.87 0.46 + 27 -293.19 0.83 0.42 + 28 -314.47 0.89 0.48 + 29 -361.51 1.03 0.63 + 30 40.32 0.11 0.01 + 31 21.28 0.14 0.02 + 32 12.32 0.08 0.01 + 34 1.93 0.01 0.00 + 35 3.36 0.02 0.00 + 36 1.68 0.01 0.00 + 37 -3.60 0.02 0.00 + 38 2.55 0.02 0.00 + 39 3.36 0.02 0.00 + 40 0.81 0.01 0.00 + 41 1.12 0.01 0.00 + + + Node Results at 45:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Fluoride + Node gpm ft psi mg/L + -------------------------------------------------------- + 1 0.00 295.70 106.46 0.09 + 2 10.72 295.70 84.80 0.09 + 3 18.76 295.70 102.13 0.09 + 4 10.72 295.70 102.13 0.09 + 5 10.72 295.71 84.80 0.09 + 6 6.70 295.73 73.98 0.09 + 7 5.36 295.78 58.83 0.09 + 8 12.06 295.78 80.50 0.09 + 9 18.76 295.80 50.18 0.09 + 10 6.70 295.78 71.83 0.12 + 11 46.61 295.84 48.03 0.09 + 12 21.44 296.06 37.29 0.77 + 13 2.68 296.16 37.33 0.77 + 14 2.68 296.25 41.70 0.77 + 15 2.68 296.36 46.09 0.77 + 16 26.80 296.04 63.28 0.09 + 17 26.80 296.04 50.28 0.75 + 18 26.80 296.03 84.94 0.16 + 19 6.70 296.03 63.28 0.07 + 20 25.46 296.22 54.69 0.09 + 21 21.44 296.19 63.35 0.86 + 22 13.40 296.19 41.68 0.84 + 23 10.72 296.90 28.99 0.77 + 24 14.74 296.51 46.15 0.77 + 25 8.04 297.10 29.07 0.77 + 27 10.72 297.08 72.39 0.08 + 28 0.00 297.07 81.06 0.93 + 29 9.38 297.07 81.06 0.13 + 30 4.02 297.07 72.39 0.41 + 31 22.78 297.09 46.40 0.77 + 32 22.78 296.03 80.61 0.70 + 33 2.01 296.19 50.35 0.93 + 34 2.01 296.19 46.01 1.00 + 35 0.00 297.07 81.06 0.32 + 36 1.34 297.07 81.06 1.00 + 26 -432.53 297.27 26.98 0.77 Tank + + + Link Results at 45:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 1 -0.00 0.00 0.00 + 2 -20.12 0.06 0.00 + 3 9.40 0.06 0.01 + 4 -9.36 0.06 0.01 + 5 -20.08 0.06 0.00 + 6 -50.92 0.14 0.02 + 7 -57.62 0.16 0.02 + 8 18.76 0.05 0.00 + 9 -81.74 0.23 0.04 + 10 6.70 0.04 0.00 + 11 -100.50 0.29 0.06 + 12 -147.11 0.42 0.12 + 13 -168.55 0.48 0.15 + 14 -211.18 0.60 0.23 + 15 -278.18 0.79 0.39 + 16 39.96 0.26 0.08 + 17 69.92 0.45 0.22 + 18 2.15 0.01 0.00 + 19 45.27 0.13 0.01 + 20 18.47 0.05 0.00 + 21 11.01 0.07 0.01 + 22 64.32 0.18 0.03 + 23 19.50 0.12 0.02 + 24 -1.94 0.01 0.00 + 25 19.36 0.12 0.02 + 26 -365.53 1.04 0.64 + 27 -350.79 1.00 0.59 + 28 -376.25 1.07 0.67 + 29 -432.53 1.23 0.87 + 30 48.24 0.14 0.02 + 31 25.46 0.16 0.03 + 32 14.74 0.09 0.01 + 34 2.31 0.01 0.00 + 35 4.02 0.03 0.00 + 36 2.01 0.01 0.00 + 37 -4.31 0.03 0.00 + 38 3.05 0.02 0.00 + 39 4.02 0.03 0.00 + 40 0.97 0.01 0.00 + 41 1.34 0.01 0.00 + + + Node Results at 46:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Fluoride + Node gpm ft psi mg/L + -------------------------------------------------------- + 1 0.00 294.11 105.77 0.09 + 2 10.08 294.11 84.11 0.09 + 3 17.64 294.10 101.44 0.09 + 4 10.08 294.11 101.44 0.09 + 5 10.08 294.11 84.11 0.09 + 6 6.30 294.13 73.28 0.09 + 7 5.04 294.18 58.14 0.09 + 8 11.34 294.18 79.80 0.84 + 9 17.64 294.19 49.48 0.09 + 10 6.30 294.17 71.14 0.09 + 11 43.82 294.23 47.33 0.77 + 12 20.16 294.43 36.58 0.77 + 13 2.52 294.51 36.62 0.77 + 14 2.52 294.59 40.99 0.77 + 15 2.52 294.70 45.36 0.77 + 16 25.20 294.41 62.57 0.75 + 17 25.20 294.41 49.57 0.75 + 18 25.20 294.40 84.23 0.09 + 19 6.30 294.40 62.57 0.07 + 20 23.94 294.57 53.97 0.77 + 21 20.16 294.54 62.63 0.31 + 22 12.60 294.54 40.97 0.37 + 23 10.08 295.17 28.24 0.77 + 24 13.86 294.83 45.42 0.77 + 25 7.56 295.35 28.32 0.77 + 27 10.08 295.33 71.64 0.77 + 28 0.00 295.33 80.30 0.93 + 29 8.82 295.33 80.30 0.11 + 30 3.78 295.32 71.64 0.56 + 31 21.42 295.34 45.64 0.77 + 32 21.42 294.40 79.90 0.68 + 33 1.89 294.54 49.63 0.93 + 34 1.89 294.54 45.30 1.00 + 35 0.00 295.33 80.30 0.88 + 36 1.26 295.33 80.30 1.00 + 26 -406.70 295.51 26.22 0.77 Tank + + + Link Results at 46:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 1 -0.00 0.00 0.00 + 2 -18.92 0.05 0.00 + 3 8.84 0.06 0.00 + 4 -8.80 0.06 0.00 + 5 -18.88 0.05 0.00 + 6 -47.88 0.14 0.01 + 7 -54.18 0.15 0.02 + 8 17.64 0.05 0.00 + 9 -76.86 0.22 0.04 + 10 6.30 0.04 0.00 + 11 -94.50 0.27 0.05 + 12 -138.32 0.39 0.11 + 13 -158.48 0.45 0.14 + 14 -198.57 0.56 0.21 + 15 -261.57 0.74 0.34 + 16 37.57 0.24 0.07 + 17 65.75 0.42 0.19 + 18 2.02 0.01 0.00 + 19 42.57 0.12 0.01 + 20 17.37 0.05 0.00 + 21 10.35 0.07 0.01 + 22 60.48 0.17 0.02 + 23 18.34 0.12 0.02 + 24 -1.82 0.01 0.00 + 25 18.20 0.12 0.02 + 26 -343.70 0.98 0.57 + 27 -329.84 0.94 0.53 + 28 -353.78 1.00 0.60 + 29 -406.70 1.15 0.78 + 30 45.36 0.13 0.01 + 31 23.94 0.15 0.03 + 32 13.86 0.09 0.01 + 34 2.17 0.01 0.00 + 35 3.78 0.02 0.00 + 36 1.89 0.01 0.00 + 37 -4.05 0.03 0.00 + 38 2.87 0.02 0.00 + 39 3.78 0.02 0.00 + 40 0.91 0.01 0.00 + 41 1.26 0.01 0.00 + + + Node Results at 47:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Fluoride + Node gpm ft psi mg/L + -------------------------------------------------------- + 1 0.00 292.98 105.28 0.09 + 2 7.76 292.98 83.62 0.09 + 3 13.58 292.98 100.95 0.09 + 4 7.76 292.98 100.95 0.09 + 5 7.76 292.98 83.62 0.09 + 6 4.85 292.99 72.79 0.09 + 7 3.88 293.03 57.64 0.18 + 8 8.73 293.02 79.30 0.09 + 9 13.58 293.03 48.98 0.77 + 10 4.85 293.02 70.64 0.11 + 11 33.74 293.06 46.82 0.77 + 12 15.52 293.18 36.04 0.77 + 13 1.94 293.23 36.06 0.77 + 14 1.94 293.28 40.42 0.77 + 15 1.94 293.35 44.78 0.77 + 16 19.40 293.17 62.03 0.77 + 17 19.40 293.17 49.04 0.75 + 18 19.40 293.16 83.70 0.75 + 19 4.85 293.16 62.03 0.07 + 20 18.43 293.27 53.41 0.77 + 21 15.52 293.25 62.07 0.16 + 22 9.70 293.25 40.41 0.07 + 23 7.76 293.64 27.57 0.77 + 24 10.67 293.43 44.81 0.77 + 25 5.82 293.75 27.62 0.77 + 27 7.76 293.74 70.95 0.77 + 28 0.00 293.73 79.61 0.93 + 29 6.79 293.73 79.61 0.77 + 30 2.91 293.73 70.95 0.95 + 31 16.49 293.74 44.95 0.77 + 32 16.49 293.16 79.36 0.14 + 33 1.45 293.25 49.07 0.14 + 34 1.45 293.25 44.74 0.50 + 35 0.00 293.73 79.61 0.88 + 36 0.97 293.73 79.61 1.00 + 26 -313.10 293.84 25.50 0.77 Tank + + + Link Results at 47:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 1 -0.00 0.00 0.00 + 2 -14.57 0.04 0.00 + 3 6.81 0.04 0.00 + 4 -6.77 0.04 0.00 + 5 -14.53 0.04 0.00 + 6 -36.86 0.10 0.01 + 7 -41.71 0.12 0.01 + 8 13.58 0.04 0.00 + 9 -59.17 0.17 0.02 + 10 4.85 0.03 0.00 + 11 -72.75 0.21 0.03 + 12 -106.49 0.30 0.07 + 13 -122.01 0.35 0.08 + 14 -152.87 0.43 0.13 + 15 -201.37 0.57 0.21 + 16 28.92 0.18 0.04 + 17 50.62 0.32 0.12 + 18 1.55 0.01 0.00 + 19 32.77 0.09 0.01 + 20 13.37 0.04 0.00 + 21 7.97 0.05 0.00 + 22 46.56 0.13 0.01 + 23 14.12 0.09 0.01 + 24 -1.40 0.01 0.00 + 25 14.01 0.09 0.01 + 26 -264.60 0.75 0.35 + 27 -253.93 0.72 0.33 + 28 -272.36 0.77 0.37 + 29 -313.10 0.89 0.48 + 30 34.92 0.10 0.01 + 31 18.43 0.12 0.02 + 32 10.67 0.07 0.01 + 34 1.67 0.01 0.00 + 35 2.91 0.02 0.00 + 36 1.45 0.01 0.00 + 37 -3.12 0.02 0.00 + 38 2.21 0.01 0.00 + 39 2.91 0.02 0.00 + 40 0.70 0.00 0.00 + 41 0.97 0.01 0.00 + + + Node Results at 48:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Fluoride + Node gpm ft psi mg/L + -------------------------------------------------------- + 1 0.00 291.93 104.83 0.09 + 2 6.56 291.93 83.16 0.09 + 3 11.48 291.93 100.50 0.09 + 4 6.56 291.93 100.50 0.07 + 5 6.56 291.93 83.17 0.09 + 6 4.10 291.94 72.34 0.09 + 7 3.28 291.97 57.18 0.77 + 8 7.38 291.96 78.85 0.09 + 9 11.48 291.97 48.52 0.77 + 10 4.10 291.96 70.18 0.09 + 11 28.52 291.99 46.36 0.77 + 12 13.12 292.08 35.56 0.77 + 13 1.64 292.12 35.58 0.77 + 14 1.64 292.15 39.93 0.77 + 15 1.64 292.20 44.28 0.77 + 16 16.40 292.07 61.56 0.77 + 17 16.40 292.07 48.56 0.75 + 18 16.40 292.07 83.22 0.75 + 19 4.10 292.07 61.56 0.07 + 20 15.58 292.14 52.92 0.77 + 21 13.12 292.13 61.59 0.16 + 22 8.20 292.13 39.92 0.07 + 23 6.56 292.41 27.04 0.77 + 24 9.02 292.26 44.31 0.77 + 25 4.92 292.50 27.08 0.77 + 27 6.56 292.49 70.41 0.77 + 28 0.00 292.48 79.07 0.18 + 29 5.74 292.48 79.07 0.77 + 30 2.46 292.48 70.40 0.95 + 31 13.94 292.49 44.41 0.77 + 32 13.94 292.07 78.89 0.09 + 33 1.23 292.13 48.59 0.12 + 34 1.23 292.13 44.25 0.21 + 35 0.00 292.48 79.07 0.88 + 36 0.82 292.48 79.07 1.00 + 26 -264.68 292.57 24.94 0.77 Tank + + + Link Results at 48:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 1 -0.00 0.00 0.00 + 2 -12.31 0.03 0.00 + 3 5.75 0.04 0.00 + 4 -5.73 0.04 0.00 + 5 -12.29 0.03 0.00 + 6 -31.16 0.09 0.01 + 7 -35.26 0.10 0.01 + 8 11.48 0.03 0.00 + 9 -50.02 0.14 0.02 + 10 4.10 0.03 0.00 + 11 -61.50 0.17 0.02 + 12 -90.02 0.26 0.05 + 13 -103.14 0.29 0.06 + 14 -129.23 0.37 0.09 + 15 -170.23 0.48 0.16 + 16 24.45 0.16 0.03 + 17 42.79 0.27 0.09 + 18 1.31 0.01 0.00 + 19 27.70 0.08 0.01 + 20 11.30 0.03 0.00 + 21 6.74 0.04 0.00 + 22 39.36 0.11 0.01 + 23 11.93 0.08 0.01 + 24 -1.19 0.01 0.00 + 25 11.85 0.08 0.01 + 26 -223.68 0.63 0.26 + 27 -214.66 0.61 0.24 + 28 -230.24 0.65 0.27 + 29 -264.68 0.75 0.35 + 30 29.52 0.08 0.01 + 31 15.58 0.10 0.01 + 32 9.02 0.06 0.00 + 34 1.41 0.01 0.00 + 35 2.46 0.02 0.00 + 36 1.23 0.01 0.00 + 37 -2.64 0.02 0.00 + 38 1.87 0.01 0.00 + 39 2.46 0.02 0.00 + 40 0.59 0.00 0.00 + 41 0.82 0.01 0.00 + + + Node Results at 49:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Fluoride + Node gpm ft psi mg/L + -------------------------------------------------------- + 1 -513.86 301.57 109.01 0.09 + 2 10.96 298.69 86.09 0.09 + 3 19.18 298.29 103.25 0.09 + 4 10.96 298.05 103.15 0.07 + 5 10.96 298.03 85.81 0.09 + 6 6.85 296.85 74.46 0.09 + 7 5.48 294.27 58.18 0.77 + 8 12.33 294.26 79.84 0.07 + 9 19.18 293.92 49.36 0.77 + 10 6.85 294.26 71.17 0.09 + 11 47.65 293.36 46.95 0.77 + 12 21.92 292.16 35.60 0.77 + 13 2.74 291.82 35.45 0.77 + 14 2.74 291.68 39.72 0.77 + 15 2.74 291.62 44.03 0.77 + 16 27.40 291.50 61.31 0.77 + 17 27.40 291.49 48.31 0.75 + 18 27.40 291.48 82.97 0.75 + 19 6.85 291.48 61.30 0.07 + 20 26.03 291.65 52.71 0.77 + 21 21.92 291.62 61.36 0.17 + 22 13.70 291.62 39.70 0.09 + 23 10.96 291.52 26.66 0.77 + 24 15.07 291.58 44.02 0.77 + 25 8.22 291.49 26.64 0.77 + 27 10.96 291.47 69.96 0.77 + 28 0.00 291.46 78.63 0.14 + 29 9.59 291.46 78.63 0.77 + 30 4.11 291.46 69.96 0.95 + 31 23.29 291.48 43.97 0.77 + 32 23.29 291.48 78.63 0.45 + 33 2.05 291.62 48.37 0.11 + 34 2.05 291.62 44.03 0.23 + 35 0.00 291.46 78.63 0.88 + 36 1.37 291.46 78.63 1.00 + 26 71.65 291.48 24.47 0.77 Tank + + + Link Results at 49:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 1 513.86 1.46 1.20 + 2 417.72 1.18 0.82 + 3 85.18 0.54 0.31 + 4 66.00 0.42 0.19 + 5 55.04 0.16 0.02 + 6 461.80 1.31 0.99 + 7 454.95 1.29 0.96 + 8 19.18 0.05 0.00 + 9 430.29 1.22 0.86 + 10 6.85 0.04 0.00 + 11 411.11 1.17 0.79 + 12 363.46 1.03 0.63 + 13 341.54 0.97 0.56 + 14 269.25 0.76 0.36 + 15 200.75 0.57 0.21 + 16 69.55 0.44 0.21 + 17 42.79 0.27 0.09 + 18 24.62 0.16 0.03 + 19 40.01 0.11 0.01 + 20 12.61 0.04 0.00 + 21 17.53 0.11 0.02 + 22 65.76 0.19 0.03 + 23 19.94 0.13 0.02 + 24 -1.98 0.01 0.00 + 25 19.79 0.13 0.02 + 26 140.15 0.40 0.11 + 27 155.22 0.44 0.13 + 28 129.19 0.37 0.09 + 29 71.65 0.20 0.03 + 30 49.32 0.14 0.02 + 31 26.03 0.17 0.03 + 32 15.07 0.10 0.01 + 34 2.36 0.02 0.00 + 35 4.11 0.03 0.00 + 36 2.05 0.01 0.00 + 37 -10.68 0.07 0.01 + 38 3.12 0.02 0.00 + 39 4.11 0.03 0.00 + 40 0.99 0.01 0.00 + 41 1.37 0.01 0.00 + + + Node Results at 50:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Fluoride + Node gpm ft psi mg/L + -------------------------------------------------------- + 1 -638.85 309.26 112.34 0.08 + 2 8.24 304.95 88.81 0.08 + 3 14.42 304.38 105.89 0.08 + 4 8.24 303.98 105.72 0.09 + 5 8.24 303.94 88.37 0.08 + 6 5.15 302.02 76.70 0.08 + 7 4.12 297.78 59.70 0.09 + 8 9.27 297.78 81.36 0.09 + 9 14.42 297.18 50.78 0.09 + 10 5.15 297.77 72.70 0.09 + 11 35.82 296.19 48.18 0.09 + 12 16.48 293.81 36.32 0.25 + 13 2.06 293.10 36.01 0.77 + 14 2.06 292.77 40.20 0.77 + 15 2.06 292.58 44.45 0.77 + 16 20.60 292.63 61.80 0.77 + 17 20.60 292.58 48.78 0.77 + 18 20.60 292.58 83.44 0.75 + 19 5.15 292.58 61.78 0.09 + 20 19.57 292.76 53.19 0.77 + 21 16.48 292.74 61.85 0.79 + 22 10.30 292.74 40.18 0.77 + 23 8.24 292.05 26.88 0.77 + 24 11.33 292.41 44.38 0.77 + 25 6.18 291.87 26.81 0.77 + 27 8.24 291.86 70.13 0.77 + 28 0.00 291.85 78.80 0.15 + 29 7.21 291.85 78.80 0.77 + 30 3.09 291.85 70.13 0.95 + 31 17.51 291.86 44.14 0.77 + 32 17.51 292.58 79.11 0.44 + 33 1.54 292.74 48.85 0.09 + 34 1.54 292.74 44.52 0.14 + 35 0.00 291.85 78.80 0.88 + 36 1.03 291.85 78.80 0.43 + 26 306.38 291.78 24.60 0.77 Tank + + + Link Results at 50:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 1 638.85 1.81 1.80 + 2 527.76 1.50 1.26 + 3 102.85 0.66 0.44 + 4 88.43 0.56 0.33 + 5 80.19 0.23 0.04 + 6 599.71 1.70 1.60 + 7 594.56 1.69 1.57 + 8 14.42 0.04 0.00 + 9 576.02 1.63 1.48 + 10 5.15 0.03 0.00 + 11 561.60 1.59 1.42 + 12 525.77 1.49 1.25 + 13 509.29 1.44 1.18 + 14 420.87 1.19 0.83 + 15 369.37 1.05 0.65 + 16 86.36 0.55 0.32 + 17 -1.90 0.01 0.00 + 18 41.68 0.27 0.08 + 19 19.18 0.05 0.00 + 20 -1.42 0.00 0.00 + 21 24.08 0.15 0.03 + 22 49.44 0.14 0.02 + 23 14.99 0.10 0.01 + 24 -1.49 0.01 0.00 + 25 14.88 0.09 0.01 + 26 357.88 1.02 0.61 + 27 369.21 1.05 0.65 + 28 349.64 0.99 0.59 + 29 306.38 0.87 0.46 + 30 37.08 0.11 0.01 + 31 19.57 0.12 0.02 + 32 11.33 0.07 0.01 + 34 1.77 0.01 0.00 + 35 3.09 0.02 0.00 + 36 1.54 0.01 0.00 + 37 -18.93 0.12 0.02 + 38 2.35 0.01 0.00 + 39 3.09 0.02 0.00 + 40 0.74 0.00 0.00 + 41 1.03 0.01 0.00 + + + Node Results at 51:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Fluoride + Node gpm ft psi mg/L + -------------------------------------------------------- + 1 -638.85 311.42 113.27 0.35 + 2 6.48 307.10 89.74 0.35 + 3 11.34 306.54 106.83 0.28 + 4 6.48 306.13 106.65 0.08 + 5 6.48 306.08 89.30 0.32 + 6 4.05 304.12 77.61 0.32 + 7 3.24 299.74 60.55 0.08 + 8 7.29 299.74 82.22 0.09 + 9 11.34 299.12 51.62 0.08 + 10 4.05 299.74 73.55 0.09 + 11 28.17 298.08 49.00 0.08 + 12 12.96 295.49 37.04 0.08 + 13 1.62 294.71 36.70 0.08 + 14 1.62 294.34 40.88 0.09 + 15 1.62 294.11 45.11 0.09 + 16 16.20 294.21 62.48 0.09 + 17 16.20 294.15 49.46 0.77 + 18 16.20 294.15 84.12 0.75 + 19 4.05 294.16 62.46 0.77 + 20 15.39 294.33 53.87 0.77 + 21 12.96 294.32 62.53 0.78 + 22 8.10 294.32 40.87 0.77 + 23 6.48 293.40 27.47 0.09 + 24 8.91 293.89 45.02 0.09 + 25 4.86 293.16 27.37 0.09 + 27 6.48 293.16 70.70 0.77 + 28 0.00 293.15 79.36 0.09 + 29 5.67 293.15 79.36 0.77 + 30 2.43 293.15 70.69 0.95 + 31 13.77 293.16 44.70 0.77 + 32 13.77 294.15 79.79 0.07 + 33 1.22 294.32 49.53 0.10 + 34 1.22 294.32 45.20 0.93 + 35 0.00 293.15 79.36 0.88 + 36 0.81 293.15 79.36 0.21 + 26 377.40 293.03 25.14 0.76 Tank + + + Link Results at 51:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 1 638.85 1.81 1.80 + 2 530.56 1.51 1.27 + 3 101.81 0.65 0.43 + 4 90.47 0.58 0.35 + 5 83.99 0.24 0.04 + 6 608.07 1.72 1.64 + 7 604.02 1.71 1.62 + 8 11.34 0.03 0.00 + 9 589.44 1.67 1.55 + 10 4.05 0.03 0.00 + 11 578.10 1.64 1.49 + 12 549.93 1.56 1.36 + 13 536.97 1.52 1.30 + 14 446.51 1.27 0.93 + 15 406.01 1.15 0.78 + 16 88.83 0.57 0.34 + 17 -22.41 0.14 0.03 + 18 46.58 0.30 0.10 + 19 7.97 0.02 0.00 + 20 -8.23 0.02 0.00 + 21 26.05 0.17 0.03 + 22 38.88 0.11 0.01 + 23 11.79 0.08 0.01 + 24 -1.17 0.01 0.00 + 25 11.70 0.07 0.01 + 26 417.90 1.19 0.82 + 27 426.81 1.21 0.85 + 28 411.42 1.17 0.80 + 29 377.40 1.07 0.68 + 30 29.16 0.08 0.01 + 31 15.39 0.10 0.01 + 32 8.91 0.06 0.00 + 34 1.39 0.01 0.00 + 35 2.43 0.02 0.00 + 36 1.22 0.01 0.00 + 37 -22.00 0.14 0.03 + 38 1.85 0.01 0.00 + 39 2.43 0.02 0.00 + 40 0.58 0.00 0.00 + 41 0.81 0.01 0.00 + + + Node Results at 52:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Fluoride + Node gpm ft psi mg/L + -------------------------------------------------------- + 1 -638.85 312.66 113.81 0.72 + 2 7.04 308.35 90.28 0.72 + 3 12.32 307.79 107.37 0.62 + 4 7.04 307.38 107.19 0.35 + 5 7.04 307.33 89.84 0.63 + 6 4.40 305.38 78.16 0.63 + 7 3.52 301.05 61.12 0.31 + 8 7.92 301.05 82.78 0.09 + 9 12.32 300.44 52.19 0.31 + 10 4.40 301.05 74.12 0.09 + 11 30.61 299.41 49.57 0.31 + 12 14.08 296.89 37.65 0.31 + 13 1.76 296.13 37.32 0.30 + 14 1.76 295.78 41.50 0.26 + 15 1.76 295.56 45.74 0.22 + 16 17.60 295.63 63.10 0.09 + 17 17.60 295.58 50.08 0.09 + 18 17.60 295.58 84.74 0.75 + 19 4.40 295.59 63.08 0.77 + 20 16.72 295.76 54.49 0.77 + 21 14.08 295.75 63.15 0.76 + 22 8.80 295.75 41.49 0.77 + 23 7.04 294.91 28.13 0.12 + 24 9.68 295.36 45.65 0.15 + 25 5.28 294.69 28.03 0.12 + 27 7.04 294.68 71.36 0.77 + 28 0.00 294.68 80.02 0.09 + 29 6.16 294.68 80.02 0.77 + 30 2.64 294.68 71.35 0.95 + 31 14.96 294.69 45.36 0.77 + 32 14.96 295.58 80.41 0.77 + 33 1.32 295.75 50.15 0.10 + 34 1.32 295.75 45.82 0.93 + 35 0.00 294.68 80.02 0.88 + 36 0.88 294.68 80.02 0.24 + 26 354.80 294.57 25.81 0.75 Tank + + + Link Results at 52:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 1 638.85 1.81 1.80 + 2 529.67 1.50 1.27 + 3 102.14 0.65 0.43 + 4 89.82 0.57 0.34 + 5 82.78 0.23 0.04 + 6 605.41 1.72 1.63 + 7 601.01 1.70 1.60 + 8 12.32 0.03 0.00 + 9 585.17 1.66 1.53 + 10 4.40 0.03 0.00 + 11 572.85 1.63 1.47 + 12 542.24 1.54 1.33 + 13 528.16 1.50 1.26 + 14 438.02 1.24 0.89 + 15 394.02 1.12 0.73 + 16 88.39 0.56 0.33 + 17 -16.23 0.10 0.01 + 18 45.26 0.29 0.10 + 19 11.44 0.03 0.00 + 20 -6.16 0.02 0.00 + 21 25.52 0.16 0.03 + 22 42.24 0.12 0.01 + 23 12.81 0.08 0.01 + 24 -1.27 0.01 0.00 + 25 12.71 0.08 0.01 + 26 398.80 1.13 0.75 + 27 408.48 1.16 0.78 + 28 391.76 1.11 0.73 + 29 354.80 1.01 0.60 + 30 31.68 0.09 0.01 + 31 16.72 0.11 0.02 + 32 9.68 0.06 0.01 + 34 1.52 0.01 0.00 + 35 2.64 0.02 0.00 + 36 1.32 0.01 0.00 + 37 -21.12 0.13 0.02 + 38 2.00 0.01 0.00 + 39 2.64 0.02 0.00 + 40 0.64 0.00 0.00 + 41 0.88 0.01 0.00 + + + Node Results at 53:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Fluoride + Node gpm ft psi mg/L + -------------------------------------------------------- + 1 -638.85 314.41 114.57 0.82 + 2 6.48 310.09 91.03 0.82 + 3 11.34 309.53 108.12 0.79 + 4 6.48 309.12 107.94 0.72 + 5 6.48 309.07 90.59 0.76 + 6 4.05 307.11 78.91 0.75 + 7 3.24 302.73 61.85 0.64 + 8 7.29 302.73 83.51 0.09 + 9 11.34 302.11 52.91 0.64 + 10 4.05 302.73 74.84 0.09 + 11 28.17 301.07 50.29 0.64 + 12 12.96 298.48 38.34 0.63 + 13 1.62 297.70 38.00 0.62 + 14 1.62 297.33 42.17 0.55 + 15 1.62 297.10 46.41 0.47 + 16 16.20 297.20 63.78 0.32 + 17 16.20 297.14 50.76 0.09 + 18 16.20 297.14 85.42 0.60 + 19 4.05 297.15 63.76 0.77 + 20 15.39 297.32 55.17 0.09 + 21 12.96 297.31 63.83 0.76 + 22 8.10 297.31 42.16 0.77 + 23 6.48 296.39 28.77 0.34 + 24 8.91 296.88 46.31 0.38 + 25 4.86 296.15 28.66 0.33 + 27 6.48 296.15 71.99 0.77 + 28 0.00 296.14 80.66 0.31 + 29 5.67 296.14 80.66 0.77 + 30 2.43 296.14 71.99 0.95 + 31 13.77 296.15 46.00 0.09 + 32 13.77 297.14 81.09 0.77 + 33 1.22 297.31 50.83 0.10 + 34 1.22 297.31 46.50 0.93 + 35 0.00 296.14 80.66 0.88 + 36 0.81 296.14 80.66 0.93 + 26 377.40 296.02 26.44 0.73 Tank + + + Link Results at 53:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 1 638.85 1.81 1.80 + 2 530.56 1.51 1.27 + 3 101.81 0.65 0.43 + 4 90.47 0.58 0.35 + 5 83.99 0.24 0.04 + 6 608.07 1.72 1.64 + 7 604.02 1.71 1.62 + 8 11.34 0.03 0.00 + 9 589.44 1.67 1.55 + 10 4.05 0.03 0.00 + 11 578.10 1.64 1.49 + 12 549.93 1.56 1.36 + 13 536.97 1.52 1.30 + 14 446.51 1.27 0.93 + 15 406.01 1.15 0.78 + 16 88.83 0.57 0.34 + 17 -22.41 0.14 0.03 + 18 46.58 0.30 0.10 + 19 7.97 0.02 0.00 + 20 -8.23 0.02 0.00 + 21 26.05 0.17 0.03 + 22 38.88 0.11 0.01 + 23 11.79 0.08 0.01 + 24 -1.17 0.01 0.00 + 25 11.70 0.07 0.01 + 26 417.90 1.19 0.82 + 27 426.81 1.21 0.85 + 28 411.42 1.17 0.80 + 29 377.40 1.07 0.68 + 30 29.16 0.08 0.01 + 31 15.39 0.10 0.01 + 32 8.91 0.06 0.00 + 34 1.39 0.01 0.00 + 35 2.43 0.02 0.00 + 36 1.22 0.01 0.00 + 37 -22.00 0.14 0.03 + 38 1.85 0.01 0.00 + 39 2.43 0.02 0.00 + 40 0.58 0.00 0.00 + 41 0.81 0.01 0.00 + + + Node Results at 54:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Fluoride + Node gpm ft psi mg/L + -------------------------------------------------------- + 1 -638.85 315.95 115.24 0.92 + 2 6.48 311.64 91.70 0.92 + 3 11.34 311.07 108.79 0.89 + 4 6.48 310.66 108.61 0.82 + 5 6.48 310.62 91.26 0.89 + 6 4.05 308.65 79.57 0.89 + 7 3.24 304.28 62.51 0.76 + 8 7.29 304.27 84.18 0.09 + 9 11.34 303.66 53.58 0.76 + 10 4.05 304.27 75.51 0.20 + 11 28.17 302.61 50.96 0.76 + 12 12.96 300.02 39.01 0.75 + 13 1.62 299.24 38.67 0.74 + 14 1.62 298.87 42.84 0.71 + 15 1.62 298.64 47.07 0.68 + 16 16.20 298.74 64.45 0.63 + 17 16.20 298.68 51.42 0.33 + 18 16.20 298.68 86.09 0.63 + 19 4.05 298.69 64.43 0.09 + 20 15.39 298.86 55.84 0.09 + 21 12.96 298.85 64.50 0.79 + 22 8.10 298.85 42.83 0.77 + 23 6.48 297.94 29.44 0.64 + 24 8.91 298.43 46.98 0.65 + 25 4.86 297.70 29.33 0.64 + 27 6.48 297.69 72.66 0.09 + 28 0.00 297.69 81.32 0.84 + 29 5.67 297.69 81.32 0.77 + 30 2.43 297.69 72.66 0.95 + 31 13.77 297.69 46.66 0.12 + 32 13.77 298.68 81.75 0.77 + 33 1.22 298.85 51.50 0.84 + 34 1.22 298.85 47.16 0.93 + 35 0.00 297.69 81.32 0.38 + 36 0.81 297.69 81.32 0.93 + 26 377.40 297.56 27.11 0.73 Tank + + + Link Results at 54:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 1 638.85 1.81 1.80 + 2 530.56 1.51 1.27 + 3 101.81 0.65 0.43 + 4 90.47 0.58 0.35 + 5 83.99 0.24 0.04 + 6 608.07 1.72 1.64 + 7 604.02 1.71 1.62 + 8 11.34 0.03 0.00 + 9 589.44 1.67 1.55 + 10 4.05 0.03 0.00 + 11 578.10 1.64 1.49 + 12 549.93 1.56 1.36 + 13 536.97 1.52 1.30 + 14 446.51 1.27 0.93 + 15 406.01 1.15 0.78 + 16 88.83 0.57 0.34 + 17 -22.41 0.14 0.03 + 18 46.58 0.30 0.10 + 19 7.97 0.02 0.00 + 20 -8.23 0.02 0.00 + 21 26.05 0.17 0.03 + 22 38.88 0.11 0.01 + 23 11.79 0.08 0.01 + 24 -1.17 0.01 0.00 + 25 11.70 0.07 0.01 + 26 417.90 1.19 0.82 + 27 426.81 1.21 0.85 + 28 411.42 1.17 0.80 + 29 377.40 1.07 0.68 + 30 29.16 0.08 0.01 + 31 15.39 0.10 0.01 + 32 8.91 0.06 0.00 + 34 1.39 0.01 0.00 + 35 2.43 0.02 0.00 + 36 1.22 0.01 0.00 + 37 -22.00 0.14 0.03 + 38 1.85 0.01 0.00 + 39 2.43 0.02 0.00 + 40 0.58 0.00 0.00 + 41 0.81 0.01 0.00 + + + Node Results at 55:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure Fluoride + Node gpm ft psi mg/L + -------------------------------------------------------- + 1 -666.62 317.29 115.82 1.00 + 2 10.08 312.62 92.13 1.00 + 3 17.64 311.99 109.19 0.98 + 4 10.08 311.58 109.01 0.92 + 5 10.08 311.54 91.66 0.98 + 6 6.30 309.51 79.95 0.97 + 7 5.04 305.02 62.84 0.89 + 8 11.34 305.02 84.50 0.09 + 9 17.64 304.40 53.90 0.89 + 10 6.30 305.02 75.83 0.84 + 11 43.82 303.37 51.29 0.89 + 12 20.16 300.97 39.42 0.89 + 13 2.52 300.27 39.11 0.87 + 14 2.52 299.94 43.30 0.83 + 15 2.52 299.76 47.56 0.76 + 16 25.20 299.78 64.90 0.75 + 17 25.20 299.74 51.88 0.64 + 18 25.20 299.73 86.54 0.63 + 19 6.30 299.74 64.88 0.26 + 20 23.94 299.91 56.29 0.31 + 21 20.16 299.89 64.95 0.79 + 22 12.60 299.89 43.28 0.77 + 23 10.08 299.31 30.03 0.72 + 24 13.86 299.62 47.50 0.73 + 25 7.56 299.17 29.97 0.72 + 27 10.08 299.15 73.29 0.12 + 28 0.00 299.15 81.96 0.84 + 29 8.82 299.15 81.96 0.77 + 30 3.78 299.15 73.29 0.35 + 31 21.42 299.16 47.30 0.33 + 32 21.42 299.73 82.21 0.09 + 33 1.89 299.89 51.95 0.84 + 34 1.89 299.89 47.61 0.93 + 35 0.00 299.15 81.96 0.35 + 36 1.26 299.15 81.96 0.93 + 26 259.92 299.10 27.78 0.73 Tank + + + Link Results at 55:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 1 666.62 1.89 1.94 + 2 548.36 1.56 1.35 + 3 108.18 0.69 0.48 + 4 90.54 0.58 0.35 + 5 80.46 0.23 0.04 + 6 618.74 1.76 1.69 + 7 612.44 1.74 1.66 + 8 17.64 0.05 0.00 + 9 589.76 1.67 1.55 + 10 6.30 0.04 0.00 + 11 572.12 1.62 1.46 + 12 528.30 1.50 1.26 + 13 508.14 1.44 1.18 + 14 418.27 1.19 0.82 + 15 355.27 1.01 0.61 + 16 87.35 0.56 0.33 + 17 15.97 0.10 0.01 + 18 38.76 0.25 0.07 + 19 29.52 0.08 0.01 + 20 4.32 0.01 0.00 + 21 23.40 0.15 0.03 + 22 60.48 0.17 0.02 + 23 18.34 0.12 0.02 + 24 -1.82 0.01 0.00 + 25 18.20 0.12 0.02 + 26 322.92 0.92 0.51 + 27 336.78 0.96 0.55 + 28 312.84 0.89 0.48 + 29 259.92 0.74 0.34 + 30 45.36 0.13 0.01 + 31 23.94 0.15 0.03 + 32 13.86 0.09 0.01 + 34 2.17 0.01 0.00 + 35 3.78 0.02 0.00 + 36 1.89 0.01 0.00 + 37 -17.10 0.11 0.02 + 38 2.87 0.02 0.00 + 39 3.78 0.02 0.00 + 40 0.91 0.01 0.00 + 41 1.26 0.01 0.00 + + Analysis ended Fri Oct 13 13:50:30 2017 diff --git a/tests/epanet-nrtestsuite/benchmark/epanet-2012/Example_2/performance.json b/tests/epanet-nrtestsuite/benchmark/epanet-2012/Example_2/performance.json new file mode 100644 index 0000000..df647f2 --- /dev/null +++ b/tests/epanet-nrtestsuite/benchmark/epanet-2012/Example_2/performance.json @@ -0,0 +1,4 @@ +{ + "duration": 1.045, + "max_memory_MB": 0.0 +} \ No newline at end of file diff --git a/tests/epanet-nrtestsuite/benchmark/epanet-2012/Example_3/example3.out b/tests/epanet-nrtestsuite/benchmark/epanet-2012/Example_3/example3.out new file mode 100644 index 0000000000000000000000000000000000000000..c227d48f81d065db9240ce9fc986ad68df4ab359 GIT binary patch literal 144692 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

PTs&PI_lJ>#dGP^p_alTf>2+v9_ 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 literal 0 HcmV?d00001 diff --git a/tests/epanet-nrtestsuite/benchmark/epanet-2012/Example_3/example3.rpt b/tests/epanet-nrtestsuite/benchmark/epanet-2012/Example_3/example3.rpt new file mode 100644 index 0000000..6bbe404 --- /dev/null +++ b/tests/epanet-nrtestsuite/benchmark/epanet-2012/Example_3/example3.rpt @@ -0,0 +1,5852 @@ + Page 1 Fri Oct 13 13:50:30 2017 + + ****************************************************************** + * E P A N E T * + * Hydraulic and Water Quality * + * Analysis for Pipe Networks * + * Version 2.00.12 * + ****************************************************************** + + Analysis begun Fri Oct 13 13:50:30 2017 + + + Hydraulic Status: + ----------------------------------------------------------------------- + 0:00:00: Balanced after 5 trials + 0:00:00: Reservoir River is emptying + 0:00:00: Reservoir Lake is closed + 0:00:00: Tank 1 is filling at 13.10 ft + 0:00:00: Tank 2 is emptying at 23.50 ft + 0:00:00: Tank 3 is filling at 29.00 ft + + 1:00:00: Pump 10 changed by timer control + 1:00:00: Balanced after 7 trials + 1:00:00: Reservoir Lake is emptying + 1:00:00: Pump 10 changed from closed to open + + 2:00:00: Balanced after 3 trials + 2:00:00: Tank 2 is filling at 20.90 ft + + 3:00:00: Balanced after 2 trials + + 4:00:00: Balanced after 3 trials + + 4:13:33: Pump 335 changed by Tank 1 control + 4:13:33: Pipe 330 changed by Tank 1 control + 4:13:33: Balanced after 4 trials + 4:13:33: Pipe 330 changed from closed to open + 4:13:33: Pump 335 changed from open to closed + + 5:00:00: Balanced after 3 trials + 5:00:00: Tank 3 is emptying at 34.30 ft + + 6:00:00: Balanced after 3 trials + 6:00:00: Tank 3 is filling at 34.12 ft + + 7:00:00: Balanced after 3 trials + + 8:00:00: Balanced after 2 trials + + 9:00:00: Balanced after 3 trials + 9:00:00: Tank 3 is emptying at 35.15 ft + + 10:00:00: Balanced after 2 trials + 10:00:00: Tank 1 is emptying at 22.20 ft + + 11:00:00: Balanced after 3 trials + 11:00:00: Tank 2 is emptying at 27.70 ft + + 12:00:00: Balanced after 2 trials + 12:00:00: Tank 2 is filling at 27.64 ft + + 13:00:00: Balanced after 3 trials + 13:00:00: Tank 1 is filling at 21.73 ft + + 14:00:00: Balanced after 3 trials + + 15:00:00: Pump 10 changed by timer control + 15:00:00: Balanced after 5 trials + 15:00:00: Reservoir Lake is closed + 15:00:00: Tank 1 is emptying at 21.98 ft + 15:00:00: Tank 2 is emptying at 28.20 ft + 15:00:00: Pump 10 changed from open to closed + + 16:00:00: Balanced after 3 trials + + 17:00:00: Balanced after 2 trials + + 18:00:00: Balanced after 3 trials + + 19:00:00: Balanced after 2 trials + + 20:00:00: Balanced after 3 trials + + 21:00:00: Balanced after 2 trials + + 21:19:39: Pump 335 changed by Tank 1 control + 21:19:39: Pipe 330 changed by Tank 1 control + 21:19:39: Balanced after 5 trials + 21:19:39: Tank 1 is filling at 17.10 ft + 21:19:39: Tank 3 is filling at 29.68 ft + 21:19:39: Pipe 330 changed from open to closed + 21:19:39: Pump 335 changed from closed to open + + 22:00:00: Balanced after 3 trials + 22:00:00: Tank 1 is emptying at 17.30 ft + + 23:00:00: Balanced after 3 trials + + 24:00:00: Balanced after 4 trials + 24:00:00: Tank 1 is filling at 15.79 ft + + + Node Results at 0:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure % from + Node gpm ft psi Lake + -------------------------------------------------------- + 10 0.00 145.52 -0.64 0.00 + 15 620.00 125.81 40.65 0.00 + 20 0.00 158.00 12.57 0.00 + 35 1637.00 145.74 57.73 0.00 + 40 0.00 145.00 5.68 0.00 + 50 0.00 140.00 10.18 0.00 + 60 0.00 209.01 90.56 0.00 + 601 0.00 302.45 131.05 0.00 + 61 0.00 302.45 131.05 0.00 + 101 254.53 145.52 44.86 0.00 + 103 178.49 145.49 44.41 0.00 + 105 181.40 146.83 51.27 0.00 + 107 73.22 146.82 54.09 0.00 + 109 310.08 145.49 54.25 0.00 + 111 190.20 146.11 58.98 0.00 + 113 26.81 146.15 62.46 0.00 + 115 69.81 146.92 57.59 0.00 + 117 157.73 150.03 59.12 0.00 + 119 236.01 157.55 67.40 0.00 + 120 0.00 155.12 67.21 0.00 + 121 55.78 161.01 70.63 0.00 + 123 0.00 165.47 66.93 0.00 + 125 61.10 160.43 64.75 0.00 + 127 23.66 158.74 44.52 0.00 + 129 0.00 158.73 46.68 0.00 + 131 57.28 158.71 66.17 0.00 + 139 7.89 153.08 52.90 0.00 + 141 13.20 149.06 62.85 0.00 + 143 8.31 138.25 61.85 0.00 + 145 37.02 150.28 64.68 0.00 + 147 11.46 151.20 57.50 0.00 + 149 36.27 151.60 58.75 0.00 + 151 193.60 155.44 52.84 0.00 + 153 59.19 155.54 38.71 0.00 + 157 69.40 155.11 61.53 0.00 + 159 55.37 151.76 63.16 0.00 + 161 21.17 149.48 63.04 0.00 + 163 12.62 149.02 62.41 0.00 + 164 0.00 149.02 62.41 0.00 + 166 3.48 149.02 65.44 0.00 + 167 19.51 147.15 65.93 0.00 + 169 0.00 147.15 65.93 0.00 + 171 52.72 146.07 65.03 0.00 + 173 0.00 146.05 65.02 0.00 + 177 77.95 145.73 59.68 0.00 + 179 0.00 145.72 59.67 0.00 + 181 0.00 145.75 59.69 0.00 + 183 0.00 145.72 58.37 0.00 + 184 0.00 144.49 55.68 0.00 + 185 34.37 145.08 55.93 0.00 + 187 0.00 145.78 57.75 0.00 + 189 144.61 146.09 61.57 0.00 + 191 109.75 146.05 52.45 0.00 + 193 95.56 146.15 55.53 0.00 + 195 0.00 146.22 56.64 0.00 + 197 22.83 146.06 53.32 0.00 + 199 159.89 140.83 61.89 0.00 + 201 59.78 140.10 60.66 0.00 + 203 4439.00 139.93 59.77 0.00 + 204 0.00 145.53 53.96 0.00 + 205 87.58 140.80 51.91 0.00 + 206 0.00 139.89 60.18 0.00 + 207 92.98 140.10 56.80 0.00 + 208 0.00 139.67 53.58 0.00 + 209 1.17 139.27 61.21 0.00 + 211 11.62 139.14 57.25 0.00 + 213 18.68 139.07 57.23 0.00 + 215 123.53 138.88 57.14 0.00 + 217 32.45 138.86 57.57 0.00 + 219 55.37 138.85 58.43 0.00 + 225 30.55 138.85 56.70 0.00 + 229 86.00 138.98 55.67 0.00 + 231 22.08 138.97 58.05 0.00 + 237 20.92 139.08 54.20 0.00 + 239 59.78 139.08 54.63 0.00 + 241 0.00 139.09 54.63 0.00 + 243 5.82 139.08 54.20 0.00 + 247 94.31 139.09 52.47 0.00 + 249 0.00 139.09 52.47 0.00 + 251 32.37 139.10 47.27 0.00 + 253 73.06 139.22 44.72 0.00 + 255 54.12 139.27 48.65 0.00 + 257 0.00 152.00 58.49 0.00 + 259 0.00 151.56 54.84 0.00 + 261 0.00 149.98 64.99 0.00 + 263 0.00 149.82 64.92 0.00 + 265 0.00 147.75 64.02 0.00 + 267 0.00 146.17 54.24 0.00 + 269 0.00 146.49 63.48 0.00 + 271 0.00 145.84 60.59 0.00 + 273 0.00 140.80 57.54 0.00 + 275 0.00 140.10 56.37 0.00 + River -13157.88 220.00 0.00 0.00 Reservoir + Lake 0.00 167.00 0.00 100.00 Reservoir + 1 460.32 145.00 5.68 0.00 Tank + 2 -329.21 140.00 10.18 0.00 Tank + 3 2246.30 158.00 12.57 0.00 Tank + + + Link Results at 0:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 20 -2246.30 0.09 0.00 + 40 -460.32 0.02 0.00 + 50 329.21 0.01 0.00 + 60 13157.88 9.33 8.93 + 101 0.00 0.00 0.00 + 103 168.06 0.27 0.02 + 105 -422.59 1.20 0.51 + 107 32.30 0.09 0.00 + 109 -10.43 0.02 0.00 + 111 -320.51 0.91 0.31 + 112 498.35 1.41 0.70 + 113 -80.63 0.23 0.02 + 114 124.37 0.79 0.38 + 115 -40.92 0.26 0.05 + 116 16.93 0.05 0.00 + 117 636.29 1.81 1.10 + 119 -733.46 2.08 1.43 + 120 1159.13 3.29 3.33 + 121 1039.29 2.95 2.72 + 122 368.34 2.35 2.87 + 123 9821.71 4.46 1.73 + 125 13157.88 5.97 2.97 + 129 2912.04 2.07 0.63 + 131 2634.65 1.87 0.52 + 133 -2246.30 2.29 0.94 + 135 364.69 0.26 0.01 + 137 57.28 0.09 0.00 + 145 307.41 1.96 2.06 + 147 299.51 1.91 1.96 + 149 -628.31 4.01 7.72 + 151 -620.00 3.96 7.54 + 153 341.99 0.97 0.35 + 155 379.02 1.08 0.42 + 159 -390.47 1.11 0.44 + 161 -426.75 2.72 3.77 + 163 -157.09 0.45 0.08 + 169 216.28 1.38 1.07 + 171 463.26 1.31 0.61 + 173 7963.30 3.61 1.17 + 175 7893.91 3.58 1.15 + 177 7838.54 3.56 1.14 + 179 7565.72 3.43 1.07 + 180 3.48 0.01 0.00 + 181 3.48 0.01 0.00 + 183 7341.33 3.33 1.01 + 185 -19.51 0.12 0.01 + 186 340.16 2.17 2.48 + 187 6708.97 3.05 0.85 + 189 4690.03 2.13 0.44 + 191 -1966.23 1.39 0.30 + 193 -1637.00 1.16 0.22 + 195 473.47 1.34 0.63 + 197 395.52 1.12 0.45 + 199 -64.80 0.18 0.02 + 201 -460.32 1.31 0.60 + 202 541.82 3.46 5.87 + 203 236.03 1.51 1.26 + 204 541.82 1.54 0.81 + 205 340.16 0.96 0.34 + 207 300.83 0.85 0.27 + 209 160.66 1.03 0.62 + 211 612.84 1.74 1.02 + 213 179.50 0.51 0.11 + 215 137.51 0.39 0.06 + 217 -243.80 0.69 0.19 + 219 -251.65 0.71 0.20 + 221 251.65 1.61 1.42 + 223 45.44 0.13 0.01 + 225 68.28 0.19 0.02 + 229 4690.03 3.33 1.30 + 231 4419.40 3.13 1.17 + 233 4439.00 3.15 1.37 + 235 110.74 0.31 0.04 + 237 453.73 1.29 0.59 + 238 392.62 1.11 0.45 + 239 31.87 0.09 0.00 + 240 392.62 1.11 0.45 + 241 392.62 1.11 0.45 + 243 391.45 0.62 0.11 + 245 298.08 0.48 0.07 + 247 241.91 0.39 0.05 + 249 118.38 0.19 0.01 + 251 55.37 0.12 0.01 + 257 30.55 0.09 0.00 + 261 37.49 0.24 0.04 + 263 22.08 0.06 0.00 + 269 81.76 0.23 0.02 + 271 70.60 0.45 0.13 + 273 -9.76 0.03 0.00 + 275 -22.78 0.06 0.00 + 277 5.82 0.02 0.00 + 281 -28.60 0.12 0.01 + 283 -46.75 0.13 0.01 + 285 2.62 0.01 0.00 + 287 -125.53 0.51 0.13 + 289 329.21 1.34 0.79 + 291 73.06 0.30 0.05 + 293 76.50 0.49 0.16 + 295 -44.13 0.13 0.01 + 297 488.19 3.12 4.84 + 299 234.55 1.50 1.25 + 301 234.55 1.50 1.25 + 303 253.64 1.62 1.44 + 305 148.10 0.42 0.07 + 307 401.74 1.14 0.47 + 309 208.28 1.33 1.00 + 311 -70.78 0.20 0.02 + 313 468.60 1.33 0.62 + 315 -2110.47 1.50 0.35 + 317 111.25 0.71 0.31 + 319 -0.51 0.00 0.00 + 321 7549.61 3.43 1.06 + 323 -79.37 0.23 0.02 + 325 144.25 0.92 0.51 + 329 13157.88 5.97 3.01 + 330 0.00 0.00 0.00 + 333 -0.00 0.00 0.00 + 10 0.00 0.00 0.00 Pump + 335 13157.88 0.00 -93.44 Pump + + + Node Results at 1:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure % from + Node gpm ft psi Lake + -------------------------------------------------------- + 10 0.00 239.70 40.17 0.00 + 15 620.00 126.77 41.07 0.00 + 20 0.00 158.85 12.94 0.00 + 35 1706.00 148.70 59.02 0.00 + 40 0.00 145.65 5.96 0.00 + 50 0.00 138.66 9.60 0.00 + 60 0.00 209.16 90.63 0.00 + 601 0.00 303.45 131.48 0.00 + 61 0.00 303.45 131.48 0.00 + 101 368.50 172.80 56.68 0.00 + 103 258.41 170.18 55.11 0.00 + 105 262.62 163.49 58.49 0.00 + 107 106.00 162.76 60.99 0.00 + 109 448.92 164.39 62.43 0.00 + 111 275.36 157.94 64.10 0.00 + 113 38.82 155.99 66.72 0.00 + 115 101.07 158.76 62.73 0.00 + 117 228.36 160.57 63.68 0.00 + 119 341.69 161.00 68.89 0.00 + 120 0.00 161.00 69.76 0.00 + 121 80.76 163.91 71.89 0.00 + 123 0.00 168.30 68.16 0.00 + 125 88.46 162.96 65.84 0.00 + 127 34.26 160.15 45.13 0.00 + 129 0.00 160.13 47.29 0.00 + 131 82.93 160.09 66.77 0.00 + 139 11.43 154.24 53.40 0.00 + 141 19.11 150.14 63.32 0.00 + 143 12.03 139.21 62.27 0.00 + 145 53.60 151.40 65.17 0.00 + 147 16.59 152.43 58.03 0.00 + 149 52.52 152.88 59.31 0.00 + 151 280.29 157.50 53.73 0.00 + 153 85.69 157.58 39.59 0.00 + 157 100.47 158.53 63.01 0.00 + 159 80.16 155.16 64.63 0.00 + 161 30.65 152.88 64.51 0.00 + 163 18.27 152.40 63.87 0.00 + 164 0.00 152.40 63.87 0.00 + 166 5.04 152.40 66.90 0.00 + 167 28.25 150.40 67.33 0.00 + 169 0.00 150.40 67.33 0.00 + 171 76.32 149.09 66.33 0.00 + 173 0.00 149.06 66.32 0.00 + 177 112.85 148.67 60.95 0.00 + 179 0.00 148.65 60.94 0.00 + 181 0.00 148.71 60.97 0.00 + 183 0.00 148.77 59.70 0.00 + 184 0.00 147.69 57.06 0.00 + 185 49.76 148.50 57.41 0.00 + 187 0.00 150.17 59.65 0.00 + 189 209.36 150.11 63.31 0.00 + 191 158.89 152.75 55.36 0.00 + 193 138.34 152.77 58.40 0.00 + 195 0.00 152.77 59.48 0.00 + 197 33.06 154.17 56.84 0.00 + 199 231.48 142.74 62.71 0.00 + 201 86.54 141.88 61.43 0.00 + 203 4531.00 141.71 60.54 0.00 + 204 0.00 149.58 55.71 0.00 + 205 126.80 142.64 52.71 0.00 + 206 0.00 140.85 60.60 0.00 + 207 134.62 141.50 57.41 0.00 + 208 0.00 140.12 53.78 0.00 + 209 1.69 138.84 61.03 0.00 + 211 16.82 138.42 56.94 0.00 + 213 27.04 138.27 56.88 0.00 + 215 178.85 137.89 56.71 0.00 + 217 46.99 137.85 57.13 0.00 + 219 80.16 137.82 57.99 0.00 + 225 44.23 137.83 56.26 0.00 + 229 124.51 137.87 55.19 0.00 + 231 31.97 137.86 57.57 0.00 + 237 30.28 137.98 53.72 0.00 + 239 86.54 137.94 54.14 0.00 + 241 0.00 137.94 54.14 0.00 + 243 8.42 137.94 53.70 0.00 + 247 136.54 137.94 51.97 0.00 + 249 0.00 137.94 51.97 0.00 + 251 46.87 137.94 46.77 0.00 + 253 105.77 137.91 44.16 0.00 + 255 78.36 138.01 48.10 0.00 + 257 0.00 161.04 62.41 0.00 + 259 0.00 161.08 58.96 0.00 + 261 0.00 161.02 69.77 0.00 + 263 0.00 161.25 69.87 0.00 + 265 0.00 151.07 65.46 0.00 + 267 0.00 151.32 56.47 0.00 + 269 0.00 150.16 65.06 0.00 + 271 0.00 148.82 61.88 0.00 + 273 0.00 142.65 58.34 0.00 + 275 0.00 141.86 57.13 0.00 + River -13062.03 220.00 0.00 0.00 Reservoir + Lake -3435.20 167.00 0.00 100.00 Reservoir + 1 996.56 145.65 5.96 0.00 Tank + 2 -307.71 138.66 9.60 0.00 Tank + 3 3038.04 158.85 12.94 0.00 Tank + + + Link Results at 1:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 20 -3038.04 0.13 0.00 + 40 -996.56 0.04 0.00 + 50 307.71 0.01 0.00 + 60 13062.03 9.26 8.81 + 101 3435.20 4.33 4.71 + 103 1846.16 2.95 1.94 + 105 1220.53 3.46 3.67 + 107 413.09 1.17 0.49 + 109 1587.75 2.53 1.47 + 111 1138.84 3.23 3.22 + 112 503.62 1.43 0.71 + 113 655.73 1.86 1.16 + 114 248.60 1.59 1.39 + 115 307.09 1.96 2.05 + 116 865.52 2.46 1.94 + 117 -544.83 1.55 0.82 + 119 -546.21 1.55 0.83 + 120 -21.77 0.06 0.00 + 121 274.75 0.78 0.23 + 122 251.51 1.61 1.42 + 123 8943.91 4.06 1.45 + 125 13062.03 5.93 2.93 + 129 3785.85 2.68 1.02 + 131 3469.58 2.46 0.87 + 133 -3038.04 3.10 1.65 + 135 397.28 0.28 0.02 + 137 82.93 0.13 0.01 + 145 314.34 2.01 2.14 + 147 302.92 1.93 2.00 + 149 -632.03 4.03 7.81 + 151 -620.00 3.96 7.54 + 153 348.22 0.99 0.36 + 155 401.82 1.14 0.47 + 159 -418.41 1.19 0.50 + 161 -470.93 3.01 4.53 + 163 -142.12 0.40 0.07 + 169 227.81 1.45 1.18 + 171 609.10 1.73 1.01 + 173 8014.88 3.64 1.19 + 175 7914.41 3.59 1.16 + 177 7834.25 3.56 1.14 + 179 7762.99 3.52 1.12 + 180 5.04 0.01 0.00 + 181 5.04 0.01 0.00 + 183 7817.51 3.55 1.13 + 185 -28.25 0.18 0.02 + 186 542.85 3.46 5.89 + 187 7431.51 3.37 1.03 + 189 5205.59 2.36 0.53 + 191 -2149.60 1.52 0.36 + 193 -1706.00 1.21 0.23 + 195 656.05 1.86 1.16 + 197 543.20 1.54 0.82 + 199 -453.37 1.29 0.59 + 201 -996.56 2.83 2.52 + 202 642.17 4.10 8.04 + 203 149.08 0.95 0.54 + 204 642.17 1.82 1.12 + 205 542.85 1.54 0.82 + 207 602.45 1.71 0.99 + 209 -65.68 0.42 0.12 + 211 357.75 1.01 0.38 + 213 608.54 1.73 1.01 + 215 600.83 1.70 0.99 + 217 -89.12 0.25 0.03 + 219 -40.61 0.12 0.01 + 221 40.61 0.26 0.05 + 223 678.31 1.92 1.24 + 225 711.36 2.02 1.35 + 229 5205.59 3.69 1.58 + 231 4783.02 3.39 1.35 + 233 4531.00 3.21 1.42 + 235 191.09 0.54 0.12 + 237 587.22 1.67 0.95 + 238 737.33 2.09 1.44 + 239 284.72 0.81 0.25 + 240 737.33 2.09 1.44 + 241 737.33 2.09 1.44 + 243 735.64 1.17 0.35 + 245 460.16 0.73 0.15 + 247 350.23 0.56 0.09 + 249 171.38 0.27 0.02 + 251 80.16 0.17 0.01 + 257 44.23 0.13 0.01 + 261 82.88 0.53 0.18 + 263 31.97 0.09 0.00 + 269 258.66 0.73 0.21 + 271 73.60 0.47 0.15 + 273 154.79 0.44 0.08 + 275 30.78 0.09 0.00 + 277 8.42 0.02 0.00 + 281 22.36 0.09 0.01 + 283 37.46 0.11 0.01 + 285 -38.38 0.11 0.01 + 287 -75.79 0.31 0.05 + 289 307.71 1.26 0.69 + 291 105.77 0.43 0.10 + 293 47.79 0.31 0.07 + 295 -0.92 0.00 0.00 + 297 -45.01 0.29 0.06 + 299 -66.18 0.42 0.12 + 301 -66.18 0.42 0.12 + 303 21.17 0.14 0.01 + 305 -499.82 1.42 0.70 + 307 -478.65 1.36 0.65 + 309 -77.84 0.50 0.16 + 311 678.67 1.93 1.24 + 313 145.30 0.41 0.07 + 315 -2362.05 1.68 0.43 + 317 119.24 0.76 0.36 + 319 71.85 0.20 0.02 + 321 7739.67 3.51 1.11 + 323 165.48 0.47 0.09 + 325 212.45 1.36 1.04 + 329 13062.03 5.93 2.97 + 330 0.00 0.00 0.00 + 333 -0.00 0.00 0.00 + 10 3435.20 0.00 -72.70 Pump + 335 13062.03 0.00 -94.29 Pump + + + Node Results at 2:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure % from + Node gpm ft psi Lake + -------------------------------------------------------- + 10 0.00 241.37 40.89 100.00 + 15 620.00 130.44 42.65 0.00 + 20 0.00 160.01 13.44 0.00 + 35 1719.00 151.70 60.32 0.00 + 40 0.00 147.06 6.57 0.00 + 50 0.00 137.40 9.06 0.00 + 60 0.00 209.30 90.69 0.00 + 601 0.00 304.38 131.89 0.00 + 61 0.00 304.38 131.89 0.00 + 101 277.33 178.21 59.02 7.12 + 103 194.47 175.75 57.52 0.00 + 105 197.64 168.17 60.52 0.00 + 107 79.77 167.47 63.03 0.00 + 109 337.84 169.97 64.85 0.00 + 111 207.23 162.31 65.99 0.00 + 113 29.21 160.04 68.48 0.00 + 115 76.07 162.94 64.53 0.00 + 117 171.86 164.13 65.22 0.00 + 119 257.15 164.00 70.20 0.00 + 120 0.00 164.18 71.14 0.00 + 121 60.78 166.61 73.06 0.00 + 123 0.00 170.95 69.31 0.00 + 125 66.58 165.43 66.92 0.00 + 127 25.78 161.80 45.84 0.00 + 129 0.00 161.79 48.00 0.00 + 131 62.42 161.76 67.49 0.00 + 139 8.60 157.04 54.61 0.00 + 141 14.38 153.71 64.87 0.00 + 143 9.05 142.87 63.86 0.00 + 145 40.34 155.14 66.79 0.00 + 147 12.48 156.22 59.67 0.00 + 149 39.52 156.68 60.96 0.00 + 151 210.94 161.19 55.33 0.00 + 153 64.49 161.26 41.19 0.00 + 157 75.61 161.59 64.34 0.00 + 159 60.33 158.28 65.98 0.00 + 161 23.07 156.03 65.87 0.00 + 163 13.75 155.54 65.23 0.00 + 164 0.00 155.54 65.23 0.00 + 166 3.80 155.54 68.26 0.00 + 167 21.26 153.50 68.68 0.00 + 169 0.00 153.50 68.68 0.00 + 171 57.44 152.14 67.66 0.00 + 173 0.00 152.12 67.65 0.00 + 177 84.93 151.66 62.25 0.00 + 179 0.00 151.62 62.23 0.00 + 181 0.00 151.71 62.27 0.00 + 183 0.00 151.78 61.00 0.00 + 184 0.00 150.78 58.40 0.00 + 185 37.45 151.59 58.75 0.00 + 187 0.00 153.39 61.05 0.00 + 189 157.56 153.30 64.69 0.00 + 191 119.57 156.32 56.90 0.00 + 193 104.11 156.32 59.93 0.00 + 195 0.00 156.31 61.01 0.00 + 197 24.88 157.99 58.49 0.00 + 199 174.21 145.76 64.03 0.00 + 201 65.13 144.90 62.74 0.00 + 203 4511.00 144.73 61.84 0.00 + 204 0.00 152.76 57.09 0.00 + 205 95.43 145.63 54.00 0.00 + 206 0.00 143.29 61.65 0.00 + 207 101.31 144.26 58.61 0.00 + 208 0.00 142.18 54.67 0.00 + 209 1.27 140.26 61.64 0.00 + 211 12.66 139.62 57.46 0.00 + 213 20.35 139.49 57.41 0.00 + 215 134.60 139.26 57.31 0.00 + 217 35.36 139.24 57.73 0.00 + 219 60.33 139.22 58.59 0.00 + 225 33.29 139.23 56.86 0.00 + 229 93.70 138.32 55.38 0.00 + 231 24.06 138.31 57.77 0.00 + 237 22.79 138.30 53.86 0.00 + 239 65.13 137.96 54.15 0.00 + 241 0.00 137.96 54.15 0.00 + 243 6.34 137.96 53.71 0.00 + 247 102.75 137.88 51.94 0.00 + 249 0.00 137.88 51.94 0.00 + 251 35.27 137.79 46.71 0.00 + 253 79.60 137.47 43.97 0.00 + 255 58.97 137.53 47.89 0.00 + 257 0.00 164.52 63.92 0.00 + 259 0.00 164.60 60.49 0.00 + 261 0.00 164.62 71.33 0.00 + 263 0.00 164.93 71.46 0.00 + 265 0.00 154.19 66.81 0.00 + 267 0.00 154.63 57.90 0.00 + 269 0.00 153.32 66.43 0.00 + 271 0.00 151.84 63.19 0.00 + 273 0.00 145.65 59.65 0.00 + 275 0.00 144.84 58.43 0.00 + River -12972.12 220.00 0.00 0.00 Reservoir + Lake -3330.25 167.00 0.00 100.00 Reservoir + 1 1249.69 147.06 6.57 0.00 Tank + 2 132.71 137.40 9.06 0.00 Tank + 3 3619.73 160.01 13.44 0.00 Tank + + + Link Results at 2:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 20 -3619.73 0.15 0.00 + 40 -1249.69 0.05 0.00 + 50 -132.71 0.01 0.00 + 60 12972.12 9.20 8.70 + 101 3330.25 4.20 4.45 + 103 1781.95 2.84 1.82 + 105 1270.97 3.61 3.95 + 107 408.15 1.16 0.48 + 109 1587.48 2.53 1.47 + 111 1249.64 3.54 3.83 + 112 434.96 1.23 0.54 + 113 711.21 2.02 1.35 + 114 254.36 1.62 1.45 + 115 328.38 2.10 2.32 + 116 936.35 2.66 2.24 + 117 -665.18 1.89 1.19 + 119 -437.00 1.24 0.55 + 120 -284.45 0.81 0.25 + 121 89.36 0.25 0.03 + 122 228.14 1.46 1.18 + 123 8430.48 3.83 1.30 + 125 12972.12 5.89 2.89 + 129 4252.72 3.02 1.26 + 131 3987.51 2.83 1.12 + 133 -3619.73 3.70 2.28 + 135 342.00 0.24 0.01 + 137 62.42 0.10 0.00 + 145 279.58 1.78 1.72 + 147 270.98 1.73 1.63 + 149 -629.05 4.02 7.74 + 151 -620.00 3.96 7.54 + 153 372.45 1.06 0.41 + 155 412.79 1.17 0.49 + 159 -425.27 1.21 0.52 + 161 -464.80 2.97 4.42 + 163 -134.15 0.38 0.06 + 169 198.64 1.27 0.92 + 171 541.59 1.54 0.81 + 173 7916.19 3.59 1.16 + 175 7840.58 3.56 1.14 + 177 7780.25 3.53 1.12 + 179 7824.07 3.55 1.13 + 180 3.80 0.01 0.00 + 181 3.80 0.01 0.00 + 183 7911.57 3.59 1.16 + 185 -21.26 0.14 0.01 + 186 565.05 3.61 6.35 + 187 7579.96 3.44 1.07 + 189 5218.04 2.37 0.54 + 191 -2304.48 1.63 0.41 + 193 -1719.00 1.22 0.24 + 195 809.88 2.30 1.72 + 197 724.95 2.06 1.40 + 199 -524.74 1.49 0.77 + 201 -1249.69 3.55 3.83 + 202 648.21 4.14 8.18 + 203 120.61 0.77 0.36 + 204 648.21 1.84 1.14 + 205 565.05 1.60 0.88 + 207 645.35 1.83 1.13 + 209 -84.93 0.54 0.19 + 211 310.35 0.88 0.29 + 213 649.98 1.84 1.14 + 215 632.03 1.79 1.08 + 217 -28.28 0.08 0.00 + 219 66.88 0.19 0.02 + 221 -66.88 0.43 0.12 + 223 741.27 2.10 1.46 + 225 766.15 2.17 1.55 + 229 5218.04 3.70 1.59 + 231 4825.72 3.42 1.37 + 233 4511.00 3.20 1.41 + 235 218.11 0.62 0.15 + 237 649.94 1.84 1.14 + 238 919.18 2.61 2.17 + 239 370.55 1.05 0.40 + 240 919.18 2.61 2.17 + 241 919.18 2.61 2.17 + 243 917.91 1.46 0.53 + 245 431.87 0.69 0.13 + 247 263.57 0.42 0.05 + 249 128.98 0.21 0.01 + 251 60.33 0.13 0.01 + 257 33.29 0.09 0.00 + 261 147.95 0.94 0.53 + 263 24.06 0.07 0.00 + 269 473.37 1.34 0.63 + 271 -30.18 0.19 0.03 + 273 480.77 1.36 0.65 + 275 159.60 0.45 0.08 + 277 6.34 0.02 0.00 + 281 153.26 0.63 0.19 + 283 256.04 0.73 0.20 + 285 -125.43 0.36 0.05 + 287 175.94 0.72 0.25 + 289 -132.71 0.54 0.15 + 291 79.60 0.33 0.06 + 293 -95.33 0.61 0.24 + 295 130.61 0.37 0.06 + 297 -145.68 0.93 0.52 + 299 -95.49 0.61 0.24 + 301 -95.49 0.61 0.24 + 303 -50.19 0.32 0.07 + 305 -519.50 1.47 0.75 + 307 -569.68 1.62 0.89 + 309 -105.05 0.67 0.28 + 311 737.08 2.09 1.44 + 313 85.96 0.24 0.03 + 315 -2528.88 1.79 0.48 + 317 120.96 0.77 0.37 + 319 97.15 0.28 0.03 + 321 7806.52 3.54 1.13 + 323 249.59 0.71 0.19 + 325 224.40 1.43 1.15 + 329 12972.12 5.89 2.93 + 330 0.00 0.00 0.00 + 333 -0.00 0.00 0.00 + 10 3330.25 0.00 -74.37 Pump + 335 12972.12 0.00 -95.08 Pump + + + Node Results at 3:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure % from + Node gpm ft psi Lake + -------------------------------------------------------- + 10 0.00 241.72 41.04 100.00 + 15 620.00 131.79 43.24 0.00 + 20 0.00 161.38 14.03 0.00 + 35 1719.00 152.98 60.87 0.00 + 40 0.00 148.83 7.33 0.00 + 50 0.00 137.94 9.29 0.00 + 60 0.00 209.36 90.72 0.00 + 601 0.00 304.81 132.07 0.00 + 61 0.00 304.81 132.07 0.00 + 101 273.53 179.34 59.51 100.00 + 103 191.81 176.92 58.03 100.00 + 105 194.93 169.41 61.06 100.00 + 107 78.68 168.71 63.57 100.00 + 109 333.22 171.19 65.38 100.00 + 111 204.39 163.57 66.54 74.47 + 113 28.81 161.31 69.03 29.92 + 115 75.02 164.20 65.08 42.58 + 117 169.50 165.40 65.77 85.32 + 119 253.63 165.27 70.75 0.00 + 120 0.00 165.45 71.69 0.00 + 121 59.95 167.87 73.61 0.00 + 123 0.00 172.19 69.84 0.00 + 125 65.66 166.72 67.47 0.00 + 127 25.43 163.14 46.42 0.00 + 129 0.00 163.13 48.59 0.00 + 131 61.56 163.11 68.07 0.00 + 139 8.48 158.39 55.20 0.00 + 141 14.18 155.06 65.45 0.00 + 143 8.93 144.22 64.44 0.00 + 145 39.79 156.48 67.37 0.00 + 147 12.31 157.56 60.26 0.00 + 149 38.98 158.02 61.54 0.00 + 151 208.05 162.50 55.90 0.00 + 153 63.60 162.57 41.76 0.00 + 157 74.58 162.86 64.89 0.00 + 159 59.50 159.54 66.53 0.00 + 161 22.75 157.29 66.42 0.00 + 163 13.56 156.80 65.78 0.00 + 164 0.00 156.80 65.78 0.00 + 166 3.74 156.80 68.81 0.00 + 167 20.97 154.76 69.22 0.00 + 169 0.00 154.76 69.22 0.00 + 171 56.65 153.40 68.20 0.00 + 173 0.00 153.37 68.19 0.00 + 177 83.76 152.94 62.80 0.00 + 179 0.00 152.90 62.79 0.00 + 181 0.00 152.98 62.82 0.00 + 183 0.00 153.06 61.55 0.00 + 184 0.00 152.01 58.93 0.00 + 185 36.94 152.85 59.30 0.00 + 187 0.00 154.66 61.60 0.00 + 189 155.40 154.56 65.24 0.00 + 191 117.94 157.59 57.45 0.03 + 193 102.69 157.59 60.48 0.90 + 195 0.00 157.58 61.56 0.00 + 197 24.54 159.26 59.04 3.73 + 199 171.82 146.84 64.49 0.00 + 201 64.24 145.95 63.20 0.00 + 203 4582.00 145.78 62.30 0.00 + 204 0.00 154.02 57.64 0.00 + 205 94.12 146.71 54.47 0.00 + 206 0.00 144.27 62.08 0.00 + 207 99.92 145.29 59.05 0.00 + 208 0.00 143.12 55.08 0.00 + 209 1.25 141.12 62.01 0.00 + 211 12.48 140.45 57.82 0.00 + 213 20.07 140.31 57.76 0.00 + 215 132.75 140.09 57.67 0.00 + 217 34.88 140.07 58.09 0.00 + 219 59.50 140.06 58.95 0.00 + 225 32.83 140.06 57.22 0.00 + 229 92.42 139.06 55.70 0.00 + 231 23.73 139.05 58.09 0.00 + 237 22.48 139.02 54.17 0.00 + 239 64.24 138.66 54.45 0.00 + 241 0.00 138.65 54.45 0.00 + 243 6.25 138.65 54.01 0.00 + 247 101.35 138.56 52.24 0.00 + 249 0.00 138.56 52.24 0.00 + 251 34.79 138.46 47.00 0.00 + 253 78.51 138.08 44.23 0.00 + 255 58.16 138.14 48.16 0.00 + 257 0.00 165.78 64.47 0.00 + 259 0.00 165.86 61.04 0.00 + 261 0.00 165.88 71.88 100.00 + 263 0.00 166.19 72.01 100.00 + 265 0.00 155.44 67.35 0.00 + 267 0.00 155.90 58.45 0.00 + 269 0.00 154.58 66.98 0.00 + 271 0.00 153.10 63.74 0.00 + 273 0.00 146.73 60.11 0.00 + 275 0.00 145.89 58.88 0.00 + River -12929.88 220.00 0.00 0.00 Reservoir + Lake -3307.91 167.00 0.00 100.00 Reservoir + 1 1176.83 148.83 7.33 0.00 Tank + 2 164.68 137.94 9.29 0.00 Tank + 3 3586.00 161.38 14.03 0.00 Tank + + + Link Results at 3:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 20 -3586.00 0.15 0.00 + 40 -1176.83 0.05 0.00 + 50 -164.68 0.01 0.00 + 60 12929.88 9.17 8.64 + 101 3307.91 4.17 4.39 + 103 1770.64 2.83 1.80 + 105 1263.74 3.58 3.91 + 107 406.20 1.15 0.48 + 109 1578.84 2.52 1.45 + 111 1245.62 3.53 3.81 + 112 435.35 1.24 0.54 + 113 710.77 2.02 1.35 + 114 254.28 1.62 1.45 + 115 327.52 2.09 2.31 + 116 936.24 2.66 2.24 + 117 -662.60 1.88 1.18 + 119 -437.14 1.24 0.55 + 120 -283.99 0.81 0.25 + 121 89.07 0.25 0.03 + 122 228.03 1.46 1.18 + 123 8425.90 3.82 1.30 + 125 12929.88 5.87 2.88 + 129 4216.00 2.99 1.24 + 131 3952.43 2.80 1.10 + 133 -3586.00 3.66 2.24 + 135 341.00 0.24 0.01 + 137 61.56 0.10 0.00 + 145 279.44 1.78 1.72 + 147 270.95 1.73 1.63 + 149 -628.93 4.01 7.74 + 151 -620.00 3.96 7.54 + 153 372.16 1.06 0.41 + 155 411.94 1.17 0.49 + 159 -424.26 1.20 0.52 + 161 -463.24 2.96 4.39 + 163 -134.31 0.38 0.06 + 169 197.91 1.26 0.91 + 171 536.98 1.52 0.80 + 173 7919.28 3.59 1.16 + 175 7844.70 3.56 1.14 + 177 7785.20 3.53 1.12 + 179 7830.43 3.55 1.14 + 180 3.74 0.01 0.00 + 181 3.74 0.01 0.00 + 183 7919.10 3.59 1.16 + 185 -20.97 0.13 0.01 + 186 566.46 3.62 6.38 + 187 7593.28 3.45 1.07 + 189 5293.89 2.40 0.55 + 191 -2242.74 1.59 0.39 + 193 -1719.00 1.22 0.24 + 195 747.76 2.12 1.48 + 197 664.00 1.88 1.19 + 199 -512.83 1.45 0.74 + 201 -1176.83 3.34 3.43 + 202 658.59 4.20 8.43 + 203 129.06 0.82 0.41 + 204 658.59 1.87 1.17 + 205 566.46 1.61 0.88 + 207 641.90 1.82 1.12 + 209 -84.29 0.54 0.19 + 211 304.86 0.86 0.28 + 213 650.76 1.85 1.14 + 215 632.17 1.79 1.08 + 217 -27.42 0.08 0.00 + 219 67.98 0.19 0.02 + 221 -67.98 0.43 0.13 + 223 741.27 2.10 1.46 + 225 765.81 2.17 1.55 + 229 5293.89 3.75 1.63 + 231 4901.49 3.48 1.41 + 233 4582.00 3.25 1.45 + 235 220.58 0.63 0.15 + 237 662.06 1.88 1.18 + 238 940.38 2.67 2.26 + 239 378.24 1.07 0.42 + 240 940.38 2.67 2.26 + 241 940.38 2.67 2.26 + 243 939.13 1.50 0.56 + 245 433.96 0.69 0.13 + 247 259.96 0.41 0.05 + 249 127.21 0.20 0.01 + 251 59.50 0.12 0.01 + 257 32.83 0.09 0.00 + 261 153.92 0.98 0.57 + 263 23.73 0.07 0.00 + 269 492.68 1.40 0.68 + 271 -37.77 0.24 0.04 + 273 507.98 1.44 0.72 + 275 170.07 0.48 0.10 + 277 6.25 0.02 0.00 + 281 163.82 0.67 0.22 + 283 273.67 0.78 0.23 + 285 -132.45 0.38 0.06 + 287 194.92 0.80 0.30 + 289 -164.68 0.67 0.22 + 291 78.51 0.32 0.06 + 293 -106.43 0.68 0.29 + 295 141.22 0.40 0.07 + 297 -145.03 0.93 0.51 + 299 -95.14 0.61 0.23 + 301 -95.14 0.61 0.23 + 303 -49.89 0.32 0.07 + 305 -517.57 1.47 0.75 + 307 -567.46 1.61 0.89 + 309 -105.98 0.68 0.29 + 311 738.15 2.09 1.44 + 313 80.84 0.23 0.02 + 315 -2466.76 1.75 0.46 + 317 122.99 0.79 0.38 + 319 97.59 0.28 0.03 + 321 7813.12 3.55 1.13 + 323 255.25 0.72 0.20 + 325 224.02 1.43 1.14 + 329 12929.88 5.87 2.91 + 330 0.00 0.00 0.00 + 333 -0.00 0.00 0.00 + 10 3307.91 0.00 -74.72 Pump + 335 12929.88 0.00 -95.45 Pump + + + Node Results at 4:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure % from + Node gpm ft psi Lake + -------------------------------------------------------- + 10 0.00 244.31 42.16 100.00 + 15 620.00 136.89 45.45 0.00 + 20 0.00 162.74 14.62 0.00 + 35 1791.00 157.83 62.97 0.00 + 40 0.00 150.49 8.05 16.76 + 50 0.00 138.61 9.58 0.00 + 60 0.00 209.57 90.81 0.00 + 601 0.00 306.26 132.70 0.00 + 61 0.00 306.26 132.70 0.00 + 101 144.36 187.67 63.12 100.00 + 103 101.23 185.48 61.74 100.00 + 105 102.88 176.88 64.29 100.00 + 107 41.53 176.18 66.81 100.00 + 109 175.86 179.80 69.11 100.00 + 111 107.87 170.38 69.49 97.83 + 113 15.21 167.75 71.82 96.09 + 115 39.60 170.71 67.90 91.61 + 117 89.46 171.11 68.25 85.32 + 119 133.86 169.89 72.74 0.78 + 120 0.00 170.71 73.97 26.11 + 121 31.64 172.05 75.42 0.00 + 123 0.00 176.28 71.62 0.00 + 125 34.66 170.49 69.11 0.00 + 127 13.42 165.42 47.41 0.00 + 129 0.00 165.42 49.58 0.00 + 131 32.49 165.41 69.07 0.00 + 139 4.48 162.28 56.88 0.00 + 141 7.49 160.03 67.61 0.00 + 143 4.71 149.33 66.65 0.00 + 145 21.00 161.75 69.66 0.00 + 147 6.50 162.94 62.59 0.00 + 149 20.57 163.43 63.88 0.00 + 151 109.80 167.87 58.22 0.00 + 153 33.57 167.93 44.08 0.00 + 157 39.36 167.60 66.94 0.26 + 159 31.40 164.42 68.64 0.00 + 161 12.01 162.26 68.57 0.00 + 163 7.16 161.78 67.93 0.00 + 164 0.00 161.78 67.93 0.00 + 166 1.98 161.78 70.97 0.00 + 167 11.07 159.76 71.39 0.03 + 169 0.00 159.76 71.39 0.20 + 171 29.90 158.37 70.35 0.04 + 173 0.00 158.34 70.34 0.01 + 177 44.21 157.77 64.89 0.00 + 179 0.00 157.69 64.86 21.01 + 181 0.00 157.84 64.93 0.00 + 183 0.00 157.93 63.66 53.49 + 184 0.00 157.09 61.14 22.99 + 185 19.49 157.88 61.48 45.00 + 187 0.00 159.88 63.86 77.06 + 189 82.02 159.68 67.46 64.94 + 191 62.24 163.44 59.99 83.94 + 193 54.20 163.44 63.02 91.54 + 195 0.00 163.41 64.09 68.37 + 197 12.95 165.42 61.71 94.60 + 199 90.68 152.25 66.83 0.00 + 201 33.90 151.38 65.55 0.00 + 203 4531.00 151.21 64.65 0.00 + 204 0.00 159.18 59.87 73.51 + 205 49.67 152.14 56.82 0.00 + 206 0.00 149.64 64.41 0.00 + 207 52.74 150.74 61.42 0.00 + 208 0.00 148.40 57.37 0.00 + 209 0.66 146.25 64.24 0.00 + 211 6.59 145.52 60.02 0.00 + 213 10.59 145.44 59.99 0.00 + 215 70.06 145.37 59.96 0.00 + 217 18.41 145.37 60.39 0.00 + 219 31.40 145.36 61.25 0.00 + 225 17.33 145.36 59.52 0.00 + 229 48.78 143.58 57.66 0.00 + 231 12.52 143.58 60.05 0.00 + 237 11.86 143.26 56.01 0.00 + 239 33.90 142.50 56.11 0.00 + 241 0.00 142.49 56.11 0.00 + 243 3.30 142.49 55.68 0.00 + 247 53.49 142.25 53.84 0.00 + 249 0.00 142.25 53.84 0.00 + 251 18.36 141.97 48.52 0.00 + 253 41.44 140.60 45.32 0.00 + 255 30.70 140.62 49.23 0.00 + 257 0.00 171.43 66.92 94.23 + 259 0.00 171.58 63.51 100.00 + 261 0.00 171.73 74.41 100.00 + 263 0.00 172.17 74.60 100.00 + 265 0.00 160.44 69.52 0.47 + 267 0.00 161.28 60.78 81.11 + 269 0.00 159.68 69.19 0.00 + 271 0.00 157.99 65.86 0.00 + 273 0.00 152.16 62.46 0.00 + 275 0.00 151.32 61.24 0.00 + River -12789.79 220.00 0.00 0.00 Reservoir + Lake -3139.84 167.00 0.00 100.00 Reservoir + 1 1600.38 150.49 8.05 0.00 Tank + 2 569.30 138.61 9.58 0.00 Tank + 3 4501.38 162.74 14.62 0.00 Tank + + + Link Results at 4:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 20 -4501.38 0.19 0.00 + 40 -1600.38 0.07 0.00 + 50 -569.30 0.02 0.00 + 60 12789.79 9.07 8.47 + 101 3139.84 3.96 3.99 + 103 1673.92 2.67 1.62 + 105 1321.56 3.75 4.25 + 107 405.13 1.15 0.48 + 109 1572.68 2.51 1.44 + 111 1396.82 3.96 4.71 + 112 307.79 0.87 0.29 + 113 771.15 2.19 1.57 + 114 257.60 1.64 1.48 + 115 363.60 2.32 2.80 + 116 1013.54 2.88 2.60 + 117 -813.55 2.31 1.73 + 119 -241.38 0.68 0.18 + 120 -648.27 1.84 1.14 + 121 -261.84 0.74 0.21 + 122 165.55 1.06 0.65 + 123 7634.33 3.47 1.08 + 125 12789.78 5.81 2.82 + 129 4958.27 3.52 1.68 + 131 4771.01 3.38 1.56 + 133 -4501.38 4.60 3.41 + 135 256.21 0.18 0.01 + 137 32.49 0.05 0.00 + 145 223.72 1.43 1.14 + 147 219.24 1.40 1.10 + 149 -624.71 3.99 7.64 + 151 -620.00 3.96 7.54 + 153 412.96 1.17 0.49 + 155 433.96 1.23 0.54 + 159 -440.45 1.25 0.56 + 161 -461.03 2.94 4.35 + 163 -119.03 0.34 0.05 + 169 152.60 0.97 0.56 + 171 451.80 1.28 0.58 + 173 7696.93 3.49 1.10 + 175 7657.57 3.48 1.09 + 177 7626.17 3.46 1.08 + 179 7757.64 3.52 1.12 + 180 1.98 0.00 0.00 + 181 1.98 0.00 0.00 + 183 7895.98 3.58 1.15 + 185 -11.07 0.07 0.00 + 186 597.56 3.81 7.04 + 187 7686.00 3.49 1.10 + 189 5101.70 2.32 0.51 + 191 -2554.40 1.81 0.49 + 193 -1791.00 1.27 0.25 + 195 1004.12 2.85 2.55 + 197 959.91 2.72 2.35 + 199 -640.47 1.82 1.11 + 201 -1600.38 4.54 6.06 + 202 634.99 4.05 7.88 + 203 56.93 0.36 0.09 + 204 634.99 1.80 1.09 + 205 597.56 1.70 0.98 + 207 697.40 1.98 1.30 + 209 -125.57 0.80 0.39 + 211 198.92 0.56 0.13 + 213 723.13 2.05 1.39 + 215 695.64 1.97 1.29 + 217 27.26 0.08 0.00 + 219 143.48 0.41 0.07 + 221 -143.48 0.92 0.50 + 223 812.63 2.31 1.73 + 225 825.58 2.34 1.78 + 229 5101.70 3.62 1.52 + 231 4815.58 3.42 1.37 + 233 4531.00 3.21 1.42 + 235 195.44 0.55 0.12 + 237 658.42 1.87 1.17 + 238 978.69 2.78 2.44 + 239 373.01 1.06 0.41 + 240 978.69 2.78 2.44 + 241 978.69 2.78 2.44 + 243 978.03 1.56 0.60 + 245 337.90 0.54 0.08 + 247 137.20 0.22 0.02 + 249 67.14 0.11 0.00 + 251 31.40 0.07 0.00 + 257 17.33 0.05 0.00 + 261 190.11 1.21 0.84 + 263 12.52 0.04 0.00 + 269 633.54 1.80 1.09 + 271 -128.80 0.82 0.41 + 273 750.48 2.13 1.49 + 275 270.43 0.77 0.22 + 277 3.30 0.01 0.00 + 281 267.13 1.09 0.53 + 283 446.15 1.27 0.57 + 285 -195.09 0.55 0.12 + 287 408.73 1.67 1.17 + 289 -569.30 2.33 2.17 + 291 41.44 0.17 0.02 + 293 -232.70 1.49 1.23 + 295 251.06 0.71 0.20 + 297 -220.87 1.41 1.11 + 299 -130.51 0.83 0.42 + 301 -130.51 0.83 0.42 + 303 -90.36 0.58 0.21 + 305 -592.68 1.68 0.96 + 307 -683.04 1.94 1.25 + 309 -147.48 0.94 0.53 + 311 843.13 2.39 1.85 + 313 -41.80 0.12 0.01 + 315 -2795.12 1.98 0.58 + 317 122.34 0.78 0.37 + 319 73.10 0.21 0.02 + 321 7748.50 3.52 1.11 + 323 250.68 0.71 0.20 + 325 240.72 1.54 1.31 + 329 12789.78 5.81 2.86 + 330 0.00 0.00 0.00 + 333 -0.00 0.00 0.00 + 10 3139.84 0.00 -77.31 Pump + 335 12789.79 0.00 -96.68 Pump + + + Node Results at 5:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure % from + Node gpm ft psi Lake + -------------------------------------------------------- + 10 0.00 242.16 41.23 100.00 + 15 360.00 150.32 51.27 0.00 + 20 0.00 163.30 14.86 0.00 + 35 1819.00 152.55 60.68 1.85 + 40 0.00 151.80 8.62 64.01 + 50 0.00 140.30 10.31 0.00 + 60 0.00 215.88 93.54 0.00 + 601 0.00 215.87 93.54 0.00 + 61 0.00 215.87 93.54 0.00 + 101 174.75 180.76 60.12 100.00 + 103 122.54 178.41 58.67 100.00 + 105 124.54 169.25 60.99 100.00 + 107 50.27 168.53 63.49 100.00 + 109 212.89 172.44 65.92 100.00 + 111 130.58 162.97 66.28 99.65 + 113 18.41 160.66 68.75 98.75 + 115 47.93 163.14 64.62 97.00 + 117 108.29 163.28 64.85 100.00 + 119 162.04 161.67 69.19 9.43 + 120 0.00 162.62 70.46 88.23 + 121 38.30 163.24 71.60 0.00 + 123 1219.00 164.46 66.50 0.00 + 125 41.95 163.24 65.97 0.00 + 127 16.25 163.26 46.48 0.00 + 129 0.00 163.26 48.64 0.00 + 131 39.33 163.25 68.13 0.00 + 139 5.42 160.67 56.19 0.00 + 141 9.06 158.83 67.09 0.00 + 143 5.70 154.87 69.05 0.00 + 145 25.42 159.20 68.55 0.00 + 147 7.87 159.49 61.09 0.37 + 149 24.90 159.62 62.23 1.04 + 151 132.92 160.91 55.21 1.96 + 153 40.64 160.96 41.06 0.00 + 157 47.65 159.91 63.61 9.43 + 159 38.01 157.47 65.63 4.15 + 161 14.54 155.81 65.78 5.90 + 163 8.67 155.44 65.19 5.83 + 164 0.00 155.44 65.19 0.00 + 166 2.39 155.44 68.22 0.00 + 167 13.40 153.89 68.85 3.26 + 169 0.00 153.89 68.85 6.17 + 171 36.19 152.81 67.95 4.41 + 173 0.00 152.78 67.93 3.98 + 177 53.52 152.56 62.64 1.85 + 179 0.00 152.56 62.64 64.86 + 181 0.00 152.56 62.64 2.37 + 183 0.00 152.68 61.39 95.96 + 184 0.00 151.72 58.81 97.00 + 185 23.60 152.48 59.14 97.30 + 187 0.00 154.05 61.34 98.48 + 189 99.29 153.87 64.94 95.65 + 191 75.35 156.94 57.17 99.28 + 193 65.61 156.94 60.20 94.70 + 195 0.00 156.91 61.28 94.33 + 197 15.68 158.65 58.78 99.32 + 199 109.77 146.96 64.55 1.76 + 201 41.04 146.13 63.27 1.64 + 203 4582.00 145.95 62.37 1.51 + 204 0.00 153.50 57.41 98.41 + 205 60.13 146.92 54.56 71.24 + 206 0.00 145.20 62.48 26.20 + 207 63.84 145.85 59.30 32.71 + 208 0.00 144.45 55.66 13.53 + 209 0.80 143.15 62.89 1.47 + 211 7.98 142.72 58.81 0.00 + 213 12.82 142.65 58.78 0.00 + 215 84.81 142.55 58.73 0.00 + 217 22.28 142.54 59.16 0.00 + 219 38.01 142.53 60.03 0.00 + 225 20.98 142.54 58.29 0.00 + 229 59.05 141.71 56.85 0.00 + 231 15.16 141.71 59.24 0.00 + 237 14.36 141.64 55.31 0.00 + 239 41.04 141.32 55.60 0.00 + 241 0.00 141.32 55.60 0.00 + 243 3.99 141.32 55.17 0.00 + 247 64.75 141.23 53.39 0.00 + 249 0.00 141.23 53.39 0.00 + 251 22.23 141.13 48.15 0.00 + 253 50.16 140.70 45.37 0.00 + 255 37.16 140.73 49.28 0.00 + 257 0.00 163.49 63.48 100.00 + 259 0.00 163.66 60.08 100.00 + 261 0.00 163.89 71.01 100.00 + 263 0.00 164.34 71.21 100.00 + 265 0.00 154.42 66.91 6.35 + 267 0.00 155.16 58.13 94.58 + 269 0.00 153.86 66.67 49.39 + 271 0.00 152.63 63.54 3.92 + 273 0.00 146.92 60.20 0.16 + 275 0.00 146.11 58.98 0.59 + River -7751.18 220.00 0.00 0.00 Reservoir + Lake -3279.91 167.00 0.00 100.00 Reservoir + 1 475.63 151.80 8.62 0.64 Tank + 2 248.17 140.30 10.31 0.00 Tank + 3 -476.98 163.30 14.86 0.00 Tank + + + Link Results at 5:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 20 476.98 0.02 0.00 + 40 -475.63 0.02 0.00 + 50 -248.17 0.01 0.00 + 60 7751.18 5.50 3.35 + 101 3279.91 4.14 4.32 + 103 1737.01 2.77 1.74 + 105 1368.14 3.88 4.53 + 107 411.17 1.17 0.49 + 109 1614.46 2.58 1.52 + 111 1401.58 3.98 4.74 + 112 214.49 0.61 0.15 + 113 719.45 2.04 1.38 + 114 234.20 1.49 1.24 + 115 360.91 2.30 2.77 + 116 935.24 2.65 2.24 + 117 -832.43 2.36 1.80 + 119 -135.72 0.38 0.06 + 120 -697.87 1.98 1.30 + 121 -343.28 0.97 0.35 + 122 109.44 0.70 0.30 + 123 6418.73 2.91 0.79 + 125 6532.18 2.96 0.81 + 129 -34.29 0.02 0.00 + 131 -219.80 0.16 0.01 + 133 476.98 0.49 0.05 + 135 240.93 0.17 0.01 + 137 39.33 0.06 0.00 + 145 201.60 1.29 0.94 + 147 196.18 1.25 0.89 + 149 -365.70 2.33 2.84 + 151 -360.00 2.30 2.75 + 153 178.58 0.51 0.10 + 155 204.00 0.58 0.13 + 159 -211.87 0.60 0.14 + 161 -236.77 1.51 1.27 + 163 -102.92 0.29 0.04 + 169 143.56 0.92 0.50 + 171 266.78 0.76 0.22 + 173 6687.77 3.04 0.85 + 175 6640.13 3.01 0.84 + 177 6602.11 3.00 0.83 + 179 6727.57 3.05 0.86 + 180 2.39 0.00 0.00 + 181 2.39 0.00 0.00 + 183 6854.98 3.11 0.89 + 185 -13.40 0.09 0.01 + 186 524.84 3.35 5.54 + 187 6714.11 3.05 0.85 + 189 4976.31 2.26 0.49 + 191 -1701.61 1.21 0.23 + 193 -1819.00 1.29 0.26 + 195 85.25 0.24 0.03 + 197 31.74 0.09 0.00 + 199 -443.89 1.26 0.56 + 201 -475.63 1.35 0.64 + 202 624.23 3.98 7.63 + 203 122.99 0.79 0.38 + 204 624.23 1.77 1.06 + 205 524.84 1.49 0.77 + 207 566.89 1.61 0.89 + 209 -120.90 0.77 0.36 + 211 127.47 0.36 0.06 + 213 645.74 1.83 1.13 + 215 620.45 1.76 1.05 + 217 29.27 0.08 0.00 + 219 140.00 0.40 0.07 + 221 -140.00 0.89 0.48 + 223 750.36 2.13 1.49 + 225 766.03 2.17 1.55 + 229 4976.31 3.53 1.46 + 231 4741.14 3.36 1.33 + 233 4582.00 3.25 1.45 + 235 125.39 0.36 0.05 + 237 568.64 1.61 0.89 + 238 743.75 2.11 1.46 + 239 238.96 0.68 0.18 + 240 743.75 2.11 1.46 + 241 743.75 2.11 1.46 + 243 742.95 1.19 0.36 + 245 310.29 0.50 0.07 + 247 166.09 0.27 0.02 + 249 81.27 0.13 0.01 + 251 38.01 0.08 0.00 + 257 20.98 0.06 0.00 + 261 131.37 0.84 0.43 + 263 15.16 0.04 0.00 + 269 424.69 1.20 0.52 + 271 -57.17 0.36 0.09 + 273 467.50 1.33 0.62 + 275 162.19 0.46 0.09 + 277 3.99 0.01 0.00 + 281 158.20 0.65 0.20 + 283 264.26 0.75 0.22 + 285 -121.67 0.35 0.05 + 287 215.12 0.88 0.36 + 289 -248.17 1.01 0.47 + 291 50.16 0.20 0.02 + 293 -120.37 0.77 0.36 + 295 142.60 0.40 0.07 + 297 -245.14 1.56 1.35 + 299 -140.29 0.90 0.48 + 301 -140.29 0.90 0.48 + 303 -104.86 0.67 0.28 + 305 -587.29 1.67 0.95 + 307 -692.14 1.96 1.28 + 309 -138.46 0.88 0.47 + 311 758.91 2.15 1.52 + 313 -75.17 0.21 0.02 + 315 -1904.25 1.35 0.29 + 317 120.86 0.77 0.36 + 319 4.54 0.01 0.00 + 321 6716.51 3.05 0.86 + 323 118.10 0.34 0.05 + 325 202.65 1.29 0.95 + 329 7751.18 3.52 1.13 + 330 7751.18 3.52 1.13 + 333 7751.18 3.52 1.13 + 10 3279.91 0.00 -75.16 Pump + 335 0.00 0.00 0.00 Pump + + + Node Results at 6:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure % from + Node gpm ft psi Lake + -------------------------------------------------------- + 10 0.00 242.47 41.37 100.00 + 15 360.00 150.76 51.46 0.00 + 20 0.00 163.12 14.79 0.00 + 35 1777.00 153.33 61.02 12.62 + 40 0.00 152.47 8.91 90.40 + 50 0.00 141.31 10.75 0.00 + 60 0.00 215.94 93.57 0.00 + 601 0.00 215.94 93.57 0.00 + 61 0.00 215.94 93.57 0.00 + 101 161.46 181.75 60.55 100.00 + 103 113.22 179.44 59.12 100.00 + 105 115.06 170.18 61.39 100.00 + 107 46.44 169.46 63.90 100.00 + 109 196.69 173.47 66.37 100.00 + 111 120.65 163.82 66.65 99.47 + 113 17.01 161.49 69.11 98.89 + 115 44.28 163.96 64.98 100.00 + 117 100.05 164.05 65.19 100.00 + 119 149.71 162.21 69.42 8.27 + 120 0.00 163.25 70.74 84.32 + 121 35.39 163.72 71.81 0.00 + 123 0.00 165.36 66.89 0.00 + 125 38.76 163.59 66.12 0.00 + 127 15.01 163.25 46.47 0.00 + 129 0.00 163.24 48.64 0.00 + 131 36.34 163.24 68.13 0.00 + 139 5.01 160.92 56.29 0.00 + 141 8.37 159.26 67.28 0.00 + 143 5.27 155.30 69.24 0.00 + 145 23.49 159.67 68.75 0.26 + 147 7.27 159.98 61.30 2.72 + 149 23.01 160.12 62.45 5.64 + 151 122.81 161.46 55.45 6.81 + 153 37.54 161.50 41.29 0.00 + 157 44.02 160.50 63.87 8.27 + 159 35.12 158.13 65.92 8.27 + 161 13.43 156.52 66.09 10.07 + 163 8.01 156.16 65.50 10.08 + 164 0.00 156.16 65.50 0.00 + 166 2.21 156.16 68.53 0.00 + 167 12.38 154.65 69.17 12.88 + 169 0.00 154.65 69.17 12.28 + 171 33.44 153.58 68.28 12.78 + 173 0.00 153.56 68.27 12.87 + 177 49.44 153.33 62.97 12.62 + 179 0.00 153.33 62.97 90.85 + 181 0.00 153.33 62.97 14.31 + 183 0.00 153.45 61.72 96.96 + 184 0.00 152.52 59.16 98.75 + 185 21.80 153.27 59.48 98.79 + 187 0.00 154.84 61.67 99.61 + 189 91.73 154.64 65.27 98.84 + 191 69.61 157.75 57.52 99.59 + 193 60.61 157.75 60.55 98.62 + 195 0.00 157.72 61.62 98.68 + 197 14.48 159.48 59.14 99.48 + 199 101.42 147.83 64.92 8.35 + 201 37.92 147.01 63.65 7.61 + 203 4572.00 146.83 62.76 7.57 + 204 0.00 154.29 57.75 99.63 + 205 55.56 147.80 54.94 96.96 + 206 0.00 146.14 62.89 67.28 + 207 58.98 146.77 59.70 68.13 + 208 0.00 145.43 56.08 65.39 + 209 0.74 144.21 63.35 61.27 + 211 7.37 143.79 59.27 54.67 + 213 11.85 143.73 59.24 15.36 + 215 78.36 143.64 59.21 0.00 + 217 20.59 143.64 59.64 0.00 + 219 35.12 143.63 60.50 0.00 + 225 19.38 143.63 58.77 0.00 + 229 54.55 142.82 57.33 0.00 + 231 14.01 142.81 59.71 0.00 + 237 13.27 142.73 55.78 21.90 + 239 37.92 142.42 56.08 10.54 + 241 0.00 142.42 56.08 3.97 + 243 3.69 142.42 55.64 0.00 + 247 59.82 142.32 53.87 0.26 + 249 0.00 142.32 53.87 1.39 + 251 20.54 142.22 48.63 0.00 + 253 46.34 141.78 45.83 0.00 + 255 34.33 141.80 49.74 0.00 + 257 0.00 164.21 63.79 100.00 + 259 0.00 164.39 60.40 100.00 + 261 0.00 164.66 71.35 100.00 + 263 0.00 165.12 71.54 100.00 + 265 0.00 155.16 67.23 11.97 + 267 0.00 155.94 58.47 98.68 + 269 0.00 154.62 67.00 40.57 + 271 0.00 153.41 63.87 15.39 + 273 0.00 147.80 60.58 4.82 + 275 0.00 146.99 59.36 3.86 + River -7682.94 220.00 0.00 0.00 Reservoir + Lake -3260.06 167.00 0.00 100.00 Reservoir + 1 508.82 152.47 8.91 1.94 Tank + 2 264.91 141.31 10.75 0.00 Tank + 3 869.37 163.12 14.79 0.00 Tank + + + Link Results at 6:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 20 -869.37 0.04 0.00 + 40 -508.82 0.02 0.00 + 50 -264.91 0.01 0.00 + 60 7682.94 5.45 3.30 + 101 3260.06 4.11 4.28 + 103 1726.24 2.75 1.72 + 105 1372.36 3.89 4.56 + 107 411.34 1.17 0.49 + 109 1613.02 2.57 1.51 + 111 1416.33 4.02 4.83 + 112 194.36 0.55 0.12 + 113 721.86 2.05 1.39 + 114 233.49 1.49 1.24 + 115 364.90 2.33 2.82 + 116 938.34 2.66 2.25 + 117 -845.95 2.40 1.86 + 119 -107.23 0.30 0.04 + 120 -732.26 2.08 1.42 + 121 -380.65 1.08 0.42 + 122 93.59 0.60 0.23 + 123 6267.38 2.84 0.75 + 125 7682.94 3.49 1.10 + 129 1286.57 0.91 0.14 + 131 1111.15 0.79 0.11 + 133 -869.37 0.89 0.16 + 135 226.78 0.16 0.01 + 137 36.34 0.06 0.00 + 145 190.44 1.22 0.85 + 147 185.43 1.18 0.81 + 149 -365.27 2.33 2.83 + 151 -360.00 2.30 2.75 + 153 188.21 0.53 0.11 + 155 211.69 0.60 0.14 + 159 -218.96 0.62 0.15 + 161 -241.97 1.54 1.32 + 163 -99.11 0.28 0.04 + 169 136.66 0.87 0.46 + 171 265.67 0.75 0.22 + 173 6584.26 2.99 0.82 + 175 6540.24 2.97 0.81 + 177 6505.12 2.95 0.81 + 179 6638.49 3.01 0.84 + 180 2.21 0.00 0.00 + 181 2.21 0.00 0.00 + 183 6770.66 3.07 0.87 + 185 -12.38 0.08 0.01 + 186 523.18 3.34 5.50 + 187 6649.72 3.02 0.84 + 189 4929.71 2.24 0.48 + 191 -1686.57 1.20 0.23 + 193 -1777.00 1.26 0.25 + 195 111.07 0.32 0.04 + 197 61.63 0.17 0.01 + 199 -447.19 1.27 0.57 + 201 -508.82 1.44 0.73 + 202 618.96 3.95 7.51 + 203 117.58 0.75 0.35 + 204 618.96 1.76 1.04 + 205 523.18 1.48 0.76 + 207 564.78 1.60 0.88 + 209 -125.97 0.80 0.39 + 211 108.57 0.31 0.04 + 213 649.14 1.84 1.14 + 215 623.48 1.77 1.06 + 217 34.94 0.10 0.01 + 219 146.80 0.42 0.07 + 221 -146.80 0.94 0.52 + 223 753.69 2.14 1.50 + 225 768.18 2.18 1.56 + 229 4929.71 3.50 1.43 + 231 4713.17 3.34 1.32 + 233 4572.00 3.24 1.45 + 235 115.12 0.33 0.05 + 237 558.19 1.58 0.86 + 238 722.79 2.05 1.39 + 239 223.58 0.63 0.16 + 240 722.79 2.05 1.39 + 241 722.79 2.05 1.39 + 243 722.05 1.15 0.34 + 245 294.77 0.47 0.06 + 247 153.45 0.24 0.02 + 249 75.09 0.12 0.01 + 251 35.12 0.07 0.00 + 257 19.38 0.05 0.00 + 261 129.47 0.83 0.41 + 263 14.01 0.04 0.00 + 269 419.91 1.19 0.51 + 271 -60.91 0.39 0.10 + 273 467.55 1.33 0.62 + 275 163.20 0.46 0.09 + 277 3.69 0.01 0.00 + 281 159.51 0.65 0.21 + 283 266.43 0.76 0.22 + 285 -121.65 0.35 0.05 + 287 221.34 0.90 0.38 + 289 -264.91 1.08 0.53 + 291 46.34 0.19 0.02 + 293 -124.25 0.79 0.38 + 295 144.78 0.41 0.07 + 297 -258.02 1.65 1.49 + 299 -145.80 0.93 0.52 + 301 -145.80 0.93 0.52 + 303 -112.22 0.72 0.32 + 305 -587.94 1.67 0.95 + 307 -700.15 1.99 1.31 + 309 -142.39 0.91 0.49 + 311 765.86 2.17 1.55 + 313 -92.94 0.26 0.03 + 315 -1888.07 1.34 0.28 + 317 120.33 0.77 0.36 + 319 -5.21 0.01 0.00 + 321 6628.28 3.01 0.83 + 323 103.25 0.29 0.04 + 325 201.50 1.29 0.94 + 329 7682.94 3.49 1.11 + 330 7682.94 3.49 1.11 + 333 7682.94 3.49 1.10 + 10 3260.06 0.00 -75.47 Pump + 335 0.00 0.00 0.00 Pump + + + Node Results at 7:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure % from + Node gpm ft psi Lake + -------------------------------------------------------- + 10 0.00 242.01 41.17 100.00 + 15 0.00 162.44 56.52 0.00 + 20 0.00 163.45 14.93 0.00 + 35 1842.00 153.44 61.07 16.30 + 40 0.00 153.19 9.22 88.86 + 50 0.00 142.40 11.22 11.93 + 60 0.00 215.96 93.58 0.00 + 601 0.00 215.96 93.58 0.00 + 61 0.00 215.96 93.57 0.00 + 101 203.25 180.26 59.91 100.00 + 103 142.52 177.90 58.45 100.00 + 105 144.85 169.11 60.93 100.00 + 107 58.46 168.41 63.44 100.00 + 109 247.60 172.03 65.75 100.00 + 111 151.88 163.17 66.37 100.00 + 113 21.41 160.99 68.89 100.00 + 115 55.75 163.38 64.73 100.00 + 117 125.95 163.61 65.00 100.00 + 119 188.46 162.43 69.52 9.12 + 120 0.00 163.18 70.71 87.22 + 121 44.54 163.96 71.91 0.00 + 123 0.00 165.60 66.99 0.00 + 125 48.79 163.86 66.23 0.00 + 127 18.90 163.57 46.61 0.00 + 129 0.00 163.57 48.78 0.00 + 131 45.74 163.56 68.27 0.00 + 139 6.30 162.89 57.15 0.00 + 141 10.54 162.45 68.65 0.00 + 143 6.63 162.44 72.34 0.00 + 145 29.56 162.37 69.92 2.74 + 147 9.15 162.36 62.33 6.16 + 149 28.96 162.35 63.41 6.02 + 151 154.59 162.35 55.83 6.02 + 153 47.26 162.37 41.67 0.00 + 157 55.42 160.68 63.94 9.12 + 159 44.21 158.26 65.97 9.12 + 161 16.91 156.61 66.13 11.10 + 163 10.08 156.25 65.54 11.09 + 164 0.00 156.25 65.54 0.00 + 166 2.78 156.25 68.57 0.00 + 167 15.58 154.72 69.21 12.12 + 169 0.00 154.72 69.21 12.55 + 171 42.09 153.67 68.32 12.21 + 173 0.00 153.64 68.31 12.12 + 177 62.24 153.45 63.02 16.30 + 179 0.00 153.45 63.02 89.43 + 181 0.00 153.45 63.02 16.33 + 183 0.00 153.55 61.77 99.57 + 184 0.00 152.54 59.16 99.38 + 185 27.45 153.31 59.50 99.43 + 187 0.00 154.82 61.67 99.57 + 189 115.47 154.67 65.29 99.80 + 191 87.63 157.49 57.41 100.00 + 193 76.30 157.49 60.44 99.99 + 195 0.00 157.48 61.52 99.98 + 197 18.23 159.10 58.97 100.00 + 199 127.67 147.75 64.89 12.06 + 201 47.73 146.91 63.61 12.05 + 203 4613.00 146.73 62.71 11.99 + 204 0.00 154.29 57.75 99.49 + 205 69.94 147.71 54.90 99.19 + 206 0.00 146.09 62.87 71.77 + 207 74.25 146.68 59.66 71.95 + 208 0.00 145.42 56.08 71.65 + 209 0.93 144.26 63.37 71.46 + 211 9.28 143.86 59.30 70.79 + 213 14.92 143.79 59.27 65.28 + 215 98.64 143.66 59.22 0.00 + 217 25.92 143.65 59.64 0.00 + 219 44.21 143.64 60.51 0.00 + 225 24.40 143.64 58.77 0.00 + 229 68.67 143.06 57.44 44.49 + 231 17.63 143.05 59.82 0.00 + 237 16.70 143.04 55.91 58.82 + 239 47.73 142.82 56.25 56.73 + 241 0.00 142.82 56.25 54.53 + 243 4.64 142.82 55.82 0.00 + 247 75.31 142.76 54.06 50.38 + 249 0.00 142.76 54.06 52.55 + 251 25.85 142.70 48.83 2.13 + 253 58.34 142.48 46.14 0.00 + 255 43.22 142.52 50.05 25.67 + 257 0.00 163.89 63.65 100.00 + 259 0.00 164.04 60.24 100.00 + 261 0.00 164.19 71.14 100.00 + 263 0.00 164.61 71.33 100.00 + 265 0.00 155.24 67.26 12.80 + 267 0.00 155.87 58.44 99.98 + 269 0.00 154.67 67.02 52.07 + 271 0.00 153.51 63.92 16.36 + 273 0.00 147.71 60.54 12.22 + 275 0.00 146.90 59.32 8.99 + River -7664.82 220.00 0.00 0.00 Reservoir + Lake -3289.86 167.00 0.00 100.00 Reservoir + 1 268.68 153.19 9.22 4.38 Tank + 2 125.26 142.40 11.22 0.00 Tank + 3 844.25 163.45 14.93 0.00 Tank + + + Link Results at 7:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 20 -844.25 0.04 0.00 + 40 -268.68 0.01 0.00 + 50 -125.26 0.01 0.00 + 60 7664.82 5.44 3.28 + 101 3289.86 4.15 4.35 + 103 1741.89 2.78 1.74 + 105 1344.72 3.81 4.39 + 107 405.98 1.15 0.48 + 109 1599.37 2.55 1.49 + 111 1351.77 3.83 4.43 + 112 239.71 0.68 0.18 + 113 697.02 1.98 1.30 + 114 229.42 1.46 1.20 + 115 347.52 2.22 2.58 + 116 905.03 2.57 2.11 + 117 -793.89 2.25 1.65 + 119 -177.36 0.50 0.10 + 120 -614.30 1.74 1.03 + 121 -271.28 0.77 0.23 + 122 123.72 0.79 0.38 + 123 6327.17 2.87 0.77 + 125 7664.82 3.48 1.09 + 129 1169.38 0.83 0.12 + 131 1006.78 0.71 0.09 + 133 -844.25 0.86 0.15 + 135 143.63 0.10 0.00 + 137 45.74 0.07 0.00 + 145 97.89 0.62 0.25 + 147 91.58 0.58 0.22 + 149 -6.63 0.04 0.00 + 151 0.00 0.00 0.00 + 153 -74.41 0.21 0.02 + 155 -44.85 0.13 0.01 + 159 35.70 0.10 0.01 + 161 6.73 0.04 0.00 + 163 -66.55 0.19 0.02 + 169 113.81 0.73 0.33 + 171 81.31 0.23 0.02 + 173 6671.70 3.03 0.84 + 175 6616.28 3.00 0.83 + 177 6572.07 2.98 0.82 + 179 6677.81 3.03 0.85 + 180 2.78 0.01 0.00 + 181 2.78 0.01 0.00 + 183 6791.86 3.08 0.87 + 185 -15.58 0.10 0.01 + 186 513.69 3.28 5.32 + 187 6619.27 3.00 0.83 + 189 5007.88 2.27 0.50 + 191 -1569.30 1.11 0.20 + 193 -1842.00 1.31 0.27 + 195 -76.37 0.22 0.02 + 197 -138.61 0.39 0.07 + 199 -407.29 1.16 0.48 + 201 -268.68 0.76 0.22 + 202 626.35 4.00 7.68 + 203 140.11 0.89 0.48 + 204 626.35 1.78 1.07 + 205 513.69 1.46 0.74 + 207 547.40 1.55 0.83 + 209 -106.34 0.68 0.29 + 211 157.01 0.45 0.08 + 213 620.03 1.76 1.05 + 215 595.87 1.69 0.97 + 217 16.69 0.05 0.00 + 219 122.64 0.35 0.05 + 221 -122.64 0.78 0.37 + 223 724.35 2.05 1.39 + 225 742.58 2.11 1.46 + 229 5007.88 3.55 1.47 + 231 4757.70 3.37 1.34 + 233 4613.00 3.27 1.47 + 235 122.51 0.35 0.05 + 237 557.86 1.58 0.86 + 238 701.65 1.99 1.31 + 239 218.04 0.62 0.15 + 240 701.65 1.99 1.31 + 241 701.65 1.99 1.31 + 243 700.72 1.12 0.32 + 245 323.14 0.52 0.08 + 247 193.17 0.31 0.03 + 249 94.52 0.15 0.01 + 251 44.21 0.09 0.00 + 257 24.40 0.07 0.00 + 261 115.06 0.73 0.33 + 263 17.63 0.05 0.00 + 269 368.30 1.04 0.40 + 271 -28.75 0.18 0.03 + 273 380.35 1.08 0.42 + 275 127.45 0.36 0.06 + 277 4.64 0.01 0.00 + 281 122.81 0.50 0.13 + 283 205.16 0.58 0.13 + 285 -99.17 0.28 0.04 + 287 146.67 0.60 0.18 + 289 -125.26 0.51 0.13 + 291 58.34 0.24 0.03 + 293 -80.15 0.51 0.17 + 295 106.00 0.30 0.04 + 297 -219.30 1.40 1.10 + 299 -128.66 0.82 0.41 + 301 -128.66 0.82 0.41 + 303 -90.64 0.58 0.21 + 305 -574.60 1.63 0.91 + 307 -665.24 1.89 1.19 + 309 -126.91 0.81 0.40 + 311 722.78 2.05 1.39 + 313 -39.33 0.11 0.01 + 315 -1765.63 1.25 0.25 + 317 121.07 0.77 0.37 + 319 1.44 0.00 0.00 + 321 6664.95 3.03 0.84 + 323 96.97 0.28 0.03 + 325 196.33 1.25 0.90 + 329 7664.82 3.48 1.11 + 330 7664.82 3.48 1.11 + 333 7664.82 3.48 1.11 + 10 3289.86 0.00 -75.01 Pump + 335 0.00 0.00 0.00 Pump + + + Node Results at 8:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure % from + Node gpm ft psi Lake + -------------------------------------------------------- + 10 0.00 242.38 41.33 100.00 + 15 0.00 162.97 56.75 0.00 + 20 0.00 163.77 15.07 0.00 + 35 1815.00 154.00 61.31 17.95 + 40 0.00 153.57 9.39 99.93 + 50 0.00 142.91 11.44 32.01 + 60 0.00 215.99 93.59 0.00 + 601 0.00 215.99 93.59 0.00 + 61 0.00 215.99 93.59 0.00 + 101 182.35 181.47 60.43 100.00 + 103 127.87 179.14 58.99 100.00 + 105 129.96 170.18 61.39 100.00 + 107 52.45 169.47 63.90 100.00 + 109 222.14 173.26 66.28 100.00 + 111 136.26 164.07 66.76 100.00 + 113 19.21 161.82 69.25 100.00 + 115 50.02 164.24 65.10 100.00 + 117 113.00 164.40 65.34 100.00 + 119 169.08 162.94 69.74 7.07 + 120 0.00 163.82 70.98 79.86 + 121 39.96 164.42 72.11 0.00 + 123 0.00 166.04 67.18 0.00 + 125 43.78 164.29 66.42 0.00 + 127 16.95 163.93 46.77 0.00 + 129 0.00 163.93 48.93 0.00 + 131 41.04 163.92 68.43 0.00 + 139 5.65 163.36 57.35 0.00 + 141 9.46 162.97 68.88 0.00 + 143 5.95 162.97 72.57 0.00 + 145 26.52 162.91 70.16 1.88 + 147 8.21 162.90 62.57 6.78 + 149 25.99 162.89 63.65 6.02 + 151 138.70 162.89 56.06 4.90 + 153 42.40 162.91 41.90 0.00 + 157 49.72 161.21 64.17 7.07 + 159 39.67 158.81 66.21 7.07 + 161 15.17 157.18 66.37 8.77 + 163 9.04 156.82 65.78 8.82 + 164 0.00 156.82 65.78 0.00 + 166 2.50 156.82 68.82 0.00 + 167 13.98 155.30 69.46 12.45 + 169 0.00 155.30 69.46 11.39 + 171 37.77 154.24 68.56 12.22 + 173 0.00 154.21 68.55 12.45 + 177 55.84 154.01 63.27 99.99 + 179 0.00 154.01 63.27 99.99 + 181 0.00 154.01 63.27 17.95 + 183 0.00 154.12 62.01 99.99 + 184 0.00 153.14 59.42 100.00 + 185 24.62 153.90 59.75 100.00 + 187 0.00 155.44 61.93 100.00 + 189 103.60 155.27 65.55 99.99 + 191 78.62 158.22 57.72 100.00 + 193 68.46 158.22 60.76 99.99 + 195 0.00 158.20 61.83 99.99 + 197 16.36 159.88 59.31 100.00 + 199 114.55 148.36 65.15 12.45 + 201 42.83 147.51 63.87 12.47 + 203 4643.00 147.33 62.97 12.59 + 204 0.00 154.90 58.02 100.00 + 205 62.75 148.33 55.17 99.50 + 206 0.00 146.74 63.15 74.12 + 207 66.61 147.31 59.93 74.20 + 208 0.00 146.10 56.37 74.10 + 209 0.84 144.98 63.68 74.06 + 211 8.32 144.60 59.62 73.79 + 213 13.38 144.53 59.59 71.72 + 215 88.50 144.43 59.55 0.00 + 217 23.25 144.42 59.98 0.00 + 219 39.67 144.41 60.84 0.00 + 225 21.89 144.41 59.11 0.00 + 229 61.61 143.78 57.75 68.42 + 231 15.82 143.78 60.13 0.00 + 237 14.99 143.74 56.22 69.14 + 239 42.83 143.51 56.55 68.50 + 241 0.00 143.50 56.55 68.09 + 243 4.17 143.50 56.11 0.00 + 247 67.56 143.44 54.35 66.64 + 249 0.00 143.44 54.35 67.29 + 251 23.19 143.37 49.12 47.73 + 253 52.34 143.09 46.40 4.34 + 255 38.77 143.12 50.32 47.16 + 257 0.00 164.63 63.97 100.00 + 259 0.00 164.79 60.57 100.00 + 261 0.00 165.00 71.49 100.00 + 263 0.00 165.43 71.68 100.00 + 265 0.00 155.81 67.51 10.85 + 267 0.00 156.51 58.72 99.99 + 269 0.00 155.26 67.27 29.90 + 271 0.00 154.08 64.16 14.40 + 273 0.00 148.33 60.80 12.27 + 275 0.00 147.50 59.58 12.37 + River -7631.38 220.00 0.00 0.00 Reservoir + Lake -3265.74 167.00 0.00 100.00 Reservoir + 1 355.87 153.57 9.39 5.88 Tank + 2 170.66 142.91 11.44 0.00 Tank + 3 986.40 163.77 15.07 0.00 Tank + + + Link Results at 8:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 20 -986.40 0.04 0.00 + 40 -355.87 0.01 0.00 + 50 -170.66 0.01 0.00 + 60 7631.38 5.41 3.26 + 101 3265.74 4.12 4.29 + 103 1729.18 2.76 1.72 + 105 1354.21 3.84 4.44 + 107 407.33 1.16 0.48 + 109 1601.31 2.56 1.49 + 111 1379.16 3.91 4.60 + 112 218.67 0.62 0.15 + 113 707.85 2.01 1.34 + 114 231.00 1.47 1.21 + 115 354.88 2.27 2.68 + 116 919.64 2.61 2.17 + 117 -816.92 2.32 1.74 + 119 -144.81 0.41 0.07 + 120 -666.50 1.89 1.20 + 121 -322.43 0.91 0.31 + 122 107.39 0.69 0.29 + 123 6196.96 2.81 0.74 + 125 7631.38 3.46 1.08 + 129 1287.06 0.91 0.14 + 131 1134.06 0.80 0.11 + 133 -986.40 1.01 0.21 + 135 130.70 0.09 0.00 + 137 41.04 0.07 0.00 + 145 89.66 0.57 0.21 + 147 84.01 0.54 0.19 + 149 -5.95 0.04 0.00 + 151 0.00 0.00 0.00 + 153 -68.60 0.19 0.02 + 155 -42.07 0.12 0.01 + 159 33.86 0.10 0.00 + 161 7.88 0.05 0.00 + 163 -66.83 0.19 0.02 + 169 109.23 0.70 0.30 + 171 64.00 0.18 0.02 + 173 6630.38 3.01 0.84 + 175 6580.66 2.99 0.82 + 177 6540.99 2.97 0.81 + 179 6659.87 3.02 0.84 + 180 2.50 0.01 0.00 + 181 2.50 0.01 0.00 + 183 6782.48 3.08 0.87 + 185 -13.98 0.09 0.01 + 186 518.22 3.31 5.41 + 187 6634.96 3.01 0.84 + 189 4992.26 2.27 0.49 + 191 -1604.94 1.14 0.21 + 193 -1815.00 1.29 0.26 + 195 -11.46 0.03 0.00 + 197 -67.31 0.19 0.02 + 199 -423.17 1.20 0.52 + 201 -355.87 1.01 0.37 + 202 625.27 3.99 7.66 + 203 131.67 0.84 0.43 + 204 625.27 1.77 1.06 + 205 518.22 1.47 0.75 + 207 554.84 1.57 0.85 + 209 -115.16 0.74 0.33 + 211 133.53 0.38 0.06 + 213 633.38 1.80 1.09 + 215 608.35 1.73 1.01 + 217 25.36 0.07 0.00 + 219 134.05 0.38 0.06 + 221 -134.05 0.86 0.44 + 223 737.36 2.09 1.44 + 225 753.72 2.14 1.50 + 229 4992.26 3.54 1.46 + 231 4766.83 3.38 1.34 + 233 4643.00 3.29 1.49 + 235 110.88 0.31 0.04 + 237 551.82 1.57 0.84 + 238 687.79 1.95 1.27 + 239 202.59 0.57 0.13 + 240 687.79 1.95 1.27 + 241 687.79 1.95 1.27 + 243 686.96 1.10 0.31 + 245 303.52 0.48 0.07 + 247 173.31 0.28 0.02 + 249 84.81 0.14 0.01 + 251 39.67 0.08 0.00 + 257 21.89 0.06 0.00 + 261 116.83 0.75 0.34 + 263 15.82 0.04 0.00 + 269 375.12 1.06 0.41 + 271 -39.39 0.25 0.05 + 273 399.52 1.13 0.46 + 275 136.18 0.39 0.06 + 277 4.17 0.01 0.00 + 281 132.01 0.54 0.14 + 283 220.52 0.63 0.15 + 285 -104.06 0.30 0.04 + 287 168.51 0.69 0.23 + 289 -170.66 0.70 0.23 + 291 52.34 0.21 0.03 + 293 -93.27 0.60 0.23 + 295 116.46 0.33 0.05 + 297 -236.68 1.51 1.27 + 299 -136.28 0.87 0.46 + 301 -136.28 0.87 0.46 + 303 -100.41 0.64 0.26 + 305 -580.24 1.65 0.92 + 307 -680.64 1.93 1.24 + 309 -134.14 0.86 0.44 + 311 742.49 2.11 1.46 + 313 -65.07 0.18 0.02 + 315 -1803.54 1.28 0.26 + 317 121.59 0.78 0.37 + 319 -10.70 0.03 0.00 + 321 6648.33 3.02 0.84 + 323 81.00 0.23 0.02 + 325 198.60 1.27 0.92 + 329 7631.38 3.46 1.10 + 330 7631.38 3.46 1.10 + 333 7631.38 3.46 1.10 + 10 3265.74 0.00 -75.38 Pump + 335 0.00 0.00 0.00 Pump + + + Node Results at 9:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure % from + Node gpm ft psi Lake + -------------------------------------------------------- + 10 0.00 241.99 41.16 100.00 + 15 0.00 162.56 56.57 0.00 + 20 0.00 164.15 15.23 0.00 + 35 1825.00 154.05 61.33 14.51 + 40 0.00 154.07 9.61 99.99 + 50 0.00 143.60 11.74 60.89 + 60 0.00 215.91 93.55 0.00 + 601 0.00 215.91 93.55 0.00 + 61 0.00 215.91 93.55 0.00 + 101 208.94 180.19 59.88 100.00 + 103 146.52 177.84 58.43 100.00 + 105 148.91 169.10 60.92 100.00 + 107 60.10 168.41 63.44 100.00 + 109 254.54 172.00 65.73 100.00 + 111 156.13 163.29 66.42 100.00 + 113 22.01 161.19 68.98 100.00 + 115 57.31 163.48 64.77 100.00 + 117 129.48 163.67 65.02 100.00 + 119 193.74 162.48 69.54 8.15 + 120 0.00 163.23 70.73 83.89 + 121 45.79 163.92 71.89 0.00 + 123 1866.00 164.91 66.69 0.00 + 125 50.16 163.94 66.27 0.00 + 127 19.43 164.04 46.82 0.00 + 129 0.00 164.04 48.98 0.00 + 131 47.02 164.03 68.47 0.00 + 139 6.48 163.16 57.26 0.00 + 141 10.84 162.56 68.71 0.00 + 143 6.82 162.56 72.39 0.00 + 145 30.39 162.46 69.96 0.71 + 147 9.41 162.43 62.37 6.87 + 149 29.78 162.42 63.45 6.02 + 151 158.93 162.41 55.86 4.55 + 153 48.59 162.43 41.70 0.00 + 157 56.97 160.82 64.01 8.15 + 159 45.45 158.54 66.10 8.15 + 161 17.38 157.00 66.29 9.99 + 163 10.36 156.65 65.71 9.97 + 164 0.00 156.65 65.71 0.00 + 166 2.86 156.65 68.75 0.00 + 167 16.02 155.22 69.42 10.73 + 169 0.00 155.22 69.42 11.26 + 171 43.27 154.22 68.56 10.84 + 173 0.00 154.20 68.55 10.73 + 177 63.99 154.06 63.29 99.99 + 179 0.00 154.07 63.29 99.99 + 181 0.00 154.06 63.29 14.50 + 183 0.00 154.16 62.03 99.99 + 184 0.00 153.10 59.40 100.00 + 185 28.22 153.87 59.74 100.00 + 187 0.00 155.31 61.88 100.00 + 189 118.71 155.17 65.50 99.99 + 191 90.09 157.84 57.56 100.00 + 193 78.44 157.84 60.59 100.00 + 195 0.00 157.82 61.67 99.99 + 197 18.74 159.38 59.09 100.00 + 199 131.25 148.30 65.13 10.73 + 201 49.07 147.46 63.85 10.72 + 203 4643.00 147.28 62.95 10.66 + 204 0.00 154.80 57.97 100.00 + 205 71.90 148.27 55.15 100.00 + 206 0.00 146.74 63.15 76.43 + 207 76.33 147.27 59.91 76.43 + 208 0.00 146.13 56.39 76.44 + 209 0.96 145.07 63.73 76.46 + 211 9.54 144.72 59.67 76.19 + 213 15.33 144.65 59.64 74.10 + 215 101.41 144.51 59.58 0.00 + 217 26.64 144.50 60.01 0.00 + 219 45.45 144.49 60.87 0.00 + 225 25.08 144.49 59.14 0.00 + 229 70.60 144.03 57.86 72.39 + 231 18.13 144.02 60.24 0.00 + 237 17.17 144.02 56.34 73.67 + 239 49.07 143.85 56.70 73.50 + 241 0.00 143.85 56.70 73.27 + 243 4.77 143.85 56.26 0.00 + 247 77.42 143.81 54.51 72.41 + 249 0.00 143.81 54.51 72.85 + 251 26.58 143.76 49.29 65.78 + 253 59.97 143.61 46.63 33.64 + 255 44.43 143.65 50.54 64.69 + 257 0.00 163.94 63.67 100.00 + 259 0.00 164.08 60.26 100.00 + 261 0.00 164.24 71.17 100.00 + 263 0.00 164.65 71.34 100.00 + 265 0.00 155.70 67.47 11.56 + 267 0.00 156.30 58.63 99.99 + 269 0.00 155.17 67.24 39.98 + 271 0.00 154.11 64.17 13.95 + 273 0.00 148.27 60.78 11.22 + 275 0.00 147.45 59.56 11.66 + River -7717.07 220.00 0.00 0.00 Reservoir + Lake -3291.14 167.00 0.00 100.00 Reservoir + 1 22.08 154.07 9.61 7.75 Tank + 2 72.91 143.60 11.74 0.00 Tank + 3 -773.71 164.15 15.23 0.00 Tank + + + Link Results at 9:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 20 773.71 0.03 0.00 + 40 -22.08 0.00 0.00 + 50 -72.91 0.00 0.00 + 60 7717.07 5.47 3.32 + 101 3291.14 4.15 4.35 + 103 1740.87 2.78 1.74 + 105 1341.32 3.81 4.37 + 107 403.89 1.15 0.47 + 109 1594.35 2.54 1.48 + 111 1339.81 3.80 4.36 + 112 225.19 0.64 0.16 + 113 682.46 1.94 1.25 + 114 223.85 1.43 1.14 + 115 343.78 2.19 2.53 + 116 884.29 2.51 2.02 + 117 -788.53 2.24 1.63 + 119 -162.56 0.46 0.09 + 120 -612.48 1.74 1.02 + 121 -276.86 0.79 0.23 + 122 115.99 0.74 0.34 + 123 6118.73 2.78 0.72 + 125 5851.07 2.66 0.66 + 129 -429.44 0.30 0.02 + 131 -594.17 0.42 0.03 + 133 773.71 0.79 0.13 + 135 160.12 0.11 0.00 + 137 47.02 0.08 0.00 + 145 113.09 0.72 0.32 + 147 106.61 0.68 0.29 + 149 -6.82 0.04 0.00 + 151 -0.00 0.00 0.00 + 153 -88.96 0.25 0.03 + 155 -58.56 0.17 0.01 + 159 49.16 0.14 0.01 + 161 19.38 0.12 0.01 + 163 -65.98 0.19 0.02 + 169 114.57 0.73 0.33 + 171 73.56 0.21 0.02 + 173 6463.90 2.93 0.80 + 175 6406.93 2.91 0.78 + 177 6361.47 2.89 0.77 + 179 6463.93 2.93 0.80 + 180 2.86 0.01 0.00 + 181 2.86 0.01 0.00 + 183 6574.33 2.98 0.82 + 185 -16.02 0.10 0.01 + 186 500.95 3.20 5.08 + 187 6410.82 2.91 0.78 + 189 5010.54 2.27 0.50 + 191 -1357.01 0.96 0.15 + 193 -1825.00 1.29 0.26 + 195 -280.14 0.79 0.24 + 197 -344.13 0.98 0.35 + 199 -366.21 1.04 0.39 + 201 -22.08 0.06 0.00 + 202 626.47 4.00 7.68 + 203 153.74 0.98 0.57 + 204 626.47 1.78 1.07 + 205 500.95 1.42 0.70 + 207 519.95 1.47 0.75 + 209 -101.19 0.65 0.26 + 211 147.49 0.42 0.07 + 213 602.14 1.71 0.99 + 215 577.83 1.64 0.92 + 217 15.43 0.04 0.00 + 219 119.84 0.34 0.05 + 221 -119.84 0.76 0.36 + 223 707.66 2.01 1.34 + 225 726.41 2.06 1.40 + 229 5010.54 3.55 1.47 + 231 4765.56 3.38 1.34 + 233 4643.00 3.29 1.49 + 235 113.72 0.32 0.05 + 237 546.96 1.55 0.83 + 238 665.46 1.89 1.19 + 239 194.83 0.55 0.12 + 240 665.46 1.89 1.19 + 241 665.46 1.89 1.19 + 243 664.50 1.06 0.29 + 245 318.90 0.51 0.08 + 247 198.58 0.32 0.03 + 249 97.17 0.16 0.01 + 251 45.45 0.09 0.00 + 257 25.08 0.07 0.00 + 261 104.98 0.67 0.28 + 263 18.13 0.05 0.00 + 269 336.07 0.95 0.34 + 271 -16.26 0.10 0.01 + 273 335.15 0.95 0.33 + 275 110.10 0.31 0.04 + 277 4.77 0.01 0.00 + 281 105.33 0.43 0.10 + 283 175.98 0.50 0.10 + 285 -87.52 0.25 0.03 + 287 115.43 0.47 0.11 + 289 -72.91 0.30 0.05 + 291 59.97 0.24 0.03 + 293 -61.88 0.39 0.11 + 295 88.46 0.25 0.03 + 297 -219.63 1.40 1.10 + 299 -128.42 0.82 0.41 + 301 -128.42 0.82 0.41 + 303 -91.21 0.58 0.22 + 305 -568.90 1.61 0.89 + 307 -660.11 1.87 1.17 + 309 -123.62 0.79 0.38 + 311 701.45 1.99 1.31 + 313 -40.36 0.11 0.01 + 315 -1544.86 1.10 0.19 + 317 121.34 0.77 0.37 + 319 -7.62 0.02 0.00 + 321 6450.71 2.93 0.79 + 323 73.49 0.21 0.02 + 325 187.85 1.20 0.83 + 329 7717.07 3.50 1.12 + 330 7717.07 3.50 1.11 + 333 7717.07 3.50 1.11 + 10 3291.14 0.00 -74.99 Pump + 335 0.00 0.00 0.00 Pump + + + Node Results at 10:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure % from + Node gpm ft psi Lake + -------------------------------------------------------- + 10 0.00 242.00 41.17 100.00 + 15 0.00 162.39 56.50 0.00 + 20 0.00 163.85 15.10 0.00 + 35 1856.00 154.07 61.34 27.08 + 40 0.00 154.10 9.62 99.99 + 50 0.00 143.90 11.87 65.89 + 60 0.00 215.90 93.55 0.00 + 601 0.00 215.89 93.55 0.00 + 61 0.00 215.89 93.55 0.00 + 101 205.15 180.25 59.90 100.00 + 103 143.86 177.90 58.45 100.00 + 105 146.20 169.10 60.92 100.00 + 107 59.01 168.41 63.44 100.00 + 109 249.91 172.06 65.76 100.00 + 111 153.30 163.27 66.41 100.00 + 113 21.61 161.18 68.97 100.00 + 115 56.27 163.44 64.75 100.00 + 117 127.13 163.60 64.99 100.00 + 119 190.22 162.32 69.47 7.38 + 120 0.00 163.11 70.67 81.06 + 121 44.96 163.72 71.81 0.00 + 123 1836.00 164.73 66.61 0.00 + 125 49.25 163.73 66.18 0.00 + 127 19.07 163.79 46.70 0.00 + 129 0.00 163.78 48.87 0.00 + 131 46.17 163.77 68.36 0.00 + 139 6.36 162.95 57.17 0.00 + 141 10.64 162.39 68.63 0.00 + 143 6.70 162.39 72.31 0.00 + 145 29.84 162.30 69.89 0.14 + 147 9.23 162.27 62.30 4.45 + 149 29.24 162.26 63.38 6.73 + 151 156.04 162.25 55.79 4.96 + 153 47.70 162.27 41.63 0.00 + 157 55.93 160.70 63.96 7.38 + 159 44.63 158.48 66.07 7.38 + 161 17.06 156.96 66.28 9.09 + 163 10.17 156.63 65.70 9.12 + 164 0.00 156.63 65.70 0.00 + 166 2.81 156.63 68.73 0.00 + 167 15.72 155.22 69.42 11.54 + 169 0.00 155.22 69.42 11.23 + 171 42.49 154.24 68.57 11.49 + 173 0.00 154.22 68.56 11.54 + 177 62.82 154.09 63.30 100.00 + 179 0.00 154.10 63.31 100.00 + 181 0.00 154.08 63.30 27.08 + 183 0.00 154.18 62.04 100.00 + 184 0.00 153.15 59.43 100.00 + 185 27.70 153.90 59.75 100.00 + 187 0.00 155.32 61.88 100.00 + 189 116.55 155.19 65.51 100.00 + 191 88.45 157.84 57.56 100.00 + 193 77.01 157.84 60.59 100.00 + 195 0.00 157.82 61.67 100.00 + 197 18.40 159.37 59.09 100.00 + 199 128.87 148.46 65.19 11.54 + 201 48.18 147.63 63.92 11.56 + 203 4592.00 147.45 63.03 11.61 + 204 0.00 154.82 57.99 100.00 + 205 70.59 148.43 55.21 100.00 + 206 0.00 146.94 63.23 76.68 + 207 74.94 147.46 59.99 76.66 + 208 0.00 146.35 56.48 76.70 + 209 0.94 145.33 63.84 76.82 + 211 9.36 144.98 59.79 76.95 + 213 15.06 144.91 59.76 76.43 + 215 99.57 144.78 59.70 1.74 + 217 26.16 144.77 60.13 0.00 + 219 44.63 144.76 60.99 0.00 + 225 24.62 144.77 59.26 0.00 + 229 69.31 144.31 57.98 74.20 + 231 17.80 144.31 60.36 0.00 + 237 16.86 144.31 56.46 76.20 + 239 48.18 144.14 56.82 76.20 + 241 0.00 144.14 56.82 76.15 + 243 4.69 144.14 56.39 0.00 + 247 76.01 144.10 54.64 75.78 + 249 0.00 144.10 54.64 75.96 + 251 26.09 144.06 49.42 70.41 + 253 58.88 143.91 46.76 61.64 + 255 43.62 143.95 50.67 70.97 + 257 0.00 163.85 63.63 100.00 + 259 0.00 164.00 60.23 100.00 + 261 0.00 164.18 71.14 100.00 + 263 0.00 164.59 71.32 100.00 + 265 0.00 155.70 67.46 11.00 + 267 0.00 156.31 58.63 100.00 + 269 0.00 155.18 67.24 30.54 + 271 0.00 154.13 64.18 13.86 + 273 0.00 148.43 60.85 10.15 + 275 0.00 147.62 59.63 10.75 + River -7731.31 220.00 0.00 0.00 Reservoir + Lake -3289.92 167.00 0.00 100.00 Reservoir + 1 -13.66 154.10 9.62 7.87 Tank + 2 72.20 143.90 11.87 0.00 Tank + 3 -613.26 163.85 15.10 0.00 Tank + + + Link Results at 10:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 20 613.26 0.03 0.00 + 40 13.66 0.00 0.00 + 50 -72.20 0.00 0.00 + 60 7731.31 5.48 3.33 + 101 3289.92 4.15 4.35 + 103 1739.64 2.78 1.74 + 105 1345.14 3.82 4.39 + 107 404.21 1.15 0.47 + 109 1595.79 2.55 1.48 + 111 1345.87 3.82 4.39 + 112 213.68 0.61 0.15 + 113 681.37 1.93 1.25 + 114 222.61 1.42 1.13 + 115 345.19 2.20 2.55 + 116 882.37 2.50 2.01 + 117 -794.73 2.25 1.66 + 119 -147.37 0.42 0.07 + 120 -628.75 1.78 1.07 + 121 -294.89 0.84 0.26 + 122 108.51 0.69 0.30 + 123 6018.67 2.73 0.70 + 125 5895.31 2.68 0.67 + 129 -276.83 0.20 0.01 + 131 -438.52 0.31 0.02 + 133 613.26 0.63 0.09 + 135 155.67 0.11 0.00 + 137 46.17 0.07 0.00 + 145 109.50 0.70 0.30 + 147 103.14 0.66 0.27 + 149 -6.70 0.04 0.00 + 151 -0.00 0.00 0.00 + 153 -85.81 0.24 0.03 + 155 -55.97 0.16 0.01 + 159 46.73 0.13 0.01 + 161 17.50 0.11 0.01 + 163 -64.73 0.18 0.02 + 169 112.43 0.72 0.32 + 171 73.81 0.21 0.02 + 173 6383.39 2.90 0.78 + 175 6327.46 2.87 0.77 + 177 6282.84 2.85 0.76 + 179 6388.26 2.90 0.78 + 180 2.81 0.01 0.00 + 181 2.81 0.01 0.00 + 183 6499.99 2.95 0.80 + 185 -15.72 0.10 0.01 + 186 496.66 3.17 5.00 + 187 6345.37 2.88 0.77 + 189 4949.30 2.25 0.49 + 191 -1353.58 0.96 0.15 + 193 -1856.00 1.32 0.27 + 195 -315.94 0.90 0.30 + 197 -378.76 1.07 0.42 + 199 -365.10 1.04 0.39 + 201 13.66 0.04 0.00 + 202 619.25 3.95 7.52 + 203 150.29 0.96 0.55 + 204 619.25 1.76 1.04 + 205 496.66 1.41 0.69 + 207 515.39 1.46 0.74 + 209 -103.53 0.66 0.27 + 211 138.90 0.39 0.07 + 213 600.19 1.70 0.98 + 215 575.99 1.63 0.91 + 217 17.84 0.05 0.00 + 219 122.48 0.35 0.05 + 221 -122.48 0.78 0.37 + 223 706.49 2.00 1.33 + 225 724.89 2.06 1.40 + 229 4949.30 3.51 1.44 + 231 4709.83 3.34 1.31 + 233 4592.00 3.26 1.46 + 235 110.60 0.31 0.04 + 237 539.30 1.53 0.81 + 238 653.97 1.86 1.15 + 239 189.61 0.54 0.12 + 240 653.97 1.86 1.15 + 241 653.97 1.86 1.15 + 243 653.03 1.04 0.28 + 245 313.25 0.50 0.07 + 247 194.97 0.31 0.03 + 249 95.41 0.15 0.01 + 251 44.63 0.09 0.00 + 257 24.62 0.07 0.00 + 261 103.22 0.66 0.27 + 263 17.80 0.05 0.00 + 269 330.42 0.94 0.33 + 271 -16.11 0.10 0.01 + 273 329.67 0.94 0.32 + 275 108.33 0.31 0.04 + 277 4.69 0.01 0.00 + 281 103.64 0.42 0.09 + 283 173.16 0.49 0.10 + 285 -86.08 0.24 0.03 + 287 113.72 0.46 0.11 + 289 -72.20 0.29 0.05 + 291 58.88 0.24 0.03 + 293 -60.98 0.39 0.10 + 295 87.07 0.25 0.03 + 297 -225.35 1.44 1.16 + 299 -130.83 0.84 0.42 + 301 -130.83 0.84 0.42 + 303 -94.52 0.60 0.23 + 305 -569.38 1.62 0.89 + 307 -663.90 1.88 1.19 + 309 -124.72 0.80 0.39 + 311 700.71 1.99 1.31 + 313 -47.58 0.13 0.01 + 315 -1540.06 1.09 0.19 + 317 119.96 0.77 0.36 + 319 -9.36 0.03 0.00 + 321 6375.27 2.89 0.78 + 323 69.65 0.20 0.02 + 325 186.48 1.19 0.81 + 329 7731.31 3.51 1.12 + 330 7731.31 3.51 1.11 + 333 7731.31 3.51 1.13 + 10 3289.92 0.00 -75.00 Pump + 335 0.00 0.00 0.00 Pump + + + Node Results at 11:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure % from + Node gpm ft psi Lake + -------------------------------------------------------- + 10 0.00 241.67 41.02 100.00 + 15 360.00 150.04 51.15 0.00 + 20 0.00 163.62 15.00 0.00 + 35 1801.00 153.90 61.27 28.35 + 40 0.00 154.08 9.61 99.99 + 50 0.00 144.20 12.00 71.40 + 60 0.00 215.87 93.54 0.00 + 601 0.00 215.87 93.53 0.00 + 61 0.00 215.86 93.53 0.00 + 101 226.04 179.16 59.43 100.00 + 103 158.51 176.79 57.97 100.00 + 105 161.09 168.16 60.51 100.00 + 107 65.02 167.47 63.03 100.00 + 109 275.37 170.97 65.29 100.00 + 111 168.91 162.54 66.10 100.00 + 113 23.81 160.55 68.70 100.00 + 115 62.00 162.72 64.44 100.00 + 117 140.07 162.92 64.70 100.00 + 119 209.59 161.84 69.26 7.83 + 120 0.00 162.54 70.43 82.74 + 121 49.54 163.32 71.64 0.00 + 123 1818.00 164.35 66.45 0.00 + 125 54.26 163.34 66.01 0.00 + 127 21.02 163.47 46.57 0.00 + 129 0.00 163.47 48.73 0.00 + 131 50.87 163.45 68.22 0.00 + 139 7.01 160.60 56.15 0.00 + 141 11.72 158.59 66.98 0.00 + 143 7.38 154.58 68.93 0.00 + 145 32.88 158.93 68.43 0.00 + 147 10.17 159.23 60.98 2.70 + 149 32.21 159.36 62.12 6.80 + 151 171.93 160.77 55.15 4.99 + 153 52.56 160.81 41.00 0.00 + 157 61.63 160.27 63.77 7.83 + 159 49.17 158.11 65.91 7.83 + 161 18.80 156.65 66.14 9.59 + 163 11.21 156.32 65.57 9.57 + 164 0.00 156.32 65.57 0.00 + 166 3.09 156.32 68.60 0.00 + 167 17.33 154.97 69.31 10.89 + 169 0.00 154.97 69.31 11.05 + 171 46.81 154.03 68.48 10.91 + 173 0.00 154.01 68.47 10.89 + 177 69.22 153.92 63.23 100.00 + 179 0.00 153.94 63.24 100.00 + 181 0.00 153.90 63.22 28.34 + 183 0.00 154.01 61.97 100.00 + 184 0.00 152.92 59.33 100.00 + 185 30.52 153.67 59.65 100.00 + 187 0.00 155.03 61.76 100.00 + 189 128.42 154.92 65.39 100.00 + 191 97.46 157.39 57.36 100.00 + 193 84.86 157.39 60.40 100.00 + 195 0.00 157.37 61.47 100.00 + 197 20.28 158.83 58.86 100.00 + 199 141.99 148.19 65.08 10.89 + 201 53.09 147.36 63.81 10.89 + 203 4613.00 147.19 62.91 10.86 + 204 0.00 154.56 57.87 100.00 + 205 77.78 148.16 55.10 100.00 + 206 0.00 146.74 63.15 76.74 + 207 82.57 147.21 59.89 76.74 + 208 0.00 146.19 56.41 76.75 + 209 1.04 145.25 63.80 76.81 + 211 10.32 144.93 59.77 76.88 + 213 16.59 144.86 59.73 76.71 + 215 109.71 144.70 59.67 66.91 + 217 28.82 144.69 60.09 0.00 + 219 49.17 144.68 60.96 0.00 + 225 27.13 144.68 59.22 0.00 + 229 76.37 144.39 58.01 76.45 + 231 19.61 144.38 60.40 0.00 + 237 18.58 144.39 56.50 76.56 + 239 53.09 144.28 56.88 76.67 + 241 0.00 144.27 56.88 76.70 + 243 5.16 144.27 56.45 0.00 + 247 83.75 144.25 54.70 76.70 + 249 0.00 144.25 54.70 76.70 + 251 28.75 144.23 49.50 73.72 + 253 64.88 144.15 46.86 69.96 + 255 48.06 144.19 50.78 74.47 + 257 0.00 163.20 63.35 100.00 + 259 0.00 163.34 59.94 100.00 + 261 0.00 163.48 70.84 100.00 + 263 0.00 163.88 71.01 100.00 + 265 0.00 155.43 67.35 11.19 + 267 0.00 155.97 58.48 100.00 + 269 0.00 154.92 67.13 33.62 + 271 0.00 153.94 64.10 13.64 + 273 0.00 148.16 60.73 10.61 + 275 0.00 147.36 59.52 10.79 + River -7759.91 220.00 0.00 0.00 Reservoir + Lake -3311.52 167.00 0.00 100.00 Reservoir + 1 -189.38 154.08 9.61 7.87 Tank + 2 -14.76 144.20 12.00 0.00 Tank + 3 -943.68 163.62 15.00 0.00 Tank + + + Link Results at 11:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 20 943.68 0.04 0.00 + 40 189.38 0.01 0.00 + 50 14.76 0.00 0.00 + 60 7759.91 5.50 3.36 + 101 3311.52 4.18 4.40 + 103 1749.92 2.79 1.76 + 105 1335.55 3.79 4.33 + 107 401.90 1.14 0.47 + 109 1591.42 2.54 1.48 + 111 1316.05 3.73 4.22 + 112 222.07 0.63 0.16 + 113 663.48 1.88 1.19 + 114 217.88 1.39 1.09 + 115 336.88 2.15 2.44 + 116 857.54 2.43 1.91 + 117 -772.56 2.19 1.57 + 119 -165.07 0.47 0.09 + 120 -591.46 1.68 0.96 + 121 -255.95 0.73 0.20 + 122 124.03 0.79 0.38 + 123 6221.08 2.82 0.74 + 125 5941.91 2.70 0.68 + 129 -452.74 0.32 0.02 + 131 -658.62 0.47 0.04 + 133 943.68 0.96 0.19 + 135 264.04 0.19 0.01 + 137 50.87 0.08 0.00 + 145 213.17 1.36 1.04 + 147 206.16 1.32 0.98 + 149 -367.38 2.34 2.86 + 151 -360.00 2.30 2.75 + 153 172.94 0.49 0.10 + 155 205.82 0.58 0.14 + 159 -215.99 0.61 0.15 + 161 -248.20 1.58 1.38 + 163 -99.05 0.28 0.04 + 169 151.62 0.97 0.56 + 171 321.08 0.91 0.31 + 173 6281.86 2.85 0.76 + 175 6220.23 2.82 0.74 + 177 6171.06 2.80 0.73 + 179 6263.68 2.84 0.75 + 180 3.09 0.01 0.00 + 181 3.09 0.01 0.00 + 183 6366.22 2.89 0.77 + 185 -17.33 0.11 0.01 + 186 485.54 3.10 4.79 + 187 6198.17 2.81 0.74 + 189 4973.46 2.26 0.49 + 191 -1177.89 0.84 0.12 + 193 -1801.00 1.28 0.26 + 195 -443.91 1.26 0.56 + 197 -513.13 1.46 0.74 + 199 -323.75 0.92 0.31 + 201 189.38 0.54 0.12 + 202 621.23 3.97 7.56 + 203 166.22 1.06 0.66 + 204 621.23 1.76 1.05 + 205 485.54 1.38 0.66 + 207 489.97 1.39 0.68 + 209 -92.54 0.59 0.22 + 211 150.73 0.43 0.08 + 213 578.08 1.64 0.92 + 215 554.33 1.57 0.85 + 217 9.92 0.03 0.00 + 219 111.42 0.32 0.04 + 221 -111.42 0.71 0.31 + 223 685.46 1.94 1.26 + 225 705.73 2.00 1.33 + 229 4973.46 3.53 1.45 + 231 4721.49 3.35 1.32 + 233 4613.00 3.27 1.47 + 235 109.98 0.31 0.04 + 237 533.29 1.51 0.79 + 238 626.26 1.78 1.07 + 239 175.55 0.50 0.10 + 240 626.26 1.78 1.07 + 241 626.26 1.78 1.07 + 243 625.23 1.00 0.26 + 245 321.79 0.51 0.08 + 247 214.83 0.34 0.04 + 249 105.12 0.17 0.01 + 251 49.17 0.10 0.00 + 257 27.13 0.08 0.00 + 261 90.38 0.58 0.21 + 263 19.61 0.06 0.00 + 269 293.12 0.83 0.26 + 271 5.61 0.04 0.00 + 273 268.93 0.76 0.22 + 275 84.04 0.24 0.03 + 277 5.16 0.01 0.00 + 281 78.88 0.32 0.06 + 283 131.80 0.37 0.06 + 285 -70.70 0.20 0.02 + 287 65.83 0.27 0.04 + 289 14.76 0.06 0.00 + 291 64.88 0.27 0.04 + 293 -32.35 0.21 0.03 + 295 61.10 0.17 0.01 + 297 -211.47 1.35 1.03 + 299 -124.53 0.79 0.39 + 301 -124.53 0.79 0.39 + 303 -86.94 0.55 0.20 + 305 -561.10 1.59 0.87 + 307 -648.04 1.84 1.13 + 309 -116.85 0.75 0.34 + 311 671.18 1.90 1.21 + 313 -28.47 0.08 0.00 + 315 -1357.09 0.96 0.15 + 317 120.14 0.77 0.36 + 319 -10.16 0.03 0.00 + 321 6249.37 2.84 0.75 + 323 55.41 0.16 0.01 + 325 179.20 1.14 0.76 + 329 7759.91 3.52 1.13 + 330 7759.91 3.52 1.13 + 333 7759.91 3.52 1.13 + 10 3311.52 0.00 -74.67 Pump + 335 0.00 0.00 0.00 Pump + + + Node Results at 12:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure % from + Node gpm ft psi Lake + -------------------------------------------------------- + 10 0.00 241.67 41.02 100.00 + 15 360.00 149.80 51.04 0.00 + 20 0.00 163.26 14.85 0.00 + 35 1819.00 153.70 61.18 34.44 + 40 0.00 153.81 9.50 99.99 + 50 0.00 144.14 11.97 70.82 + 60 0.00 215.85 93.53 0.00 + 601 0.00 215.84 93.53 0.00 + 61 0.00 215.84 93.53 0.00 + 101 220.34 179.19 59.44 100.00 + 103 154.51 176.81 57.98 100.00 + 105 157.03 168.10 60.49 100.00 + 107 63.38 167.41 63.01 100.00 + 109 268.42 170.98 65.29 100.00 + 111 164.65 162.42 66.04 100.00 + 113 23.21 160.42 68.64 100.00 + 115 60.44 162.59 64.38 100.00 + 117 136.54 162.76 64.63 100.00 + 119 204.31 161.58 69.15 6.86 + 120 0.00 162.32 70.33 79.03 + 121 48.29 163.03 71.51 0.00 + 123 1818.00 164.06 66.32 0.00 + 125 52.90 163.05 65.88 0.00 + 127 20.49 163.14 46.42 0.00 + 129 0.00 163.14 48.59 0.00 + 131 49.59 163.12 68.08 0.00 + 139 6.83 160.32 56.03 0.00 + 141 11.43 158.35 66.88 0.00 + 143 7.19 154.35 68.83 0.00 + 145 32.05 158.70 68.33 2.74 + 147 9.92 159.00 60.88 5.64 + 149 31.40 159.13 62.02 5.66 + 151 167.60 160.54 55.04 5.98 + 153 51.24 160.58 40.89 0.00 + 157 60.08 160.03 63.66 6.86 + 159 47.93 157.89 65.81 6.86 + 161 18.33 156.44 66.05 8.52 + 163 10.93 156.12 65.48 8.58 + 164 0.00 156.12 65.48 0.02 + 166 3.02 156.12 68.51 0.00 + 167 16.89 154.78 69.23 11.11 + 169 0.00 154.78 69.23 10.82 + 171 45.63 153.85 68.40 11.09 + 173 0.00 153.83 68.39 11.12 + 177 67.48 153.72 63.14 100.00 + 179 0.00 153.74 63.15 100.00 + 181 0.00 153.71 63.14 34.44 + 183 0.00 153.81 61.88 100.00 + 184 0.00 152.77 59.26 100.00 + 185 29.75 153.51 59.58 100.00 + 187 0.00 154.86 61.68 100.00 + 189 125.19 154.74 65.31 100.00 + 191 95.00 157.23 57.29 100.00 + 193 82.72 157.23 60.33 100.00 + 195 0.00 157.21 61.40 100.00 + 197 19.77 158.69 58.79 100.00 + 199 138.41 148.18 65.07 11.12 + 201 51.75 147.37 63.81 11.14 + 203 4531.00 147.20 62.92 11.19 + 204 0.00 154.38 57.79 100.00 + 205 75.82 148.15 55.09 100.00 + 206 0.00 146.74 63.15 77.91 + 207 80.49 147.22 59.89 77.91 + 208 0.00 146.19 56.41 77.91 + 209 1.01 145.24 63.80 77.89 + 211 10.06 144.92 59.76 77.87 + 213 16.17 144.85 59.73 76.76 + 215 106.94 144.70 59.67 72.49 + 217 28.10 144.69 60.09 0.00 + 219 47.93 144.68 60.96 0.00 + 225 26.45 144.68 59.22 0.00 + 229 74.45 144.36 58.00 76.73 + 231 19.12 144.36 60.38 0.00 + 237 18.11 144.36 56.48 76.82 + 239 51.75 144.24 56.86 76.87 + 241 0.00 144.24 56.86 76.88 + 243 5.03 144.24 56.43 0.00 + 247 81.64 144.21 54.69 76.83 + 249 0.00 144.21 54.69 76.88 + 251 28.03 144.18 49.48 76.07 + 253 63.24 144.10 46.84 73.97 + 255 46.85 144.14 50.76 75.19 + 257 0.00 163.02 63.27 100.00 + 259 0.00 163.16 59.87 100.00 + 261 0.00 163.32 70.77 100.00 + 263 0.00 163.73 70.94 100.00 + 265 0.00 155.23 67.26 10.53 + 267 0.00 155.79 58.41 100.00 + 269 0.00 154.73 67.05 25.24 + 271 0.00 153.75 64.02 13.00 + 273 0.00 148.15 60.73 9.98 + 275 0.00 147.37 59.52 10.44 + River -7781.19 220.00 0.00 0.00 Reservoir + Lake -3310.99 167.00 0.00 100.00 Reservoir + 1 -133.92 153.81 9.50 7.87 Tank + 2 3.40 144.14 11.97 0.00 Tank + 3 -841.12 163.26 14.85 0.00 Tank + + + Link Results at 12:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 20 841.12 0.04 0.00 + 40 133.92 0.01 0.00 + 50 -3.40 0.00 0.00 + 60 7781.19 5.52 3.37 + 101 3310.99 4.17 4.40 + 103 1749.39 2.79 1.76 + 105 1341.26 3.80 4.37 + 107 402.91 1.14 0.47 + 109 1594.88 2.54 1.48 + 111 1326.46 3.76 4.28 + 112 212.70 0.60 0.14 + 113 666.16 1.89 1.19 + 114 217.93 1.39 1.09 + 115 339.53 2.17 2.47 + 116 860.88 2.44 1.92 + 117 -781.32 2.22 1.60 + 119 -151.54 0.43 0.08 + 120 -610.94 1.73 1.02 + 121 -275.35 0.78 0.23 + 122 117.71 0.75 0.35 + 123 6154.68 2.79 0.73 + 125 5963.19 2.71 0.69 + 129 -357.50 0.25 0.01 + 131 -560.05 0.40 0.03 + 133 841.12 0.86 0.15 + 135 260.58 0.18 0.01 + 137 49.59 0.08 0.00 + 145 210.99 1.35 1.02 + 147 204.16 1.30 0.96 + 149 -367.19 2.34 2.86 + 151 -360.00 2.30 2.75 + 153 174.46 0.49 0.10 + 155 206.51 0.59 0.14 + 159 -216.43 0.61 0.15 + 161 -247.83 1.58 1.38 + 163 -98.42 0.28 0.03 + 169 149.66 0.96 0.54 + 171 317.01 0.90 0.30 + 173 6244.31 2.83 0.75 + 175 6184.23 2.81 0.73 + 177 6136.30 2.79 0.72 + 179 6233.21 2.83 0.74 + 180 3.02 0.01 0.00 + 181 3.02 0.01 0.00 + 183 6338.19 2.88 0.77 + 185 -16.89 0.11 0.01 + 186 483.77 3.09 4.76 + 187 6176.66 2.80 0.73 + 189 4893.94 2.22 0.48 + 191 -1237.09 0.88 0.13 + 193 -1819.00 1.29 0.26 + 195 -402.11 1.14 0.47 + 197 -469.59 1.33 0.63 + 199 -335.67 0.95 0.34 + 201 133.92 0.38 0.06 + 202 611.80 3.91 7.35 + 203 157.78 1.01 0.60 + 204 611.80 1.74 1.02 + 205 483.77 1.37 0.66 + 207 493.45 1.40 0.69 + 209 -96.78 0.62 0.24 + 211 144.64 0.41 0.07 + 213 580.55 1.65 0.93 + 215 557.03 1.58 0.86 + 217 13.02 0.04 0.00 + 219 115.24 0.33 0.05 + 221 -115.24 0.74 0.33 + 223 688.58 1.95 1.27 + 225 708.35 2.01 1.34 + 229 4893.94 3.47 1.41 + 231 4645.09 3.29 1.28 + 233 4531.00 3.21 1.42 + 235 110.44 0.31 0.04 + 237 528.24 1.50 0.78 + 238 628.27 1.78 1.07 + 239 180.52 0.51 0.11 + 240 628.27 1.78 1.07 + 241 628.27 1.78 1.07 + 243 627.26 1.00 0.26 + 245 318.15 0.51 0.07 + 247 209.41 0.33 0.03 + 249 102.47 0.16 0.01 + 251 47.93 0.10 0.00 + 257 26.45 0.08 0.00 + 261 92.56 0.59 0.22 + 263 19.12 0.05 0.00 + 269 299.06 0.85 0.27 + 271 1.00 0.01 0.00 + 273 279.95 0.79 0.24 + 275 88.59 0.25 0.03 + 277 5.03 0.01 0.00 + 281 83.55 0.34 0.06 + 283 139.61 0.40 0.07 + 285 -73.43 0.21 0.02 + 287 75.35 0.31 0.05 + 289 -3.40 0.01 0.00 + 291 63.24 0.26 0.04 + 293 -38.15 0.24 0.04 + 295 66.18 0.19 0.02 + 297 -217.88 1.39 1.09 + 299 -127.34 0.81 0.40 + 301 -127.34 0.81 0.40 + 303 -90.54 0.58 0.21 + 305 -563.44 1.60 0.88 + 307 -653.98 1.86 1.15 + 309 -118.92 0.76 0.35 + 311 675.95 1.92 1.23 + 313 -35.17 0.10 0.01 + 315 -1416.89 1.00 0.17 + 317 118.18 0.75 0.35 + 319 -7.74 0.02 0.00 + 321 6219.27 2.82 0.74 + 323 62.34 0.18 0.01 + 325 179.80 1.15 0.76 + 329 7781.19 3.53 1.14 + 330 7781.19 3.53 1.14 + 333 7781.19 3.53 1.14 + 10 3310.99 0.00 -74.67 Pump + 335 0.00 0.00 0.00 Pump + + + Node Results at 13:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure % from + Node gpm ft psi Lake + -------------------------------------------------------- + 10 0.00 241.82 41.08 100.00 + 15 360.00 149.72 51.01 0.00 + 20 0.00 162.94 14.71 0.00 + 35 1733.00 153.60 61.14 31.88 + 40 0.00 153.63 9.41 99.99 + 50 0.00 144.15 11.98 71.06 + 60 0.00 215.83 93.52 0.00 + 601 0.00 215.83 93.52 0.00 + 61 0.00 215.83 93.52 0.00 + 101 205.15 179.64 59.64 100.00 + 103 143.86 177.28 58.18 100.00 + 105 146.20 168.39 60.61 100.00 + 107 59.01 167.69 63.13 100.00 + 109 249.91 171.40 65.47 100.00 + 111 153.30 162.56 66.10 100.00 + 113 21.61 160.50 68.68 100.00 + 115 56.27 162.70 64.43 100.00 + 117 127.13 162.81 64.65 100.00 + 119 190.22 161.40 69.07 7.29 + 120 0.00 162.25 70.30 80.73 + 121 44.96 162.81 71.41 0.00 + 123 1822.00 163.84 66.23 0.00 + 125 49.25 162.81 65.78 0.00 + 127 19.07 162.86 46.30 0.00 + 129 0.00 162.86 48.47 0.00 + 131 46.17 162.84 67.96 0.00 + 139 6.36 160.16 55.96 0.00 + 141 10.64 158.26 66.84 0.00 + 143 6.70 154.27 68.79 0.00 + 145 29.84 158.62 68.30 5.53 + 147 9.23 158.92 60.84 5.24 + 149 29.24 159.05 61.98 5.24 + 151 156.04 160.44 55.00 5.24 + 153 47.70 160.47 40.85 0.00 + 157 55.93 159.87 63.60 7.29 + 159 44.63 157.76 65.76 7.29 + 161 17.06 156.33 66.01 8.99 + 163 10.17 156.02 65.44 8.97 + 164 0.00 156.02 65.44 11.12 + 166 2.81 156.02 68.47 0.00 + 167 15.72 154.68 69.19 10.30 + 169 0.00 154.68 69.19 10.42 + 171 42.49 153.76 68.36 10.31 + 173 0.00 153.73 68.35 10.29 + 177 62.82 153.62 63.10 100.00 + 179 0.00 153.63 63.10 100.00 + 181 0.00 153.61 63.09 31.86 + 183 0.00 153.70 61.83 100.00 + 184 0.00 152.71 59.23 100.00 + 185 27.70 153.43 59.55 100.00 + 187 0.00 154.79 61.66 100.00 + 189 116.55 154.65 65.28 100.00 + 191 88.45 157.24 57.30 100.00 + 193 77.01 157.24 60.33 100.00 + 195 0.00 157.22 61.41 100.00 + 197 18.40 158.74 58.82 100.00 + 199 128.87 148.17 65.07 10.29 + 201 48.18 147.37 63.81 10.28 + 203 4521.00 147.20 62.92 10.27 + 204 0.00 154.31 57.76 100.00 + 205 70.59 148.15 55.09 100.00 + 206 0.00 146.77 63.16 77.17 + 207 74.94 147.23 59.90 77.17 + 208 0.00 146.24 56.43 77.18 + 209 0.94 145.32 63.83 77.22 + 211 9.36 145.01 59.80 77.26 + 213 15.06 144.94 59.77 77.90 + 215 99.57 144.81 59.71 75.78 + 217 26.16 144.80 60.14 15.44 + 219 44.63 144.79 61.00 0.00 + 225 24.62 144.79 59.27 0.00 + 229 69.31 144.43 58.03 76.76 + 231 17.80 144.43 60.41 0.00 + 237 16.86 144.43 56.52 77.88 + 239 48.18 144.30 56.89 77.86 + 241 0.00 144.30 56.89 77.86 + 243 4.69 144.29 56.46 0.00 + 247 76.01 144.26 54.71 77.50 + 249 0.00 144.26 54.71 77.84 + 251 26.09 144.23 49.50 76.70 + 253 58.88 144.13 46.85 75.06 + 255 43.62 144.16 50.77 76.35 + 257 0.00 163.04 63.28 100.00 + 259 0.00 163.19 59.88 100.00 + 261 0.00 163.39 70.80 100.00 + 263 0.00 163.81 70.98 100.00 + 265 0.00 155.13 67.22 10.55 + 267 0.00 155.74 58.38 100.00 + 269 0.00 154.65 67.01 27.84 + 271 0.00 153.65 63.98 12.52 + 273 0.00 148.15 60.73 10.38 + 275 0.00 147.37 59.52 10.57 + River -7797.99 220.00 0.00 0.00 Reservoir + Lake -3301.99 167.00 0.00 100.00 Reservoir + 1 10.99 153.63 9.41 7.87 Tank + 2 36.11 144.15 11.98 0.00 Tank + 3 -675.07 162.94 14.71 0.00 Tank + + + Link Results at 13:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 20 675.07 0.03 0.00 + 40 -10.99 0.00 0.00 + 50 -36.11 0.00 0.00 + 60 7797.99 5.53 3.39 + 101 3301.99 4.16 4.38 + 103 1744.60 2.78 1.75 + 105 1352.25 3.84 4.43 + 107 405.02 1.15 0.48 + 109 1600.74 2.55 1.49 + 111 1350.83 3.83 4.42 + 112 194.13 0.55 0.12 + 113 674.65 1.91 1.22 + 114 219.10 1.40 1.10 + 115 346.00 2.21 2.56 + 116 872.14 2.47 1.97 + 117 -801.03 2.27 1.68 + 119 -123.49 0.35 0.05 + 120 -653.91 1.86 1.15 + 121 -317.87 0.90 0.30 + 122 103.50 0.66 0.27 + 123 6036.59 2.74 0.70 + 125 5975.99 2.71 0.69 + 129 -209.06 0.15 0.00 + 131 -403.50 0.29 0.02 + 133 675.07 0.69 0.10 + 135 252.50 0.18 0.01 + 137 46.17 0.07 0.00 + 145 206.33 1.32 0.98 + 147 199.97 1.28 0.93 + 149 -366.70 2.34 2.85 + 151 -360.00 2.30 2.75 + 153 177.37 0.50 0.10 + 155 207.21 0.59 0.14 + 159 -216.44 0.61 0.15 + 161 -245.68 1.57 1.36 + 163 -97.49 0.28 0.03 + 169 145.19 0.93 0.51 + 171 304.23 0.86 0.28 + 173 6196.06 2.81 0.74 + 175 6140.12 2.79 0.72 + 177 6095.50 2.77 0.71 + 179 6202.72 2.82 0.74 + 180 2.81 0.01 0.00 + 181 2.81 0.01 0.00 + 183 6314.13 2.87 0.76 + 185 -15.72 0.10 0.01 + 186 486.24 3.10 4.80 + 187 6170.01 2.80 0.73 + 189 4854.20 2.20 0.47 + 191 -1273.32 0.90 0.14 + 193 -1733.00 1.23 0.24 + 195 -278.64 0.79 0.24 + 197 -341.47 0.97 0.35 + 199 -352.45 1.00 0.37 + 201 -10.99 0.03 0.00 + 202 607.26 3.88 7.25 + 203 148.72 0.95 0.54 + 204 607.26 1.72 1.01 + 205 486.24 1.38 0.67 + 207 501.17 1.42 0.71 + 209 -104.08 0.66 0.28 + 211 128.40 0.36 0.06 + 213 590.31 1.67 0.95 + 215 566.29 1.61 0.88 + 217 19.85 0.06 0.00 + 219 124.28 0.35 0.05 + 221 -124.28 0.79 0.38 + 223 698.61 1.98 1.30 + 225 717.01 2.03 1.37 + 229 4854.20 3.44 1.39 + 231 4622.02 3.28 1.27 + 233 4521.00 3.21 1.42 + 235 103.32 0.29 0.04 + 237 522.21 1.48 0.76 + 238 617.89 1.75 1.04 + 239 170.62 0.48 0.10 + 240 617.89 1.75 1.04 + 241 617.89 1.75 1.04 + 243 616.95 0.98 0.26 + 245 304.46 0.49 0.07 + 247 194.97 0.31 0.03 + 249 95.41 0.15 0.01 + 251 44.63 0.09 0.00 + 257 24.62 0.07 0.00 + 261 94.43 0.60 0.23 + 263 17.80 0.05 0.00 + 269 303.13 0.86 0.28 + 271 -7.32 0.05 0.00 + 273 293.58 0.83 0.26 + 275 94.82 0.27 0.03 + 277 4.69 0.01 0.00 + 281 90.13 0.37 0.07 + 283 150.59 0.43 0.08 + 285 -76.80 0.22 0.02 + 287 90.92 0.37 0.07 + 289 -36.11 0.15 0.01 + 291 58.88 0.24 0.03 + 293 -47.70 0.30 0.07 + 295 73.79 0.21 0.02 + 297 -232.54 1.48 1.23 + 299 -133.79 0.85 0.44 + 301 -133.79 0.85 0.44 + 303 -98.75 0.63 0.25 + 305 -568.49 1.61 0.89 + 307 -667.24 1.89 1.20 + 309 -124.39 0.79 0.38 + 311 690.69 1.96 1.28 + 313 -52.64 0.15 0.01 + 315 -1454.36 1.03 0.17 + 317 117.77 0.75 0.35 + 319 -14.46 0.04 0.00 + 321 6189.74 2.81 0.74 + 323 52.84 0.15 0.01 + 325 181.04 1.16 0.77 + 329 7797.99 3.54 1.14 + 330 7797.99 3.54 1.14 + 333 7797.99 3.54 1.14 + 10 3301.99 0.00 -74.82 Pump + 335 0.00 0.00 0.00 Pump + + + Node Results at 14:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure % from + Node gpm ft psi Lake + -------------------------------------------------------- + 10 0.00 242.09 41.20 100.00 + 15 360.00 149.80 51.04 0.49 + 20 0.00 162.69 14.60 0.00 + 35 1664.00 153.74 61.20 27.92 + 40 0.00 153.64 9.42 99.99 + 50 0.00 144.30 12.04 72.61 + 60 0.00 215.82 93.51 0.00 + 601 0.00 215.82 93.51 0.00 + 61 0.00 215.82 93.51 0.00 + 101 182.35 180.52 60.02 100.00 + 103 127.87 178.18 58.57 100.00 + 105 129.96 169.04 60.89 100.00 + 107 52.45 168.33 63.40 100.00 + 109 222.14 172.25 65.84 100.00 + 111 136.26 162.98 66.29 100.00 + 113 19.21 160.87 68.84 100.00 + 115 50.02 163.08 64.60 100.00 + 117 113.00 163.13 64.79 100.00 + 119 169.08 161.34 69.04 8.23 + 120 0.00 162.33 70.34 84.17 + 121 39.96 162.65 71.34 0.00 + 123 1822.00 163.69 66.16 0.00 + 125 43.78 162.65 65.71 0.00 + 127 16.95 162.66 46.21 0.00 + 129 0.00 162.65 48.38 0.00 + 131 41.04 162.64 67.87 0.00 + 139 5.65 160.11 55.94 0.00 + 141 9.46 158.32 66.87 1.28 + 143 5.95 154.34 68.83 1.07 + 145 26.52 158.70 68.33 5.24 + 147 8.21 159.00 60.88 5.52 + 149 25.99 159.13 62.02 5.52 + 151 138.70 160.48 55.02 5.52 + 153 42.40 160.52 40.87 0.00 + 157 49.72 159.86 63.59 8.23 + 159 39.67 157.81 65.78 8.23 + 161 15.17 156.43 66.05 10.05 + 163 9.04 156.12 65.48 10.00 + 164 0.00 156.12 65.48 11.03 + 166 2.50 156.12 68.51 0.00 + 167 13.98 154.82 69.25 10.93 + 169 0.00 154.82 69.25 11.18 + 171 37.77 153.90 68.42 10.96 + 173 0.00 153.88 68.41 10.93 + 177 55.84 153.75 63.15 100.00 + 179 0.00 153.75 63.15 100.00 + 181 0.00 153.74 63.15 27.94 + 183 0.00 153.84 61.89 100.00 + 184 0.00 152.91 59.32 100.00 + 185 24.62 153.61 59.63 100.00 + 187 0.00 154.98 61.74 100.00 + 189 103.60 154.81 65.35 100.00 + 191 78.62 157.51 57.42 100.00 + 193 68.46 157.51 60.45 100.00 + 195 0.00 157.49 61.52 100.00 + 197 16.36 159.06 58.96 100.00 + 199 114.55 148.53 65.22 10.93 + 201 42.83 147.75 63.98 10.91 + 203 4449.00 147.59 63.08 10.83 + 204 0.00 154.50 57.84 100.00 + 205 62.75 148.51 55.25 100.00 + 206 0.00 147.15 63.33 77.97 + 207 66.61 147.62 60.06 77.96 + 208 0.00 146.62 56.60 77.98 + 209 0.84 145.71 64.00 77.95 + 211 8.32 145.40 59.97 77.93 + 213 13.38 145.34 59.94 77.19 + 215 88.50 145.24 59.90 76.95 + 217 23.25 145.23 60.33 68.93 + 219 39.67 145.22 61.19 0.00 + 225 21.89 145.22 59.46 0.00 + 229 61.61 144.77 58.18 77.90 + 231 15.82 144.77 60.56 0.00 + 237 14.99 144.76 56.66 77.11 + 239 42.83 144.60 57.02 77.25 + 241 0.00 144.60 57.02 77.25 + 243 4.17 144.60 56.59 0.00 + 247 67.56 144.55 54.84 77.31 + 249 0.00 144.55 54.84 77.25 + 251 23.19 144.51 49.62 76.87 + 253 52.34 144.35 46.95 76.12 + 255 38.77 144.38 50.86 77.02 + 257 0.00 163.27 63.38 100.00 + 259 0.00 163.45 59.99 100.00 + 261 0.00 163.72 70.94 100.00 + 263 0.00 164.16 71.13 100.00 + 265 0.00 155.26 67.27 11.47 + 267 0.00 155.94 58.47 100.00 + 269 0.00 154.80 67.07 36.83 + 271 0.00 153.79 64.04 14.14 + 273 0.00 148.50 60.88 15.51 + 275 0.00 147.75 59.69 9.75 + River -7809.38 220.00 0.00 0.00 Reservoir + Lake -3284.64 167.00 0.00 100.00 Reservoir + 1 166.39 153.64 9.42 7.87 Tank + 2 99.10 144.30 12.04 0.00 Tank + 3 -392.66 162.69 14.60 0.00 Tank + + + Link Results at 14:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 20 392.66 0.02 0.00 + 40 -166.39 0.01 0.00 + 50 -99.10 0.00 0.00 + 60 7809.38 5.54 3.40 + 101 3284.64 4.14 4.34 + 103 1735.50 2.77 1.73 + 105 1366.79 3.88 4.52 + 107 407.87 1.16 0.48 + 109 1607.63 2.57 1.50 + 111 1385.49 3.93 4.64 + 112 162.65 0.46 0.09 + 113 684.71 1.94 1.26 + 114 220.05 1.40 1.11 + 115 355.42 2.27 2.69 + 116 885.55 2.51 2.02 + 117 -828.96 2.35 1.79 + 119 -77.30 0.22 0.02 + 120 -715.02 2.03 1.36 + 121 -382.96 1.09 0.43 + 122 76.36 0.49 0.16 + 123 5824.17 2.64 0.66 + 125 5987.38 2.72 0.69 + 129 46.89 0.03 0.00 + 131 -135.11 0.10 0.00 + 133 392.66 0.40 0.04 + 135 240.59 0.17 0.01 + 137 41.04 0.07 0.00 + 145 199.55 1.27 0.92 + 147 193.90 1.24 0.88 + 149 -365.95 2.34 2.84 + 151 -360.00 2.30 2.75 + 153 181.51 0.51 0.11 + 155 208.03 0.59 0.14 + 159 -216.24 0.61 0.15 + 161 -242.23 1.55 1.32 + 163 -95.83 0.27 0.03 + 169 138.23 0.88 0.47 + 171 285.10 0.81 0.25 + 173 6085.00 2.76 0.71 + 175 6035.28 2.74 0.70 + 177 5995.61 2.72 0.69 + 179 6117.62 2.78 0.72 + 180 2.50 0.01 0.00 + 181 2.50 0.01 0.00 + 183 6238.33 2.83 0.75 + 185 -13.98 0.09 0.01 + 186 486.95 3.11 4.82 + 187 6123.88 2.78 0.72 + 189 4755.79 2.16 0.45 + 191 -1330.33 0.94 0.15 + 193 -1664.00 1.18 0.22 + 195 -151.72 0.43 0.08 + 197 -207.57 0.59 0.14 + 199 -373.96 1.06 0.41 + 201 -166.39 0.47 0.09 + 202 596.18 3.81 7.01 + 203 133.86 0.85 0.44 + 204 596.18 1.69 0.97 + 205 486.95 1.38 0.67 + 207 507.82 1.44 0.72 + 209 -115.20 0.74 0.33 + 211 100.47 0.28 0.04 + 213 602.14 1.71 0.99 + 215 577.71 1.64 0.92 + 217 30.04 0.09 0.00 + 219 137.17 0.39 0.06 + 221 -137.17 0.88 0.46 + 223 710.81 2.02 1.35 + 225 727.17 2.06 1.40 + 229 4755.79 3.37 1.34 + 231 4544.19 3.22 1.23 + 233 4449.00 3.16 1.37 + 235 97.05 0.28 0.03 + 237 514.51 1.46 0.74 + 238 616.24 1.75 1.03 + 239 168.34 0.48 0.09 + 240 616.24 1.75 1.03 + 241 616.24 1.75 1.03 + 243 615.40 0.98 0.25 + 245 286.78 0.46 0.06 + 247 173.31 0.28 0.02 + 249 84.81 0.14 0.01 + 251 39.67 0.08 0.00 + 257 21.89 0.06 0.00 + 261 100.09 0.64 0.26 + 263 15.82 0.04 0.00 + 269 320.29 0.91 0.31 + 271 -22.66 0.14 0.02 + 273 327.97 0.93 0.32 + 275 109.38 0.31 0.04 + 277 4.17 0.01 0.00 + 281 105.21 0.43 0.10 + 283 175.77 0.50 0.10 + 285 -85.54 0.24 0.03 + 287 123.18 0.50 0.13 + 289 -99.10 0.40 0.09 + 291 52.34 0.21 0.03 + 293 -67.03 0.43 0.12 + 295 90.23 0.26 0.03 + 297 -255.71 1.63 1.46 + 299 -143.93 0.92 0.50 + 301 -143.93 0.92 0.50 + 303 -111.78 0.71 0.32 + 305 -573.26 1.63 0.90 + 307 -685.03 1.94 1.26 + 309 -132.25 0.84 0.43 + 311 709.95 2.01 1.34 + 313 -81.48 0.23 0.02 + 315 -1512.28 1.07 0.19 + 317 115.97 0.74 0.34 + 319 -18.92 0.05 0.00 + 321 6106.08 2.77 0.72 + 323 52.36 0.15 0.01 + 325 181.95 1.16 0.78 + 329 7809.38 3.54 1.15 + 330 7809.38 3.54 1.14 + 333 7809.38 3.54 1.14 + 10 3284.64 0.00 -75.09 Pump + 335 0.00 0.00 0.00 Pump + + + Node Results at 15:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure % from + Node gpm ft psi Lake + -------------------------------------------------------- + 10 0.00 152.21 2.26 100.00 + 15 360.00 148.27 50.38 3.19 + 20 0.00 162.54 14.53 0.00 + 35 1620.00 151.74 60.33 24.42 + 40 0.00 153.88 9.52 100.00 + 50 0.00 144.70 12.22 77.44 + 60 0.00 215.70 93.46 0.00 + 601 0.00 215.70 93.46 0.00 + 61 0.00 215.70 93.46 0.00 + 101 157.66 152.21 47.75 100.00 + 103 110.56 152.18 47.31 100.00 + 105 112.36 152.92 53.91 100.00 + 107 45.35 152.90 56.72 100.00 + 109 192.06 152.18 57.14 100.00 + 111 117.81 152.34 61.67 100.00 + 113 16.61 152.33 65.14 100.00 + 115 43.24 152.90 60.19 100.00 + 117 97.70 154.74 61.16 100.00 + 119 146.19 158.97 68.01 9.77 + 120 0.00 157.61 68.29 89.32 + 121 34.55 160.99 70.62 0.00 + 123 1817.00 162.06 65.46 0.00 + 125 37.85 161.16 65.07 0.00 + 127 14.66 161.94 45.90 0.00 + 129 0.00 161.93 48.07 0.00 + 131 35.48 161.93 67.56 0.00 + 139 4.89 158.93 55.43 0.00 + 141 8.18 156.77 66.20 2.85 + 143 5.15 152.82 68.17 2.68 + 145 22.93 157.07 67.63 5.52 + 147 7.10 157.31 60.15 6.16 + 149 22.47 157.41 61.27 6.16 + 151 119.92 158.47 54.15 6.16 + 153 36.66 158.52 40.00 0.00 + 157 42.99 157.46 62.55 9.77 + 159 34.30 155.38 64.73 9.77 + 161 13.11 153.97 64.98 11.77 + 163 7.82 153.68 64.42 11.68 + 164 0.00 153.68 64.42 9.35 + 166 2.16 153.68 67.46 0.00 + 167 12.08 152.51 68.25 12.18 + 169 0.00 152.51 68.25 12.52 + 171 32.65 151.80 67.51 12.21 + 173 0.00 151.78 67.50 12.18 + 177 48.28 151.79 62.30 100.00 + 179 0.00 151.84 62.33 100.00 + 181 0.00 151.75 62.29 24.43 + 183 0.00 151.84 61.03 100.00 + 184 0.00 150.59 58.32 100.00 + 185 21.29 151.19 58.58 100.00 + 187 0.00 151.87 60.39 100.00 + 189 89.57 152.08 64.16 100.00 + 191 67.98 152.19 55.11 100.00 + 193 59.19 152.25 58.17 100.00 + 195 0.00 152.28 59.27 100.00 + 197 14.14 152.23 55.99 100.00 + 199 99.04 146.83 64.49 12.18 + 201 37.03 146.10 63.26 12.13 + 203 4439.00 145.94 62.37 12.01 + 204 0.00 151.63 56.60 100.00 + 205 54.25 146.82 54.52 100.00 + 206 0.00 145.88 62.78 77.72 + 207 57.59 146.11 59.41 77.70 + 208 0.00 145.62 56.17 77.75 + 209 0.72 145.17 63.77 77.85 + 211 7.20 145.02 59.80 77.90 + 213 11.57 144.98 59.79 77.97 + 215 76.52 144.90 59.75 76.79 + 217 20.10 144.90 60.18 72.18 + 219 34.30 144.89 61.05 0.00 + 225 18.92 144.89 59.32 0.00 + 229 53.27 144.77 58.18 77.18 + 231 13.68 144.77 60.56 0.00 + 237 12.96 144.77 56.66 77.82 + 239 37.03 144.72 57.08 77.71 + 241 0.00 144.72 57.08 77.68 + 243 3.60 144.72 56.64 0.00 + 247 58.42 144.71 54.90 77.66 + 249 0.00 144.71 54.90 77.68 + 251 20.05 144.71 49.70 77.86 + 253 45.25 144.67 47.09 76.63 + 255 33.52 144.70 51.00 77.11 + 257 0.00 155.85 60.16 100.00 + 259 0.00 155.60 56.59 100.00 + 261 0.00 154.72 67.04 100.00 + 263 0.00 154.62 67.00 100.00 + 265 0.00 152.88 66.24 13.00 + 267 0.00 152.23 56.86 100.00 + 269 0.00 152.23 65.96 51.48 + 271 0.00 151.76 63.16 16.86 + 273 0.00 146.82 60.15 25.47 + 275 0.00 146.11 58.98 20.46 + River -7930.21 220.00 0.00 0.00 Reservoir + Lake 0.00 167.00 0.00 100.00 Reservoir + 1 -808.20 153.88 9.52 7.87 Tank + 2 -26.94 144.70 12.22 0.00 Tank + 3 -2000.58 162.54 14.53 0.00 Tank + + + Link Results at 15:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 20 2000.58 0.08 0.00 + 40 808.20 0.03 0.00 + 50 26.94 0.00 0.00 + 60 7930.21 5.62 3.50 + 101 0.00 0.00 0.00 + 103 147.42 0.24 0.02 + 105 -305.07 0.87 0.28 + 107 51.96 0.15 0.01 + 109 36.86 0.06 0.00 + 111 -155.20 0.44 0.08 + 112 410.14 1.16 0.49 + 113 31.72 0.09 0.00 + 114 105.93 0.68 0.29 + 115 6.60 0.04 0.00 + 116 121.04 0.34 0.05 + 117 469.39 1.33 0.62 + 119 -552.71 1.57 0.85 + 120 846.94 2.40 1.86 + 121 761.81 2.16 1.53 + 122 272.85 1.74 1.65 + 123 7344.52 3.33 1.01 + 125 6113.21 2.77 0.72 + 129 -1538.71 1.09 0.19 + 131 -1731.72 1.23 0.24 + 133 2000.58 2.04 0.76 + 135 254.20 0.18 0.01 + 137 35.48 0.06 0.00 + 145 218.72 1.40 1.09 + 147 213.83 1.36 1.05 + 149 -365.15 2.33 2.83 + 151 -360.00 2.30 2.75 + 153 159.49 0.45 0.08 + 155 182.42 0.52 0.11 + 159 -189.52 0.54 0.12 + 161 -211.99 1.35 1.03 + 163 -118.50 0.34 0.05 + 169 155.16 0.99 0.58 + 171 213.40 0.61 0.15 + 173 6137.99 2.79 0.72 + 175 6095.00 2.77 0.71 + 177 6060.71 2.75 0.71 + 179 5871.36 2.66 0.67 + 180 2.16 0.00 0.00 + 181 2.16 0.00 0.00 + 183 5731.71 2.60 0.64 + 185 -12.08 0.08 0.01 + 186 332.03 2.12 2.37 + 187 5331.63 2.42 0.56 + 189 4558.78 2.07 0.42 + 191 -740.20 0.52 0.05 + 193 -1620.00 1.15 0.21 + 195 -760.09 2.16 1.52 + 197 -808.37 2.29 1.71 + 199 -0.17 0.00 0.00 + 201 808.20 2.29 1.71 + 202 548.29 3.50 6.00 + 203 237.55 1.52 1.28 + 204 548.29 1.56 0.83 + 205 332.03 0.94 0.33 + 207 237.72 0.67 0.18 + 209 132.67 0.85 0.43 + 211 388.00 1.10 0.44 + 213 199.36 0.57 0.13 + 215 191.68 0.54 0.12 + 217 -176.07 0.50 0.10 + 219 -176.23 0.50 0.10 + 221 176.23 1.12 0.73 + 223 91.27 0.26 0.03 + 225 105.41 0.30 0.04 + 229 4558.78 3.23 1.24 + 231 4386.44 3.11 1.15 + 233 4439.00 3.15 1.37 + 235 73.31 0.21 0.02 + 237 455.69 1.29 0.59 + 238 420.17 1.19 0.51 + 239 22.07 0.06 0.00 + 240 420.17 1.19 0.51 + 241 420.17 1.19 0.51 + 243 419.45 0.67 0.12 + 245 220.35 0.35 0.04 + 247 149.84 0.24 0.02 + 249 73.32 0.12 0.00 + 251 34.30 0.07 0.00 + 257 18.92 0.05 0.00 + 261 58.94 0.38 0.10 + 263 13.68 0.04 0.00 + 269 191.90 0.54 0.12 + 271 8.01 0.05 0.00 + 273 170.94 0.48 0.10 + 275 52.39 0.15 0.01 + 277 3.60 0.01 0.00 + 281 48.79 0.20 0.02 + 283 81.52 0.23 0.02 + 285 -45.15 0.13 0.01 + 287 35.52 0.15 0.01 + 289 26.94 0.11 0.01 + 291 45.25 0.18 0.02 + 293 -16.32 0.10 0.01 + 295 36.38 0.10 0.01 + 297 357.98 2.28 2.73 + 299 172.05 1.10 0.70 + 301 172.05 1.10 0.70 + 303 185.93 1.19 0.81 + 305 111.40 0.32 0.04 + 307 297.33 0.84 0.27 + 309 129.68 0.83 0.42 + 311 62.00 0.18 0.01 + 313 268.28 0.76 0.22 + 315 -859.91 0.61 0.07 + 317 111.66 0.71 0.32 + 319 -38.35 0.11 0.01 + 321 5861.39 2.66 0.66 + 323 -89.59 0.25 0.03 + 325 119.72 0.76 0.36 + 329 7930.21 3.60 1.18 + 330 7930.21 3.60 1.17 + 333 7930.21 3.60 1.19 + 10 0.00 0.00 0.00 Pump + 335 0.00 0.00 0.00 Pump + + + Node Results at 16:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure % from + Node gpm ft psi Lake + -------------------------------------------------------- + 10 0.00 151.99 2.16 100.00 + 15 0.00 159.18 55.11 2.49 + 20 0.00 161.78 14.20 0.00 + 35 1613.00 151.33 60.16 42.05 + 40 0.00 152.73 9.03 7.87 + 50 0.00 144.59 12.17 77.38 + 60 0.00 215.67 93.45 0.00 + 601 0.00 215.67 93.45 0.00 + 61 0.00 215.67 93.45 0.00 + 101 150.06 151.99 47.66 100.00 + 103 105.23 151.96 47.21 100.00 + 105 106.94 152.70 53.81 99.90 + 107 43.17 152.68 56.62 100.00 + 109 182.81 151.95 57.04 100.00 + 111 112.13 152.08 61.56 53.27 + 113 15.81 152.06 65.02 100.00 + 115 41.16 152.66 60.08 1.18 + 117 92.99 154.52 61.06 0.00 + 119 139.14 158.73 67.91 0.00 + 120 0.00 157.37 68.19 0.00 + 121 32.89 160.65 70.47 0.00 + 123 1824.00 161.73 65.31 0.00 + 125 36.02 160.78 64.90 0.00 + 127 13.95 161.35 45.65 0.00 + 129 0.00 161.35 47.82 0.00 + 131 33.77 161.34 67.31 0.00 + 139 4.65 160.08 55.93 0.00 + 141 7.78 159.18 67.24 2.24 + 143 4.90 159.18 70.92 2.30 + 145 21.83 159.01 68.46 5.98 + 147 6.75 158.93 60.85 5.81 + 149 21.39 158.91 61.92 6.28 + 151 114.14 158.77 54.28 6.28 + 153 34.89 158.81 40.13 0.00 + 157 40.91 157.20 62.44 0.00 + 159 32.64 155.07 64.59 0.00 + 161 12.48 153.63 64.83 0.00 + 163 7.44 153.34 64.27 0.00 + 164 0.00 153.34 64.27 9.05 + 166 2.05 153.34 67.31 0.00 + 167 11.50 152.13 68.09 9.77 + 169 0.00 152.14 68.09 6.43 + 171 31.08 151.41 67.34 9.59 + 173 0.00 151.39 67.33 9.77 + 177 45.95 151.37 62.12 7.89 + 179 0.00 151.41 62.14 7.89 + 181 0.00 151.34 62.11 21.34 + 183 0.00 151.41 60.84 48.97 + 184 0.00 150.22 58.16 86.24 + 185 20.26 150.82 58.42 68.19 + 187 0.00 151.52 60.24 78.97 + 189 85.26 151.71 64.00 47.37 + 191 64.70 151.89 54.98 97.94 + 193 56.33 151.94 58.04 46.51 + 195 0.00 151.97 59.13 9.77 + 197 13.46 151.93 55.87 100.00 + 199 94.26 146.50 64.34 9.86 + 201 35.24 145.78 63.12 10.94 + 203 4449.00 145.61 62.23 11.78 + 204 0.00 151.27 56.45 79.15 + 205 51.63 146.49 54.37 100.00 + 206 0.00 145.59 62.65 95.82 + 207 54.82 145.79 59.27 95.82 + 208 0.00 145.37 56.06 95.82 + 209 0.69 144.98 63.69 95.82 + 211 6.85 144.85 59.73 89.62 + 213 11.01 144.81 59.71 77.90 + 215 72.83 144.74 59.68 76.79 + 217 19.13 144.73 60.11 74.11 + 219 32.64 144.73 60.98 0.00 + 225 18.01 144.73 59.25 0.00 + 229 50.70 144.64 58.12 77.85 + 231 13.02 144.63 60.50 0.00 + 237 12.33 144.64 56.61 77.90 + 239 35.24 144.60 57.02 77.91 + 241 0.00 144.60 57.02 77.92 + 243 3.43 144.60 56.59 0.00 + 247 55.60 144.59 54.85 77.91 + 249 0.00 144.59 54.85 77.97 + 251 19.09 144.59 49.65 77.46 + 253 43.07 144.56 47.04 77.43 + 255 31.91 144.58 50.95 77.03 + 257 0.00 155.62 60.06 0.00 + 259 0.00 155.37 56.49 0.00 + 261 0.00 154.49 66.94 0.23 + 263 0.00 154.40 66.90 15.16 + 265 0.00 152.52 66.09 2.77 + 267 0.00 151.90 56.72 38.95 + 269 0.00 151.85 65.80 9.74 + 271 0.00 151.36 62.98 9.85 + 273 0.00 146.49 60.01 42.35 + 275 0.00 145.79 58.84 41.54 + River -7954.92 220.00 0.00 0.00 Reservoir + Lake 0.00 167.00 0.00 100.00 Reservoir + 1 -641.32 152.73 9.03 7.87 Tank + 2 -36.26 144.59 12.17 0.00 Tank + 3 -1661.51 161.78 14.20 0.00 Tank + + + Link Results at 16:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 20 1661.51 0.07 0.00 + 40 641.32 0.03 0.00 + 50 36.26 0.00 0.00 + 60 7954.92 5.64 3.52 + 101 0.00 0.00 0.00 + 103 153.85 0.25 0.02 + 105 -303.91 0.86 0.28 + 107 58.13 0.16 0.01 + 109 48.62 0.08 0.00 + 111 -134.19 0.38 0.06 + 112 419.24 1.19 0.51 + 113 52.37 0.15 0.01 + 114 109.32 0.70 0.30 + 115 14.96 0.10 0.01 + 116 145.88 0.41 0.07 + 117 468.98 1.33 0.62 + 119 -554.76 1.57 0.85 + 120 848.27 2.41 1.87 + 121 759.69 2.16 1.52 + 122 268.45 1.71 1.60 + 123 7137.22 3.24 0.96 + 125 6130.92 2.78 0.72 + 129 -1307.63 0.93 0.14 + 131 -1476.02 1.05 0.18 + 133 1661.51 1.70 0.54 + 135 171.53 0.12 0.00 + 137 33.77 0.05 0.00 + 145 137.76 0.88 0.46 + 147 133.11 0.85 0.44 + 149 -4.90 0.03 0.00 + 151 -0.00 0.00 0.00 + 153 -120.43 0.34 0.05 + 155 -98.60 0.28 0.03 + 159 91.85 0.26 0.03 + 161 70.46 0.45 0.13 + 163 -97.47 0.28 0.03 + 169 132.37 0.84 0.43 + 171 -53.79 0.15 0.01 + 173 6203.60 2.82 0.74 + 175 6162.68 2.80 0.73 + 177 6130.04 2.78 0.72 + 179 5943.10 2.70 0.68 + 180 2.05 0.00 0.00 + 181 2.05 0.00 0.00 + 183 5807.96 2.64 0.65 + 185 -11.50 0.07 0.00 + 186 339.34 2.17 2.47 + 187 5409.26 2.46 0.57 + 189 4529.04 2.06 0.41 + 191 -849.15 0.60 0.06 + 193 -1613.00 1.14 0.21 + 195 -639.84 1.82 1.11 + 197 -685.79 1.95 1.26 + 199 -44.48 0.13 0.01 + 201 641.32 1.82 1.11 + 202 545.22 3.48 5.94 + 203 226.14 1.44 1.16 + 204 545.22 1.55 0.82 + 205 339.34 0.96 0.34 + 207 270.62 0.77 0.23 + 209 126.11 0.80 0.39 + 211 387.20 1.10 0.44 + 213 213.23 0.60 0.14 + 215 218.80 0.62 0.15 + 217 -170.84 0.48 0.10 + 219 -174.46 0.49 0.10 + 221 174.46 1.11 0.72 + 223 107.09 0.30 0.04 + 225 120.55 0.34 0.05 + 229 4529.04 3.21 1.22 + 231 4366.36 3.10 1.14 + 233 4449.00 3.16 1.37 + 235 68.41 0.19 0.02 + 237 451.21 1.28 0.58 + 238 389.30 1.10 0.44 + 239 -7.10 0.02 0.00 + 240 389.30 1.10 0.44 + 241 389.30 1.10 0.44 + 243 388.61 0.62 0.11 + 245 207.16 0.33 0.03 + 247 142.62 0.23 0.02 + 249 69.79 0.11 0.00 + 251 32.64 0.07 0.00 + 257 18.01 0.05 0.00 + 261 53.53 0.34 0.08 + 263 13.02 0.04 0.00 + 269 174.60 0.50 0.10 + 271 10.20 0.07 0.00 + 273 152.08 0.43 0.08 + 275 45.89 0.13 0.01 + 277 3.43 0.01 0.00 + 281 42.46 0.17 0.02 + 283 70.95 0.20 0.02 + 285 -40.38 0.11 0.01 + 287 27.23 0.11 0.01 + 289 36.26 0.15 0.01 + 291 43.07 0.18 0.02 + 293 -11.49 0.07 0.00 + 295 30.57 0.09 0.00 + 297 357.03 2.28 2.71 + 299 171.62 1.10 0.70 + 301 171.62 1.10 0.70 + 303 185.42 1.18 0.81 + 305 111.94 0.32 0.04 + 307 297.36 0.84 0.27 + 309 125.64 0.80 0.39 + 311 93.16 0.26 0.03 + 313 263.18 0.75 0.21 + 315 -973.16 0.69 0.08 + 317 110.78 0.71 0.31 + 319 -42.37 0.12 0.01 + 321 5933.60 2.69 0.68 + 323 -117.88 0.33 0.05 + 325 124.01 0.79 0.38 + 329 7954.92 3.61 1.19 + 330 7954.92 3.61 1.17 + 333 7954.92 3.61 1.19 + 10 0.00 0.00 0.00 Pump + 335 0.00 0.00 0.00 Pump + + + Node Results at 17:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure % from + Node gpm ft psi Lake + -------------------------------------------------------- + 10 0.00 151.66 2.02 100.00 + 15 0.00 158.69 54.90 2.47 + 20 0.00 161.15 13.93 0.00 + 35 1620.00 150.80 59.93 4.20 + 40 0.00 151.83 8.63 7.87 + 50 0.00 144.44 12.11 76.62 + 60 0.00 215.64 93.43 0.00 + 601 0.00 215.63 93.43 0.00 + 61 0.00 215.63 93.43 0.00 + 101 140.56 151.66 47.52 68.43 + 103 98.57 151.63 47.07 100.00 + 105 100.17 152.35 53.67 0.00 + 107 40.43 152.33 56.47 100.00 + 109 171.24 151.62 56.90 100.00 + 111 105.04 151.70 61.40 2.63 + 113 14.81 151.67 64.85 34.17 + 115 38.55 152.30 59.92 2.63 + 117 87.11 154.13 60.89 0.00 + 119 130.34 158.23 67.69 0.00 + 120 0.00 156.91 67.99 0.00 + 121 30.81 160.13 70.25 0.00 + 123 1816.00 161.23 65.09 0.00 + 125 33.74 160.25 64.67 0.00 + 127 13.07 160.77 45.40 0.00 + 129 0.00 160.76 47.56 0.00 + 131 31.63 160.76 67.06 0.00 + 139 4.36 159.55 55.70 0.00 + 141 7.29 158.69 67.03 0.00 + 143 4.59 158.69 70.71 2.25 + 145 20.45 158.52 68.25 5.52 + 147 6.33 158.45 60.64 6.16 + 149 20.03 158.42 61.71 5.86 + 151 106.92 158.28 54.07 2.63 + 153 32.69 158.32 39.92 0.00 + 157 38.32 156.70 62.22 0.00 + 159 30.58 154.57 64.38 0.00 + 161 11.69 153.13 64.62 0.00 + 163 6.97 152.83 64.06 0.00 + 164 0.00 152.83 64.06 9.84 + 166 1.92 152.83 67.09 0.00 + 167 10.77 151.63 67.87 0.00 + 169 0.00 151.63 67.87 0.00 + 171 29.11 150.89 67.11 0.00 + 173 0.00 150.87 67.11 0.00 + 177 43.05 150.84 61.89 10.44 + 179 0.00 150.87 61.91 10.50 + 181 0.00 150.81 61.88 4.14 + 183 0.00 150.87 60.61 19.09 + 184 0.00 149.75 57.95 52.55 + 185 18.98 150.33 58.21 51.08 + 187 0.00 151.05 60.03 70.95 + 189 79.86 151.23 63.79 21.04 + 191 60.61 151.47 54.80 66.60 + 193 52.77 151.51 57.85 45.54 + 195 0.00 151.55 58.95 0.00 + 197 12.61 151.53 55.69 100.00 + 199 88.30 146.06 64.16 0.00 + 201 33.01 145.35 62.94 1.09 + 203 4460.00 145.19 62.04 1.14 + 204 0.00 150.80 56.24 70.97 + 205 48.37 146.06 54.19 67.33 + 206 0.00 145.23 62.49 99.74 + 207 51.35 145.39 59.10 95.81 + 208 0.00 145.04 55.91 100.00 + 209 0.64 144.72 63.58 100.00 + 211 6.42 144.62 59.63 96.99 + 213 10.32 144.59 59.62 95.72 + 215 68.22 144.53 59.59 77.86 + 217 17.92 144.52 60.02 76.41 + 219 30.58 144.51 60.88 0.00 + 225 16.87 144.52 59.15 0.00 + 229 47.49 144.45 58.04 77.89 + 231 12.20 144.45 60.42 0.00 + 237 11.55 144.46 56.53 78.50 + 239 33.01 144.43 56.95 77.80 + 241 0.00 144.43 56.95 77.85 + 243 3.21 144.43 56.52 0.00 + 247 52.08 144.43 54.78 77.90 + 249 0.00 144.43 54.78 77.90 + 251 17.88 144.42 49.58 77.25 + 253 40.34 144.40 46.97 77.12 + 255 29.89 144.42 50.88 77.32 + 257 0.00 155.20 59.88 0.00 + 259 0.00 154.96 56.31 0.00 + 261 0.00 154.10 66.77 0.00 + 263 0.00 154.01 66.73 0.00 + 265 0.00 152.01 65.87 0.00 + 267 0.00 151.45 56.53 42.58 + 269 0.00 151.35 65.58 0.00 + 271 0.00 150.84 62.76 0.00 + 273 0.00 146.05 59.82 44.52 + 275 0.00 145.38 58.66 44.55 + River -7991.76 220.00 0.00 0.00 Reservoir + Lake 0.00 167.00 0.00 100.00 Reservoir + 1 -538.31 151.83 8.63 7.87 Tank + 2 -50.47 144.44 12.11 0.00 Tank + 3 -1571.06 161.15 13.93 0.00 Tank + + + Link Results at 17:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 20 1571.06 0.07 0.00 + 40 538.31 0.02 0.00 + 50 50.47 0.00 0.00 + 60 7991.76 5.67 3.55 + 101 0.00 0.00 0.00 + 103 159.65 0.25 0.02 + 105 -300.21 0.85 0.27 + 107 62.62 0.18 0.01 + 109 61.08 0.10 0.00 + 111 -110.16 0.31 0.04 + 112 422.92 1.20 0.51 + 113 72.07 0.20 0.02 + 114 111.65 0.71 0.31 + 115 22.19 0.14 0.02 + 116 168.91 0.48 0.09 + 117 463.01 1.31 0.61 + 119 -550.94 1.56 0.84 + 120 835.02 2.37 1.82 + 121 749.01 2.12 1.48 + 122 266.03 1.70 1.57 + 123 7106.59 3.23 0.95 + 125 6175.76 2.80 0.73 + 129 -1227.68 0.87 0.13 + 131 -1392.20 0.99 0.16 + 133 1571.06 1.60 0.49 + 135 165.79 0.12 0.00 + 137 31.63 0.05 0.00 + 145 134.15 0.86 0.44 + 147 129.79 0.83 0.42 + 149 -4.59 0.03 0.00 + 151 0.00 0.00 0.00 + 153 -117.91 0.33 0.05 + 155 -97.47 0.28 0.03 + 159 91.14 0.26 0.03 + 161 71.11 0.45 0.14 + 163 -98.10 0.28 0.03 + 169 130.78 0.83 0.42 + 171 -62.29 0.18 0.01 + 173 6203.53 2.82 0.74 + 175 6165.21 2.80 0.73 + 177 6134.63 2.78 0.72 + 179 5952.92 2.70 0.68 + 180 1.92 0.00 0.00 + 181 1.92 0.00 0.00 + 183 5824.94 2.64 0.66 + 185 -10.77 0.07 0.00 + 186 344.84 2.20 2.54 + 187 5435.71 2.47 0.58 + 189 4487.82 2.04 0.41 + 191 -918.77 0.65 0.07 + 193 -1620.00 1.15 0.21 + 195 -573.74 1.63 0.91 + 197 -616.79 1.75 1.04 + 199 -78.47 0.22 0.02 + 201 538.31 1.53 0.80 + 202 541.35 3.46 5.86 + 203 215.49 1.38 1.06 + 204 541.35 1.54 0.81 + 205 344.84 0.98 0.35 + 207 293.97 0.83 0.26 + 209 118.49 0.76 0.35 + 211 378.46 1.07 0.42 + 213 226.35 0.64 0.16 + 215 241.35 0.68 0.18 + 217 -163.90 0.46 0.09 + 219 -170.02 0.48 0.10 + 221 170.02 1.09 0.69 + 223 123.05 0.35 0.05 + 225 135.66 0.38 0.06 + 229 4487.82 3.18 1.20 + 231 4339.89 3.08 1.13 + 233 4460.00 3.16 1.38 + 235 59.64 0.17 0.01 + 237 443.07 1.26 0.56 + 238 348.15 0.99 0.36 + 239 -43.57 0.12 0.01 + 240 348.15 0.99 0.36 + 241 348.15 0.99 0.36 + 243 347.51 0.55 0.09 + 245 190.13 0.30 0.03 + 247 133.59 0.21 0.02 + 249 65.37 0.10 0.00 + 251 30.58 0.06 0.00 + 257 16.87 0.05 0.00 + 261 46.22 0.30 0.06 + 263 12.20 0.03 0.00 + 269 150.96 0.43 0.08 + 271 13.47 0.09 0.01 + 273 125.94 0.36 0.05 + 275 36.80 0.10 0.01 + 277 3.21 0.01 0.00 + 281 33.59 0.14 0.01 + 283 56.13 0.16 0.01 + 285 -34.08 0.10 0.00 + 287 15.59 0.06 0.00 + 289 50.47 0.21 0.02 + 291 40.34 0.16 0.02 + 293 -4.17 0.03 0.00 + 295 22.05 0.06 0.00 + 297 352.04 2.25 2.64 + 299 169.23 1.08 0.68 + 301 169.23 1.08 0.68 + 303 182.81 1.17 0.78 + 305 110.97 0.31 0.04 + 307 293.78 0.83 0.26 + 309 119.09 0.76 0.35 + 311 122.26 0.35 0.05 + 313 250.97 0.71 0.20 + 315 -1046.26 0.74 0.09 + 317 109.55 0.70 0.30 + 319 -49.91 0.14 0.01 + 321 5944.03 2.70 0.68 + 323 -153.12 0.43 0.08 + 325 127.49 0.81 0.40 + 329 7991.76 3.63 1.20 + 330 7991.76 3.63 1.21 + 333 7991.76 3.63 1.19 + 10 0.00 0.00 0.00 Pump + 335 0.00 0.00 0.00 Pump + + + Node Results at 18:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure % from + Node gpm ft psi Lake + -------------------------------------------------------- + 10 0.00 151.71 2.04 100.00 + 15 0.00 158.35 54.75 2.47 + 20 0.00 160.55 13.67 0.00 + 35 1616.00 150.48 59.79 3.40 + 40 0.00 151.07 8.30 7.87 + 50 0.00 144.24 12.02 72.03 + 60 0.00 215.60 93.42 0.00 + 601 0.00 215.60 93.42 0.00 + 61 0.00 215.60 93.42 0.00 + 101 121.57 151.71 47.54 0.00 + 103 85.25 151.68 47.09 100.00 + 105 86.64 152.37 53.67 0.00 + 107 34.97 152.35 56.48 100.00 + 109 148.10 151.65 56.91 37.22 + 111 90.84 151.68 61.39 3.87 + 113 12.81 151.61 64.83 41.58 + 115 33.34 152.28 59.92 3.87 + 117 75.33 154.04 60.85 0.00 + 119 112.72 157.87 67.54 0.00 + 120 0.00 156.64 67.87 0.00 + 121 26.64 159.71 70.07 0.00 + 123 1833.00 160.82 64.92 0.00 + 125 29.18 159.81 64.48 0.00 + 127 11.30 160.23 45.17 0.00 + 129 0.00 160.23 47.33 0.00 + 131 27.36 160.23 66.83 0.00 + 139 3.77 159.13 55.52 0.00 + 141 6.30 158.35 66.88 0.00 + 143 3.97 158.35 70.56 2.25 + 145 17.68 158.19 68.11 5.25 + 147 5.47 158.12 60.50 6.16 + 149 17.32 158.09 61.57 6.16 + 151 92.47 157.95 53.92 2.24 + 153 28.27 157.99 39.77 0.00 + 157 33.15 156.35 62.07 0.00 + 159 26.44 154.25 64.24 0.00 + 161 10.11 152.82 64.48 0.00 + 163 6.03 152.52 63.92 0.00 + 164 0.00 152.52 63.92 9.14 + 166 1.66 152.52 66.96 0.00 + 167 9.32 151.32 67.73 0.00 + 169 0.00 151.32 67.73 0.00 + 171 25.18 150.58 66.98 0.00 + 173 0.00 150.56 66.97 0.00 + 177 37.23 150.51 61.75 9.54 + 179 0.00 150.53 61.76 9.52 + 181 0.00 150.49 61.74 3.40 + 183 0.00 150.54 60.46 20.48 + 184 0.00 149.50 57.85 39.90 + 185 16.42 150.08 58.10 40.39 + 187 0.00 150.84 59.94 48.55 + 189 69.07 150.98 63.69 12.44 + 191 52.42 151.34 54.74 53.16 + 193 45.64 151.38 57.79 18.53 + 195 0.00 151.41 58.89 0.00 + 197 10.91 151.43 55.65 98.67 + 199 76.36 145.87 64.07 0.00 + 201 28.55 145.17 62.86 1.29 + 203 4439.00 145.00 61.96 1.30 + 204 0.00 150.57 56.14 49.16 + 205 41.83 145.87 54.11 50.76 + 206 0.00 145.06 62.42 65.46 + 207 44.41 145.21 59.02 57.00 + 208 0.00 144.88 55.84 67.16 + 209 0.56 144.58 63.51 66.84 + 211 5.55 144.48 59.57 99.25 + 213 8.92 144.45 59.56 98.64 + 215 59.00 144.40 59.54 77.53 + 217 15.50 144.40 59.97 76.95 + 219 26.44 144.40 60.83 0.00 + 225 14.59 144.40 59.10 0.00 + 229 41.08 144.30 57.98 77.55 + 231 10.55 144.30 60.36 0.00 + 237 9.99 144.30 56.46 95.82 + 239 28.55 144.27 56.88 95.82 + 241 0.00 144.26 56.88 95.81 + 243 2.78 144.26 56.44 0.00 + 247 45.04 144.26 54.71 77.77 + 249 0.00 144.26 54.71 77.73 + 251 15.46 144.25 49.50 77.05 + 253 34.89 144.22 46.89 77.11 + 255 25.85 144.24 50.80 76.92 + 257 0.00 155.04 59.81 0.00 + 259 0.00 154.82 56.25 0.00 + 261 0.00 154.01 66.73 0.00 + 263 0.00 153.93 66.70 0.00 + 265 0.00 151.71 65.74 0.00 + 267 0.00 151.27 56.45 24.71 + 269 0.00 151.08 65.46 0.00 + 271 0.00 150.52 62.62 0.00 + 273 0.00 145.86 59.73 44.97 + 275 0.00 145.19 58.58 41.88 + River -8021.97 220.00 0.00 0.00 Reservoir + Lake 0.00 167.00 0.00 100.00 Reservoir + 1 -392.42 151.07 8.30 7.87 Tank + 2 -5.83 144.24 12.02 0.00 Tank + 3 -1418.57 160.55 13.67 0.00 Tank + + + Link Results at 18:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 20 1418.57 0.06 0.00 + 40 392.42 0.02 0.00 + 50 5.83 0.00 0.00 + 60 8021.97 5.69 3.57 + 101 0.00 0.00 0.00 + 103 171.10 0.27 0.02 + 105 -292.67 0.83 0.26 + 107 68.07 0.19 0.02 + 109 85.85 0.14 0.01 + 111 -62.24 0.18 0.01 + 112 423.79 1.20 0.52 + 113 106.69 0.30 0.04 + 114 115.16 0.74 0.33 + 115 33.10 0.21 0.03 + 116 209.04 0.59 0.14 + 117 447.38 1.27 0.57 + 119 -539.19 1.53 0.81 + 120 802.48 2.28 1.69 + 121 722.35 2.05 1.39 + 122 259.43 1.66 1.50 + 123 6999.36 3.18 0.92 + 125 6188.97 2.81 0.73 + 129 -1096.46 0.78 0.10 + 131 -1252.57 0.89 0.13 + 133 1418.57 1.45 0.40 + 135 154.70 0.11 0.00 + 137 27.36 0.04 0.00 + 145 127.34 0.81 0.40 + 147 123.57 0.79 0.38 + 149 -3.97 0.03 0.00 + 151 -0.00 0.00 0.00 + 153 -113.30 0.32 0.04 + 155 -95.62 0.27 0.03 + 159 90.14 0.26 0.03 + 161 72.82 0.46 0.14 + 163 -98.66 0.28 0.03 + 169 126.93 0.81 0.40 + 171 -79.01 0.22 0.02 + 173 6163.17 2.80 0.73 + 175 6130.02 2.78 0.72 + 177 6103.58 2.77 0.72 + 179 5933.90 2.69 0.68 + 180 1.66 0.00 0.00 + 181 1.66 0.00 0.00 + 183 5821.45 2.64 0.66 + 185 -9.32 0.06 0.00 + 186 355.14 2.27 2.69 + 187 5455.54 2.48 0.58 + 189 4431.86 2.01 0.40 + 191 -998.50 0.71 0.09 + 193 -1616.00 1.15 0.21 + 195 -484.46 1.37 0.66 + 197 -521.69 1.48 0.76 + 199 -129.27 0.37 0.06 + 201 392.42 1.11 0.45 + 202 537.22 3.43 5.78 + 203 198.49 1.27 0.91 + 204 537.22 1.52 0.80 + 205 355.14 1.01 0.37 + 207 327.77 0.93 0.32 + 209 103.92 0.66 0.28 + 211 356.59 1.01 0.38 + 213 251.22 0.71 0.20 + 215 277.20 0.79 0.24 + 217 -150.52 0.43 0.08 + 219 -159.57 0.45 0.08 + 221 159.57 1.02 0.61 + 223 153.11 0.43 0.08 + 225 164.02 0.47 0.09 + 229 4431.86 3.14 1.17 + 231 4304.52 3.05 1.11 + 233 4439.00 3.15 1.37 + 235 50.98 0.14 0.01 + 237 437.79 1.24 0.55 + 238 338.92 0.96 0.34 + 239 -54.46 0.15 0.01 + 240 338.92 0.96 0.34 + 241 338.92 0.96 0.34 + 243 338.37 0.54 0.08 + 245 173.59 0.28 0.02 + 247 115.54 0.18 0.01 + 249 56.54 0.09 0.00 + 251 26.44 0.06 0.00 + 257 14.59 0.04 0.00 + 261 49.13 0.31 0.07 + 263 10.55 0.03 0.00 + 269 159.23 0.45 0.08 + 271 2.49 0.02 0.00 + 273 146.74 0.42 0.07 + 275 45.99 0.13 0.01 + 277 2.78 0.01 0.00 + 281 43.21 0.18 0.02 + 283 72.20 0.20 0.02 + 285 -38.56 0.11 0.01 + 287 36.73 0.15 0.01 + 289 5.83 0.02 0.00 + 291 34.89 0.14 0.01 + 293 -18.18 0.12 0.01 + 295 33.65 0.10 0.00 + 297 339.55 2.17 2.47 + 299 163.25 1.04 0.64 + 301 163.25 1.04 0.64 + 303 176.30 1.13 0.73 + 305 107.83 0.31 0.04 + 307 284.13 0.81 0.25 + 309 104.75 0.67 0.28 + 311 172.45 0.49 0.10 + 313 223.56 0.63 0.16 + 315 -1131.54 0.80 0.11 + 317 108.58 0.69 0.30 + 319 -57.59 0.16 0.01 + 321 5926.20 2.69 0.68 + 323 -163.03 0.46 0.09 + 325 133.03 0.85 0.44 + 329 8021.97 3.64 1.20 + 330 8021.97 3.64 1.21 + 333 8021.97 3.64 1.21 + 10 0.00 0.00 0.00 Pump + 335 0.00 0.00 0.00 Pump + + + Node Results at 19:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure % from + Node gpm ft psi Lake + -------------------------------------------------------- + 10 0.00 151.24 1.84 100.00 + 15 0.00 157.86 54.54 2.47 + 20 0.00 160.01 13.44 0.00 + 35 1647.00 150.01 59.58 2.73 + 40 0.00 150.51 8.06 7.87 + 50 0.00 144.21 12.01 71.74 + 60 0.00 215.57 93.41 0.00 + 601 0.00 215.57 93.41 0.00 + 61 0.00 215.57 93.41 0.00 + 101 121.57 151.24 47.33 0.00 + 103 85.25 151.21 46.89 0.03 + 105 86.64 151.90 53.47 0.00 + 107 34.97 151.87 56.27 0.04 + 109 148.10 151.18 56.71 59.08 + 111 90.84 151.21 61.19 5.78 + 113 12.81 151.14 64.62 4.27 + 115 33.34 151.81 59.71 5.78 + 117 75.33 153.57 60.65 0.00 + 119 112.72 157.39 67.33 0.00 + 120 0.00 156.16 67.66 0.00 + 121 26.64 159.24 69.86 0.00 + 123 1817.00 160.36 64.72 0.00 + 125 29.18 159.32 64.27 0.00 + 127 11.30 159.72 44.94 0.00 + 129 0.00 159.72 47.11 0.00 + 131 27.36 159.71 66.60 0.00 + 139 3.77 158.63 55.30 0.00 + 141 6.30 157.86 66.67 0.00 + 143 3.97 157.86 70.35 2.25 + 145 17.68 157.71 67.90 0.83 + 147 5.47 157.64 60.29 5.52 + 149 17.32 157.61 61.36 6.14 + 151 92.47 157.47 53.72 2.61 + 153 28.27 157.51 39.56 0.00 + 157 33.15 155.88 61.86 0.00 + 159 26.44 153.78 64.03 0.00 + 161 10.11 152.34 64.28 0.00 + 163 6.03 152.05 63.72 0.00 + 164 0.00 152.05 63.72 9.22 + 166 1.66 152.05 66.75 0.00 + 167 9.32 150.85 67.53 0.00 + 169 0.00 150.85 67.53 0.00 + 171 25.18 150.11 66.78 0.00 + 173 0.00 150.10 66.77 0.00 + 177 37.23 150.03 61.54 9.13 + 179 0.00 150.05 61.55 9.41 + 181 0.00 150.01 61.53 2.72 + 183 0.00 150.06 60.26 17.14 + 184 0.00 149.06 57.65 39.39 + 185 16.42 149.62 57.90 40.41 + 187 0.00 150.37 59.74 41.46 + 189 69.07 150.51 63.48 5.82 + 191 52.42 150.87 54.54 12.32 + 193 45.64 150.91 57.59 28.41 + 195 0.00 150.94 58.69 0.00 + 197 10.91 150.96 55.45 2.63 + 199 76.36 145.51 63.92 0.00 + 201 28.55 144.82 62.71 1.23 + 203 4419.00 144.66 61.81 1.25 + 204 0.00 150.11 55.94 46.75 + 205 41.83 145.51 53.95 41.23 + 206 0.00 144.78 62.30 47.96 + 207 44.41 144.90 58.88 41.38 + 208 0.00 144.64 55.74 52.21 + 209 0.56 144.41 63.44 51.75 + 211 5.55 144.33 59.51 65.61 + 213 8.92 144.31 59.50 98.99 + 215 59.00 144.26 59.47 77.24 + 217 15.50 144.25 59.91 76.71 + 219 26.44 144.25 60.77 0.00 + 225 14.59 144.25 59.04 0.00 + 229 41.08 144.21 57.94 94.76 + 231 10.55 144.21 60.32 40.26 + 237 9.99 144.22 56.42 100.00 + 239 28.55 144.20 56.85 100.00 + 241 0.00 144.20 56.85 99.97 + 243 2.78 144.20 56.41 0.00 + 247 45.04 144.19 54.68 95.82 + 249 0.00 144.19 54.68 95.82 + 251 15.46 144.19 49.48 77.64 + 253 34.89 144.18 46.87 77.33 + 255 25.85 144.19 50.78 77.64 + 257 0.00 154.57 59.61 0.00 + 259 0.00 154.35 56.05 0.00 + 261 0.00 153.54 66.53 0.00 + 263 0.00 153.45 66.49 0.00 + 265 0.00 151.24 65.53 0.00 + 267 0.00 150.80 56.24 14.40 + 269 0.00 150.61 65.26 0.00 + 271 0.00 150.04 62.41 0.00 + 273 0.00 145.50 59.58 27.36 + 275 0.00 144.86 58.43 33.09 + River -8055.62 220.00 0.00 0.00 Reservoir + Lake 0.00 167.00 0.00 100.00 Reservoir + 1 -362.50 150.51 8.06 7.87 Tank + 2 -49.93 144.21 12.01 0.00 Tank + 3 -1365.73 160.01 13.44 0.00 Tank + + + Link Results at 19:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 20 1365.73 0.06 0.00 + 40 362.50 0.02 0.00 + 50 49.93 0.00 0.00 + 60 8055.62 5.71 3.60 + 101 0.00 0.00 0.00 + 103 170.99 0.27 0.02 + 105 -292.56 0.83 0.26 + 107 68.03 0.19 0.02 + 109 85.74 0.14 0.01 + 111 -62.35 0.18 0.01 + 112 423.61 1.20 0.52 + 113 106.58 0.30 0.04 + 114 115.10 0.73 0.33 + 115 33.06 0.21 0.03 + 116 208.87 0.59 0.14 + 117 447.23 1.27 0.57 + 119 -538.99 1.53 0.81 + 120 802.20 2.28 1.69 + 121 722.11 2.05 1.39 + 122 259.35 1.66 1.50 + 123 6997.56 3.18 0.92 + 125 6238.62 2.83 0.75 + 129 -1044.94 0.74 0.09 + 131 -1200.81 0.85 0.12 + 133 1365.73 1.39 0.37 + 135 153.62 0.11 0.00 + 137 27.36 0.04 0.00 + 145 126.26 0.81 0.40 + 147 122.49 0.78 0.37 + 149 -3.97 0.03 0.00 + 151 -0.00 0.00 0.00 + 153 -112.22 0.32 0.04 + 155 -94.54 0.27 0.03 + 159 89.07 0.25 0.03 + 161 71.74 0.46 0.14 + 163 -98.42 0.28 0.03 + 169 126.69 0.81 0.40 + 171 -77.69 0.22 0.02 + 173 6160.33 2.80 0.73 + 175 6127.19 2.78 0.72 + 177 6100.74 2.77 0.72 + 179 5931.08 2.69 0.68 + 180 1.66 0.00 0.00 + 181 1.66 0.00 0.00 + 183 5818.49 2.64 0.66 + 185 -9.32 0.06 0.00 + 186 352.74 2.25 2.65 + 187 5451.23 2.47 0.58 + 189 4375.07 1.99 0.39 + 191 -1050.98 0.75 0.09 + 193 -1647.00 1.17 0.22 + 195 -462.50 1.31 0.61 + 197 -499.73 1.42 0.70 + 199 -137.23 0.39 0.06 + 201 362.50 1.03 0.39 + 202 529.91 3.38 5.63 + 203 193.58 1.24 0.87 + 204 529.91 1.50 0.78 + 205 352.74 1.00 0.37 + 207 330.81 0.94 0.33 + 209 102.22 0.65 0.27 + 211 357.94 1.02 0.38 + 213 250.52 0.71 0.20 + 215 277.67 0.79 0.24 + 217 -150.01 0.43 0.08 + 219 -159.56 0.45 0.08 + 221 159.56 1.02 0.61 + 223 152.93 0.43 0.08 + 225 163.83 0.46 0.09 + 229 4375.07 3.10 1.15 + 231 4256.69 3.02 1.09 + 233 4419.00 3.13 1.36 + 235 42.02 0.12 0.01 + 237 423.39 1.20 0.52 + 238 294.82 0.84 0.26 + 239 -84.15 0.24 0.03 + 240 294.82 0.84 0.26 + 241 294.82 0.84 0.26 + 243 294.27 0.47 0.06 + 245 162.98 0.26 0.02 + 247 115.54 0.18 0.01 + 249 56.54 0.09 0.00 + 251 26.44 0.06 0.00 + 257 14.59 0.04 0.00 + 261 38.51 0.25 0.04 + 263 10.55 0.03 0.00 + 269 125.74 0.36 0.05 + 271 13.11 0.08 0.01 + 273 102.64 0.29 0.04 + 275 29.48 0.08 0.00 + 277 2.78 0.01 0.00 + 281 26.70 0.11 0.01 + 283 44.61 0.13 0.01 + 285 -28.37 0.08 0.00 + 287 10.03 0.04 0.00 + 289 49.93 0.20 0.02 + 291 34.89 0.14 0.01 + 293 -0.78 0.01 0.00 + 295 16.25 0.05 0.00 + 297 339.44 2.17 2.47 + 299 163.19 1.04 0.64 + 301 163.19 1.04 0.64 + 303 176.25 1.12 0.73 + 305 107.79 0.31 0.04 + 307 284.04 0.81 0.25 + 309 104.90 0.67 0.28 + 311 172.78 0.49 0.10 + 313 224.43 0.64 0.16 + 315 -1184.49 0.84 0.12 + 317 106.71 0.68 0.29 + 319 -64.69 0.18 0.02 + 321 5923.38 2.69 0.68 + 323 -190.86 0.54 0.12 + 325 133.51 0.85 0.44 + 329 8055.62 3.66 1.21 + 330 8055.62 3.66 1.21 + 333 8055.62 3.66 1.22 + 10 0.00 0.00 0.00 Pump + 335 0.00 0.00 0.00 Pump + + + Node Results at 20:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure % from + Node gpm ft psi Lake + -------------------------------------------------------- + 10 0.00 149.54 1.10 100.00 + 15 0.00 156.93 54.13 2.47 + 20 0.00 159.49 13.21 0.00 + 35 1627.00 149.08 59.18 2.20 + 40 0.00 150.00 7.84 7.87 + 50 0.00 144.01 11.92 69.28 + 60 0.00 215.51 93.38 0.00 + 601 0.00 215.51 93.38 0.00 + 61 0.00 215.51 93.38 0.00 + 101 161.46 149.54 46.60 0.00 + 103 113.22 149.52 46.15 0.00 + 105 115.06 150.28 52.77 0.00 + 107 46.44 150.26 55.58 0.00 + 109 196.69 149.51 55.99 59.46 + 111 120.65 149.69 60.53 5.78 + 113 17.01 149.68 63.99 5.78 + 115 44.28 150.26 59.04 5.81 + 117 100.05 152.16 60.04 0.08 + 119 149.71 156.52 66.95 0.11 + 120 0.00 155.11 67.21 0.08 + 121 35.39 158.47 69.53 0.00 + 123 1830.00 159.60 64.39 0.00 + 125 38.76 158.58 63.95 0.00 + 127 15.01 159.11 44.68 0.00 + 129 0.00 159.10 46.84 0.00 + 131 36.34 159.09 66.34 0.00 + 139 5.01 157.82 54.95 0.00 + 141 8.37 156.93 66.27 0.00 + 143 5.27 156.93 69.95 2.25 + 145 23.49 156.76 67.49 0.00 + 147 7.27 156.69 59.88 5.41 + 149 23.01 156.66 60.95 5.52 + 151 122.81 156.54 53.31 2.43 + 153 37.54 156.58 39.16 0.00 + 157 44.02 154.98 61.48 0.11 + 159 35.12 152.85 63.63 0.11 + 161 13.43 151.40 63.87 0.07 + 163 8.01 151.10 63.31 0.02 + 164 0.00 151.10 63.31 9.46 + 166 2.21 151.10 66.34 0.00 + 167 12.38 149.90 67.12 0.00 + 169 0.00 149.90 67.12 0.00 + 171 33.44 149.18 66.37 0.00 + 173 0.00 149.16 66.37 0.00 + 177 49.44 149.11 61.14 7.89 + 179 0.00 149.13 61.15 7.89 + 181 0.00 149.09 61.13 2.22 + 183 0.00 149.14 59.85 7.97 + 184 0.00 148.04 57.21 32.66 + 185 21.80 148.58 57.45 32.70 + 187 0.00 149.23 59.24 11.12 + 189 91.73 149.43 63.01 9.67 + 191 69.61 149.55 53.97 3.14 + 193 60.61 149.60 57.02 2.76 + 195 0.00 149.64 58.12 0.00 + 197 14.48 149.58 54.85 5.37 + 199 101.42 144.62 63.53 0.00 + 201 37.92 143.95 62.33 1.95 + 203 4368.00 143.79 61.44 2.01 + 204 0.00 149.00 55.46 17.34 + 205 55.56 144.62 53.57 41.20 + 206 0.00 143.96 61.94 38.16 + 207 58.98 144.05 58.52 38.52 + 208 0.00 143.86 55.40 36.56 + 209 0.74 143.69 63.13 38.99 + 211 7.37 143.64 59.20 52.00 + 213 11.85 143.61 59.19 66.49 + 215 78.36 143.52 59.16 77.93 + 217 20.59 143.52 59.59 76.87 + 219 35.12 143.51 60.45 0.00 + 225 19.38 143.51 58.72 0.00 + 229 54.55 143.57 57.66 91.57 + 231 14.01 143.57 60.04 58.74 + 237 13.27 143.61 56.16 67.12 + 239 37.92 143.61 56.59 91.69 + 241 0.00 143.61 56.59 97.31 + 243 3.69 143.61 56.16 0.00 + 247 59.82 143.62 54.43 99.80 + 249 0.00 143.62 54.43 100.00 + 251 20.54 143.62 49.23 77.68 + 253 46.34 143.67 46.65 77.02 + 255 34.33 143.70 50.56 73.35 + 257 0.00 153.30 59.06 0.08 + 259 0.00 153.04 55.48 0.08 + 261 0.00 152.13 65.92 0.05 + 263 0.00 152.03 65.87 0.06 + 265 0.00 150.29 65.12 0.00 + 267 0.00 149.58 55.71 1.06 + 269 0.00 149.59 64.82 0.00 + 271 0.00 149.12 62.01 0.00 + 273 0.00 144.62 59.20 24.74 + 275 0.00 143.99 58.06 44.58 + River -8110.61 220.00 0.00 0.00 Reservoir + Lake 0.00 167.00 0.00 100.00 Reservoir + 1 -509.25 150.00 7.84 7.87 Tank + 2 -209.61 144.01 11.92 0.00 Tank + 3 -1586.43 159.49 13.21 0.00 Tank + + + Link Results at 20:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 20 1586.43 0.07 0.00 + 40 509.25 0.02 0.00 + 50 209.61 0.01 0.00 + 60 8110.61 5.75 3.64 + 101 0.00 0.00 0.00 + 103 148.67 0.24 0.02 + 105 -310.13 0.88 0.29 + 107 51.51 0.15 0.01 + 109 35.45 0.06 0.00 + 111 -161.24 0.46 0.09 + 112 414.76 1.18 0.50 + 113 28.81 0.08 0.00 + 114 106.99 0.68 0.29 + 115 5.07 0.03 0.00 + 116 118.80 0.34 0.05 + 117 476.70 1.35 0.64 + 119 -560.97 1.59 0.87 + 120 865.65 2.46 1.94 + 121 774.02 2.20 1.58 + 122 272.08 1.74 1.64 + 123 7198.09 3.27 0.97 + 125 6280.61 2.85 0.76 + 129 -1224.95 0.87 0.13 + 131 -1397.28 0.99 0.16 + 133 1586.43 1.62 0.49 + 135 174.14 0.12 0.00 + 137 36.34 0.06 0.00 + 145 137.81 0.88 0.47 + 147 132.80 0.85 0.43 + 149 -5.27 0.03 0.00 + 151 -0.00 0.00 0.00 + 153 -119.16 0.34 0.05 + 155 -95.67 0.27 0.03 + 159 88.40 0.25 0.03 + 161 65.39 0.42 0.12 + 163 -96.03 0.27 0.03 + 169 133.57 0.85 0.44 + 171 -38.62 0.11 0.01 + 173 6221.34 2.82 0.74 + 175 6177.32 2.80 0.73 + 177 6142.20 2.79 0.72 + 179 5948.43 2.70 0.68 + 180 2.21 0.00 0.00 + 181 2.21 0.00 0.00 + 183 5803.44 2.63 0.65 + 185 -12.38 0.08 0.01 + 186 323.95 2.07 2.26 + 187 5382.72 2.44 0.57 + 189 4350.45 1.97 0.38 + 191 -998.83 0.71 0.09 + 193 -1627.00 1.15 0.21 + 195 -506.73 1.44 0.72 + 197 -556.17 1.58 0.86 + 199 -46.92 0.13 0.01 + 201 509.25 1.44 0.73 + 202 519.69 3.32 5.44 + 203 217.54 1.39 1.08 + 204 519.69 1.47 0.75 + 205 323.95 0.92 0.31 + 207 264.46 0.75 0.22 + 209 127.44 0.81 0.40 + 211 408.34 1.16 0.48 + 213 196.51 0.56 0.12 + 215 196.74 0.56 0.12 + 217 -176.55 0.50 0.10 + 219 -180.34 0.51 0.11 + 221 180.34 1.15 0.77 + 223 89.58 0.25 0.03 + 225 104.06 0.30 0.04 + 229 4350.45 3.09 1.13 + 231 4200.44 2.98 1.06 + 233 4368.00 3.10 1.33 + 235 48.59 0.14 0.01 + 237 408.13 1.16 0.48 + 238 248.27 0.70 0.19 + 239 -100.88 0.29 0.04 + 240 248.27 0.70 0.19 + 241 248.27 0.70 0.19 + 243 247.53 0.39 0.05 + 245 188.98 0.30 0.03 + 247 153.45 0.24 0.02 + 249 75.09 0.12 0.01 + 251 35.12 0.07 0.00 + 257 19.38 0.05 0.00 + 261 23.68 0.15 0.02 + 263 14.01 0.04 0.00 + 269 51.18 0.15 0.01 + 271 44.88 0.29 0.06 + 273 -6.97 0.02 0.00 + 275 -14.54 0.04 0.00 + 277 3.69 0.01 0.00 + 281 -18.23 0.07 0.00 + 283 -30.34 0.09 0.00 + 285 1.99 0.01 0.00 + 287 -80.05 0.33 0.06 + 289 209.61 0.86 0.34 + 291 46.34 0.19 0.02 + 293 48.88 0.31 0.07 + 295 -28.35 0.08 0.00 + 297 363.71 2.32 2.81 + 299 174.80 1.12 0.72 + 301 174.80 1.12 0.72 + 303 188.91 1.21 0.83 + 305 112.99 0.32 0.04 + 307 301.90 0.86 0.28 + 309 134.78 0.86 0.45 + 311 61.97 0.18 0.01 + 313 286.89 0.81 0.25 + 315 -1120.27 0.79 0.11 + 317 104.60 0.67 0.28 + 319 -56.00 0.16 0.01 + 321 5938.22 2.70 0.68 + 323 -205.48 0.58 0.14 + 325 121.45 0.78 0.37 + 329 8110.61 3.68 1.23 + 330 8110.61 3.68 1.22 + 333 8110.61 3.68 1.24 + 10 0.00 0.00 0.00 Pump + 335 0.00 0.00 0.00 Pump + + + Node Results at 21:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure % from + Node gpm ft psi Lake + -------------------------------------------------------- + 10 0.00 148.17 0.51 100.00 + 15 0.00 156.06 53.75 2.47 + 20 0.00 158.89 12.95 0.00 + 35 1627.00 148.04 58.73 2.53 + 40 0.00 149.28 7.53 7.87 + 50 0.00 143.15 11.55 50.80 + 60 0.00 215.46 93.36 0.00 + 601 0.00 215.46 93.36 0.00 + 61 0.00 215.46 93.36 0.00 + 101 182.35 148.17 46.01 0.00 + 103 127.87 148.15 45.56 0.00 + 105 129.96 148.98 52.20 0.08 + 107 52.45 148.97 55.02 0.00 + 109 222.14 148.15 55.40 22.76 + 111 136.26 148.43 59.98 0.96 + 113 19.21 148.43 63.45 1.99 + 115 50.02 148.98 58.49 0.93 + 117 113.00 150.97 59.52 0.04 + 119 169.08 155.66 66.58 0.05 + 120 0.00 154.13 66.79 0.04 + 121 39.96 157.69 69.19 0.00 + 123 1814.00 158.85 64.06 0.00 + 125 43.78 157.83 63.62 0.00 + 127 16.95 158.44 44.39 0.00 + 129 0.00 158.43 46.55 0.00 + 131 41.04 158.42 66.05 0.00 + 139 5.65 157.03 54.61 0.00 + 141 9.46 156.06 65.89 0.00 + 143 5.95 156.06 69.57 2.25 + 145 26.52 155.88 67.11 0.00 + 147 8.21 155.80 59.49 5.24 + 149 25.99 155.78 60.56 5.39 + 151 138.70 155.66 52.93 2.24 + 153 42.40 155.70 38.78 0.00 + 157 49.72 154.07 61.08 0.05 + 159 39.67 151.88 63.21 0.05 + 161 15.17 150.39 63.43 0.05 + 163 9.04 150.09 62.87 0.05 + 164 0.00 150.09 62.87 8.58 + 166 2.50 150.09 65.90 0.00 + 167 13.98 148.86 66.67 0.10 + 169 0.00 148.86 66.67 0.09 + 171 37.77 148.13 65.92 0.10 + 173 0.00 148.11 65.91 0.11 + 177 55.84 148.07 60.69 8.01 + 179 0.00 148.10 60.70 7.86 + 181 0.00 148.04 60.68 2.55 + 183 0.00 148.10 59.40 1.20 + 184 0.00 146.92 56.73 9.19 + 185 24.62 147.47 56.97 7.67 + 187 0.00 148.08 58.75 1.57 + 189 103.60 148.31 62.53 0.30 + 191 78.62 148.34 53.44 2.60 + 193 68.46 148.40 56.50 2.11 + 195 0.00 148.44 57.60 0.10 + 197 16.36 148.36 54.32 5.78 + 199 114.55 143.46 63.03 0.10 + 201 42.83 142.78 61.83 1.57 + 203 4399.00 142.62 60.93 1.55 + 204 0.00 147.86 54.97 1.50 + 205 62.75 143.46 53.06 31.32 + 206 0.00 142.79 61.44 30.45 + 207 66.61 142.88 58.01 31.56 + 208 0.00 142.70 54.90 32.10 + 209 0.84 142.54 62.63 40.89 + 211 8.32 142.48 58.70 36.71 + 213 13.38 142.45 58.69 50.91 + 215 88.50 142.34 58.64 77.93 + 217 23.25 142.33 59.07 77.47 + 219 39.67 142.33 59.94 7.64 + 225 21.89 142.33 58.20 2.67 + 229 61.61 142.41 57.16 78.35 + 231 15.82 142.41 59.54 70.87 + 237 14.99 142.47 55.67 66.99 + 239 42.83 142.48 56.10 100.00 + 241 0.00 142.48 56.10 100.00 + 243 4.17 142.48 55.67 0.00 + 247 67.56 142.48 53.94 77.94 + 249 0.00 142.48 53.94 94.96 + 251 23.19 142.50 48.74 76.39 + 253 52.34 142.61 46.19 76.78 + 255 38.77 142.64 50.11 59.00 + 257 0.00 152.19 58.58 0.04 + 259 0.00 151.92 55.00 0.04 + 261 0.00 150.94 65.40 0.04 + 263 0.00 150.84 65.36 0.05 + 265 0.00 149.25 64.67 0.07 + 267 0.00 148.40 55.20 0.83 + 269 0.00 148.51 64.35 0.10 + 271 0.00 148.07 61.56 0.11 + 273 0.00 143.46 58.69 21.49 + 275 0.00 142.83 57.55 28.87 + River -8165.07 220.00 0.00 0.00 Reservoir + Lake 0.00 167.00 0.00 100.00 Reservoir + 1 -603.46 149.28 7.53 7.87 Tank + 2 -273.65 143.15 11.55 0.00 Tank + 3 -1724.01 158.89 12.95 0.00 Tank + + + Link Results at 21:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 20 1724.01 0.07 0.00 + 40 603.46 0.03 0.00 + 50 273.65 0.01 0.00 + 60 8165.07 5.79 3.69 + 101 0.00 0.00 0.00 + 103 142.49 0.23 0.02 + 105 -324.85 0.92 0.32 + 107 37.82 0.11 0.01 + 109 14.62 0.02 0.00 + 111 -207.52 0.59 0.14 + 112 406.96 1.15 0.48 + 113 -18.28 0.05 0.00 + 114 104.15 0.66 0.28 + 115 -14.63 0.09 0.01 + 116 66.66 0.19 0.02 + 117 492.62 1.40 0.68 + 119 -575.76 1.63 0.91 + 120 900.87 2.56 2.09 + 121 803.79 2.28 1.69 + 122 280.51 1.79 1.73 + 123 7370.05 3.35 1.02 + 125 6351.07 2.88 0.77 + 129 -1339.45 0.95 0.15 + 131 -1521.18 1.08 0.19 + 133 1724.01 1.76 0.58 + 135 185.87 0.13 0.00 + 137 41.04 0.07 0.00 + 145 144.83 0.92 0.51 + 147 139.17 0.89 0.47 + 149 -5.95 0.04 0.00 + 151 0.00 0.00 0.00 + 153 -123.77 0.35 0.05 + 155 -97.24 0.28 0.03 + 159 89.03 0.25 0.03 + 161 63.05 0.40 0.11 + 163 -95.55 0.27 0.03 + 169 137.96 0.88 0.47 + 171 -19.90 0.06 0.00 + 173 6319.99 2.87 0.76 + 175 6270.27 2.85 0.75 + 177 6230.60 2.83 0.74 + 179 6025.08 2.73 0.70 + 180 2.50 0.01 0.00 + 181 2.50 0.01 0.00 + 183 5864.37 2.66 0.67 + 185 -13.98 0.09 0.01 + 186 314.76 2.01 2.15 + 187 5414.87 2.46 0.57 + 189 4406.20 2.00 0.39 + 191 -970.91 0.69 0.08 + 193 -1627.00 1.15 0.21 + 195 -539.55 1.53 0.81 + 197 -595.39 1.69 0.97 + 199 8.07 0.02 0.00 + 201 603.46 1.71 0.99 + 202 523.02 3.34 5.50 + 203 232.88 1.49 1.23 + 204 523.02 1.48 0.76 + 205 314.76 0.89 0.30 + 207 224.81 0.64 0.16 + 209 138.01 0.88 0.47 + 211 435.52 1.24 0.54 + 213 176.75 0.50 0.10 + 215 147.45 0.42 0.07 + 217 -190.28 0.54 0.12 + 219 -190.36 0.54 0.12 + 221 190.36 1.21 0.85 + 223 65.10 0.18 0.02 + 225 81.45 0.23 0.02 + 229 4406.20 3.12 1.16 + 231 4235.82 3.00 1.08 + 233 4399.00 3.12 1.35 + 235 55.83 0.16 0.01 + 237 410.76 1.17 0.49 + 238 243.49 0.69 0.19 + 239 -100.66 0.29 0.04 + 240 243.49 0.69 0.19 + 241 243.49 0.69 0.19 + 243 242.65 0.39 0.05 + 245 209.69 0.33 0.03 + 247 173.31 0.28 0.02 + 249 84.81 0.14 0.01 + 251 39.67 0.08 0.00 + 257 21.89 0.06 0.00 + 261 23.00 0.15 0.02 + 263 15.82 0.04 0.00 + 269 24.64 0.07 0.00 + 271 54.44 0.35 0.08 + 273 -44.78 0.13 0.01 + 275 -30.25 0.09 0.00 + 277 4.17 0.01 0.00 + 281 -34.41 0.14 0.01 + 283 -57.36 0.16 0.01 + 285 11.84 0.03 0.00 + 287 -113.82 0.46 0.11 + 289 273.65 1.12 0.56 + 291 52.34 0.21 0.03 + 293 68.71 0.44 0.13 + 295 -45.52 0.13 0.01 + 297 377.59 2.41 3.01 + 299 181.42 1.16 0.77 + 301 181.42 1.16 0.77 + 303 196.17 1.25 0.89 + 305 115.03 0.33 0.05 + 307 311.20 0.88 0.29 + 309 149.17 0.95 0.54 + 311 -1.72 0.00 0.00 + 313 318.97 0.90 0.31 + 315 -1087.45 0.77 0.10 + 317 105.35 0.67 0.28 + 319 -49.51 0.14 0.01 + 321 6013.54 2.73 0.70 + 323 -206.01 0.58 0.14 + 325 116.54 0.74 0.34 + 329 8165.07 3.71 1.24 + 330 8165.07 3.71 1.25 + 333 8165.07 3.71 1.24 + 10 0.00 0.00 0.00 Pump + 335 0.00 0.00 0.00 Pump + + + Node Results at 22:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure % from + Node gpm ft psi Lake + -------------------------------------------------------- + 10 0.00 148.31 0.57 100.00 + 15 360.00 146.42 49.58 2.47 + 20 0.00 159.27 13.12 0.00 + 35 1671.00 148.70 59.02 0.05 + 40 0.00 149.20 7.49 4.11 + 50 0.00 142.43 11.24 16.76 + 60 0.00 208.96 90.54 0.00 + 601 0.00 302.11 130.90 0.00 + 61 0.00 302.11 130.90 0.00 + 101 235.54 148.31 46.07 0.04 + 103 165.17 148.29 45.62 0.00 + 105 167.86 149.40 52.39 0.04 + 107 67.75 149.40 55.20 0.00 + 109 286.94 148.29 55.46 21.06 + 111 176.01 148.85 60.16 0.02 + 113 24.81 148.90 63.65 1.49 + 115 64.60 149.50 58.71 1.44 + 117 145.96 152.05 59.99 0.00 + 119 218.40 158.29 67.72 0.00 + 120 0.00 156.27 67.71 0.00 + 121 51.62 161.08 70.66 0.00 + 123 1840.00 164.47 66.50 0.00 + 125 56.54 160.73 64.88 0.00 + 127 21.90 159.72 44.94 0.00 + 129 0.00 159.71 47.11 0.00 + 131 53.01 159.69 66.60 0.00 + 139 7.30 156.92 54.56 0.00 + 141 12.21 154.98 65.42 0.00 + 143 7.69 150.97 67.36 2.25 + 145 34.26 155.34 66.87 0.00 + 147 10.60 155.65 59.43 0.00 + 149 33.57 155.79 60.57 5.24 + 151 179.16 157.28 53.63 0.84 + 153 54.77 157.34 39.49 0.00 + 157 64.22 156.29 62.04 0.00 + 159 51.24 153.53 63.93 0.00 + 161 19.59 151.66 63.98 0.00 + 163 11.68 151.29 63.39 0.00 + 164 0.00 151.29 63.39 8.76 + 166 3.22 151.29 66.42 0.00 + 167 18.05 149.75 67.05 0.05 + 169 0.00 149.75 67.05 0.03 + 171 48.78 148.86 66.23 0.05 + 173 0.00 148.84 66.22 0.05 + 177 72.13 148.72 60.97 0.05 + 179 0.00 148.72 60.98 0.28 + 181 0.00 148.71 60.97 0.05 + 183 0.00 148.72 59.67 0.24 + 184 0.00 147.37 56.92 1.41 + 185 31.81 147.96 57.18 1.40 + 187 0.00 148.60 58.97 1.78 + 189 133.82 148.91 62.79 0.43 + 191 101.56 148.82 53.65 3.62 + 193 88.42 148.90 56.72 2.43 + 195 0.00 148.96 57.83 0.05 + 197 21.13 148.82 54.52 5.78 + 199 147.96 143.62 63.10 0.05 + 201 55.32 142.88 61.87 0.53 + 203 4470.00 142.71 60.97 0.53 + 204 0.00 148.38 55.19 1.67 + 205 81.05 143.60 53.12 3.44 + 206 0.00 142.68 61.39 10.46 + 207 86.04 142.88 58.01 10.41 + 208 0.00 142.44 54.79 10.82 + 209 1.08 142.04 62.41 17.18 + 211 10.75 141.90 58.45 30.51 + 213 17.29 141.84 58.43 37.25 + 215 114.32 141.67 58.35 78.28 + 217 30.03 141.66 58.78 77.85 + 219 51.24 141.65 59.64 66.32 + 225 28.27 141.65 57.91 63.63 + 229 79.58 141.74 56.87 80.11 + 231 20.44 141.74 59.25 73.77 + 237 19.36 141.82 55.38 51.52 + 239 55.32 141.82 55.82 60.56 + 241 0.00 141.82 55.82 66.26 + 243 5.38 141.82 55.38 0.00 + 247 87.27 141.82 53.65 80.06 + 249 0.00 141.82 53.65 95.57 + 251 29.96 141.82 48.45 64.37 + 253 67.60 141.88 45.88 64.27 + 255 50.08 141.93 49.80 27.98 + 257 0.00 153.68 59.22 0.00 + 259 0.00 153.32 55.60 0.00 + 261 0.00 152.01 65.87 0.01 + 263 0.00 151.88 65.81 0.00 + 265 0.00 150.24 65.10 0.03 + 267 0.00 148.95 55.44 0.74 + 269 0.00 149.23 64.66 0.05 + 271 0.00 148.75 61.85 0.05 + 273 0.00 143.60 58.75 8.07 + 275 0.00 142.89 57.58 19.38 + River -13191.47 220.00 0.00 0.00 Reservoir + Lake 0.00 167.00 0.00 100.00 Reservoir + 1 -367.08 149.20 7.49 7.87 Tank + 2 -270.43 142.43 11.24 0.00 Tank + 3 1708.32 159.27 13.12 0.00 Tank + + + Link Results at 22:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 20 -1708.32 0.07 0.00 + 40 367.08 0.02 0.00 + 50 270.43 0.01 0.00 + 60 13191.47 9.36 8.97 + 101 0.00 0.00 0.00 + 103 147.08 0.23 0.02 + 105 -382.62 1.09 0.43 + 107 24.49 0.07 0.00 + 109 -18.09 0.03 0.00 + 111 -305.03 0.87 0.28 + 112 442.39 1.25 0.56 + 113 -91.51 0.26 0.03 + 114 108.60 0.69 0.30 + 115 -43.26 0.28 0.05 + 116 -7.73 0.02 0.00 + 117 574.97 1.63 0.91 + 119 -658.85 1.87 1.17 + 120 1049.56 2.98 2.77 + 121 938.79 2.66 2.25 + 122 330.21 2.11 2.35 + 123 8742.49 3.97 1.39 + 125 11351.47 5.15 2.26 + 129 2227.15 1.58 0.38 + 131 1993.18 1.41 0.31 + 133 -1708.32 1.74 0.57 + 135 262.96 0.19 0.01 + 137 53.01 0.08 0.00 + 145 209.95 1.34 1.01 + 147 202.65 1.29 0.95 + 149 -367.69 2.35 2.86 + 151 -360.00 2.30 2.75 + 153 177.25 0.50 0.10 + 155 211.51 0.60 0.14 + 159 -222.12 0.63 0.16 + 161 -255.68 1.63 1.46 + 163 -122.65 0.35 0.05 + 169 177.42 1.13 0.74 + 171 312.19 0.89 0.29 + 173 7162.34 3.25 0.96 + 175 7098.12 3.22 0.95 + 177 7046.88 3.20 0.93 + 179 6800.09 3.09 0.88 + 180 3.22 0.01 0.00 + 181 3.22 0.01 0.00 + 183 6597.98 2.99 0.83 + 185 -18.05 0.12 0.01 + 186 321.75 2.05 2.24 + 187 6041.68 2.74 0.70 + 189 4689.91 2.13 0.44 + 191 -1303.00 0.92 0.14 + 193 -1671.00 1.19 0.22 + 195 -245.80 0.70 0.19 + 197 -317.93 0.90 0.30 + 199 49.14 0.14 0.01 + 201 367.08 1.04 0.40 + 202 547.99 3.50 6.00 + 203 258.05 1.65 1.49 + 204 547.99 1.55 0.83 + 205 321.75 0.91 0.31 + 207 208.91 0.59 0.14 + 209 161.43 1.03 0.62 + 211 538.24 1.53 0.80 + 213 160.32 0.45 0.09 + 215 88.12 0.25 0.03 + 217 -230.13 0.65 0.17 + 219 -227.20 0.64 0.16 + 221 227.20 1.45 1.17 + 223 31.74 0.09 0.00 + 225 52.87 0.15 0.01 + 229 4689.91 3.33 1.30 + 231 4440.10 3.15 1.18 + 233 4470.00 3.17 1.39 + 235 101.85 0.29 0.04 + 237 456.70 1.30 0.59 + 238 397.54 1.13 0.46 + 239 26.88 0.08 0.00 + 240 397.54 1.13 0.46 + 241 397.54 1.13 0.46 + 243 396.46 0.63 0.11 + 245 280.80 0.45 0.06 + 247 223.86 0.36 0.04 + 249 109.54 0.17 0.01 + 251 51.24 0.11 0.00 + 257 28.27 0.08 0.00 + 261 39.65 0.25 0.05 + 263 20.44 0.06 0.00 + 269 104.91 0.30 0.04 + 271 60.37 0.39 0.10 + 273 25.19 0.07 0.00 + 275 -7.98 0.02 0.00 + 277 5.38 0.02 0.00 + 281 -13.36 0.05 0.00 + 283 -22.15 0.06 0.00 + 285 -6.07 0.02 0.00 + 287 -94.56 0.39 0.08 + 289 270.43 1.10 0.55 + 291 67.60 0.28 0.04 + 293 58.18 0.37 0.09 + 295 -28.22 0.08 0.00 + 297 440.99 2.81 4.01 + 299 211.87 1.35 1.03 + 301 211.87 1.35 1.03 + 303 229.12 1.46 1.19 + 305 133.97 0.38 0.06 + 307 363.09 1.03 0.39 + 309 187.21 1.19 0.82 + 311 -99.09 0.28 0.04 + 313 416.04 1.18 0.50 + 315 -1425.20 1.01 0.17 + 317 112.10 0.72 0.32 + 319 -10.24 0.03 0.00 + 321 6785.18 3.08 0.87 + 323 -85.22 0.24 0.03 + 325 122.20 0.78 0.37 + 329 13191.47 5.99 3.02 + 330 0.00 0.00 0.00 + 333 -0.00 0.00 0.00 + 10 0.00 0.00 0.00 Pump + 335 13191.47 0.00 -93.15 Pump + + + Node Results at 23:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure % from + Node gpm ft psi Lake + -------------------------------------------------------- + 10 0.00 144.95 -0.89 100.00 + 15 360.00 145.26 49.08 0.00 + 20 0.00 159.92 13.40 0.00 + 35 1668.00 147.09 58.32 1.16 + 40 0.00 148.68 7.27 7.87 + 50 0.00 141.33 10.76 0.00 + 60 0.00 208.94 90.53 0.00 + 601 0.00 301.96 130.84 0.00 + 61 0.00 301.96 130.84 0.00 + 101 317.22 144.95 44.61 0.02 + 103 222.44 144.94 44.17 0.06 + 105 226.07 146.36 51.07 0.00 + 107 91.25 146.36 53.89 0.64 + 109 386.44 144.97 54.02 0.16 + 111 237.04 146.30 59.06 0.99 + 113 33.42 146.52 62.62 0.10 + 115 87.01 146.90 57.58 0.00 + 117 196.58 149.73 58.99 0.00 + 119 294.14 157.48 67.37 0.00 + 120 0.00 154.95 67.14 0.00 + 121 69.52 160.66 70.48 0.00 + 123 1859.00 164.05 66.32 0.00 + 125 76.15 160.49 64.77 0.00 + 127 29.49 160.06 45.09 0.00 + 129 0.00 160.05 47.25 0.00 + 131 71.39 160.02 66.74 0.00 + 139 9.84 156.39 54.33 0.00 + 141 16.45 153.87 64.94 0.00 + 143 10.35 149.80 66.86 0.00 + 145 46.14 154.14 66.36 0.00 + 147 14.28 154.43 58.90 1.85 + 149 45.21 154.55 60.04 1.89 + 151 241.28 156.08 53.12 3.98 + 153 73.76 156.15 38.98 0.00 + 157 86.49 155.28 61.61 0.00 + 159 69.00 152.27 63.38 0.00 + 161 26.39 150.24 63.36 0.00 + 163 15.73 149.83 62.75 0.00 + 164 0.00 149.83 62.75 9.91 + 166 4.34 149.83 65.79 0.00 + 167 24.32 148.18 66.37 0.00 + 169 0.00 148.18 66.37 0.00 + 171 65.70 147.24 65.53 0.00 + 173 0.00 147.21 65.52 0.00 + 177 97.14 147.10 60.27 7.87 + 179 0.00 147.11 60.28 7.87 + 181 0.00 147.09 60.27 1.16 + 183 0.00 147.05 58.95 1.66 + 184 0.00 145.36 56.05 1.42 + 185 42.84 145.96 56.31 1.56 + 187 0.00 146.45 58.04 1.71 + 189 180.23 147.05 61.98 0.18 + 191 136.77 146.48 52.64 1.23 + 193 119.09 146.64 55.74 0.59 + 195 0.00 146.71 56.85 0.00 + 197 28.46 146.41 53.47 5.78 + 199 199.26 141.62 62.23 0.00 + 201 74.50 140.85 60.99 0.15 + 203 4480.00 140.68 60.09 0.15 + 204 0.00 146.28 54.28 1.61 + 205 109.15 141.57 52.24 1.16 + 206 0.00 140.56 60.47 3.01 + 207 115.88 140.81 57.11 3.07 + 208 0.00 140.28 53.85 2.94 + 209 1.45 139.78 61.43 2.89 + 211 14.48 139.62 57.46 10.37 + 213 23.28 139.52 57.42 28.89 + 215 153.96 139.23 57.30 100.00 + 217 40.45 139.20 57.72 77.43 + 219 69.00 139.18 58.57 72.45 + 225 38.08 139.19 56.84 72.08 + 229 107.18 139.41 55.86 54.03 + 231 27.52 139.40 58.24 76.20 + 237 26.07 139.59 54.42 36.81 + 239 74.50 139.59 54.85 77.62 + 241 0.00 139.59 54.85 99.98 + 243 7.25 139.59 54.42 0.00 + 247 117.53 139.61 52.69 32.81 + 249 0.00 139.61 52.69 77.90 + 251 40.35 139.64 47.50 22.76 + 253 91.05 139.91 45.02 30.35 + 255 67.45 139.99 48.96 0.00 + 257 0.00 151.74 58.38 0.00 + 259 0.00 151.30 54.72 0.00 + 261 0.00 149.68 64.86 0.00 + 263 0.00 149.51 64.78 0.00 + 265 0.00 148.70 64.43 0.00 + 267 0.00 147.00 54.60 0.00 + 269 0.00 147.51 63.92 0.00 + 271 0.00 147.13 61.15 0.00 + 273 0.00 141.57 57.88 2.97 + 275 0.00 140.85 56.70 2.89 + River -13205.64 220.00 0.00 0.00 Reservoir + Lake 0.00 167.00 0.00 100.00 Reservoir + 1 -701.41 148.68 7.27 7.87 Tank + 2 -457.74 141.33 10.76 0.00 Tank + 3 907.45 159.92 13.40 0.00 Tank + + + Link Results at 23:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 20 -907.45 0.04 0.00 + 40 701.41 0.03 0.00 + 50 457.74 0.02 0.00 + 60 13205.64 9.37 8.99 + 101 0.00 0.00 0.00 + 103 122.83 0.20 0.01 + 105 -440.05 1.25 0.55 + 107 -12.38 0.04 0.00 + 109 -99.61 0.16 0.01 + 111 -486.05 1.38 0.67 + 112 421.75 1.20 0.51 + 113 -198.15 0.56 0.13 + 114 85.20 0.54 0.19 + 115 -103.63 0.66 0.27 + 116 -146.37 0.42 0.07 + 117 653.74 1.85 1.15 + 119 -697.59 1.98 1.30 + 120 1185.55 3.36 3.47 + 121 1052.90 2.99 2.79 + 122 362.36 2.31 2.79 + 123 9384.49 4.26 1.59 + 125 11346.64 5.15 2.26 + 129 1530.27 1.09 0.19 + 131 1251.38 0.89 0.13 + 133 -907.45 0.93 0.18 + 135 314.44 0.22 0.01 + 137 71.39 0.11 0.00 + 145 243.05 1.55 1.33 + 147 233.21 1.49 1.23 + 149 -370.35 2.36 2.90 + 151 -360.00 2.30 2.75 + 153 153.59 0.44 0.08 + 155 199.73 0.57 0.13 + 159 -214.01 0.61 0.15 + 161 -259.22 1.65 1.50 + 163 -128.97 0.37 0.06 + 169 202.74 1.29 0.95 + 171 371.53 1.05 0.41 + 173 7533.28 3.42 1.06 + 175 7446.79 3.38 1.04 + 177 7377.78 3.35 1.02 + 179 7089.10 3.22 0.95 + 180 4.34 0.01 0.00 + 181 4.34 0.01 0.00 + 183 6852.18 3.11 0.89 + 185 -24.32 0.16 0.02 + 186 279.49 1.78 1.72 + 187 6213.30 2.82 0.74 + 189 4870.75 2.21 0.47 + 191 -1276.86 0.91 0.14 + 193 -1668.00 1.18 0.22 + 195 -283.59 0.80 0.25 + 197 -380.73 1.08 0.42 + 199 320.68 0.91 0.31 + 201 701.41 1.99 1.31 + 202 549.90 3.51 6.03 + 203 313.24 2.00 2.13 + 204 549.90 1.56 0.84 + 205 279.49 0.79 0.24 + 207 -7.44 0.02 0.00 + 209 229.07 1.46 1.19 + 211 614.56 1.74 1.03 + 213 50.42 0.14 0.01 + 215 -105.14 0.30 0.04 + 217 -318.83 0.90 0.31 + 219 -262.29 0.74 0.21 + 221 262.29 1.67 1.53 + 223 -131.64 0.37 0.06 + 225 -103.18 0.29 0.04 + 229 4870.75 3.45 1.40 + 231 4528.02 3.21 1.22 + 233 4480.00 3.18 1.39 + 235 143.47 0.41 0.07 + 237 471.20 1.34 0.63 + 238 441.86 1.25 0.56 + 239 86.54 0.25 0.03 + 240 441.86 1.25 0.56 + 241 441.86 1.25 0.56 + 243 440.40 0.70 0.14 + 245 366.22 0.58 0.10 + 247 301.49 0.48 0.07 + 249 147.53 0.24 0.02 + 251 69.00 0.14 0.01 + 257 38.08 0.11 0.01 + 261 41.45 0.26 0.05 + 263 27.52 0.08 0.00 + 269 59.70 0.17 0.01 + 271 93.25 0.60 0.23 + 273 -59.61 0.17 0.01 + 275 -45.77 0.13 0.01 + 277 7.25 0.02 0.00 + 281 -53.01 0.22 0.03 + 283 -88.34 0.25 0.03 + 285 15.88 0.05 0.00 + 287 -186.43 0.76 0.27 + 289 457.74 1.87 1.45 + 291 91.05 0.37 0.07 + 293 112.81 0.72 0.32 + 295 -72.46 0.21 0.02 + 297 495.01 3.16 4.97 + 299 238.02 1.52 1.28 + 301 238.02 1.52 1.28 + 303 256.99 1.64 1.48 + 305 158.73 0.45 0.08 + 307 415.72 1.18 0.50 + 309 216.85 1.38 1.08 + 311 -322.00 0.91 0.31 + 313 507.00 1.44 0.72 + 315 -1384.41 0.98 0.16 + 317 113.02 0.72 0.32 + 319 30.45 0.09 0.00 + 321 7069.03 3.21 0.94 + 323 -26.48 0.08 0.00 + 325 107.56 0.69 0.29 + 329 13205.64 5.99 3.03 + 330 0.00 0.00 0.00 + 333 -0.00 0.00 0.00 + 10 0.00 0.00 0.00 Pump + 335 13205.64 0.00 -93.02 Pump + + + Node Results at 24:00:00 hrs: + -------------------------------------------------------- + Demand Head Pressure % from + Node gpm ft psi Lake + -------------------------------------------------------- + 10 0.00 147.69 0.30 100.00 + 15 620.00 127.99 41.59 0.00 + 20 0.00 160.27 13.55 0.00 + 35 1637.00 147.95 58.69 1.34 + 40 0.00 147.69 6.84 7.87 + 50 0.00 139.46 9.95 0.00 + 60 0.00 209.12 90.61 0.00 + 601 0.00 303.19 131.37 0.00 + 61 0.00 303.19 131.37 0.00 + 101 254.53 147.69 45.80 0.00 + 103 178.49 147.66 45.35 0.02 + 105 181.40 149.00 52.21 0.00 + 107 73.22 148.99 55.02 0.00 + 109 310.08 147.66 55.19 0.06 + 111 190.20 148.28 59.92 0.27 + 113 26.81 148.32 63.40 3.27 + 115 69.81 149.09 58.53 0.00 + 117 157.73 152.19 60.05 0.00 + 119 236.01 159.69 68.33 0.00 + 120 0.00 157.27 68.14 0.00 + 121 55.78 163.15 71.56 0.00 + 123 0.00 167.56 67.84 0.00 + 125 61.10 162.58 65.68 0.00 + 127 23.66 160.97 45.48 0.00 + 129 0.00 160.96 47.64 0.00 + 131 57.28 160.94 67.13 0.00 + 139 7.89 155.27 53.85 0.00 + 141 13.20 151.24 63.80 0.00 + 143 8.31 140.42 62.79 0.00 + 145 37.02 152.45 65.62 1.87 + 147 11.46 153.37 58.44 0.00 + 149 36.27 153.76 59.69 0.00 + 151 193.60 157.59 53.77 0.00 + 153 59.19 157.69 39.64 0.00 + 157 69.40 157.26 62.46 0.00 + 159 55.37 153.91 64.09 0.00 + 161 21.17 151.64 63.97 0.00 + 163 12.62 151.18 63.34 0.00 + 164 0.00 151.18 63.34 7.23 + 166 3.48 151.18 66.37 0.00 + 167 19.51 149.31 66.86 0.00 + 169 0.00 149.31 66.86 0.00 + 171 52.72 148.23 65.96 0.00 + 173 0.00 148.20 65.95 0.00 + 177 77.95 147.94 60.64 7.87 + 179 0.00 147.94 60.64 7.87 + 181 0.00 147.95 60.64 1.34 + 183 0.00 147.94 59.34 7.87 + 184 0.00 146.58 56.58 4.47 + 185 34.37 147.21 56.85 4.47 + 187 0.00 147.94 58.69 0.66 + 189 144.61 148.27 62.51 0.00 + 191 109.75 148.22 53.39 0.00 + 193 95.56 148.32 56.47 0.00 + 195 0.00 148.39 57.58 0.00 + 197 22.83 148.23 54.26 0.59 + 199 159.89 142.64 62.67 0.00 + 201 59.78 141.86 61.43 0.02 + 203 4439.00 141.70 60.53 0.02 + 204 0.00 147.69 54.89 0.66 + 205 87.58 142.59 52.68 3.13 + 206 0.00 141.31 60.79 7.58 + 207 92.98 141.72 57.51 7.71 + 208 0.00 140.83 54.09 7.36 + 209 1.17 140.01 61.53 6.22 + 211 11.62 139.73 57.51 5.51 + 213 18.68 139.65 57.48 2.88 + 215 123.53 139.46 57.39 51.61 + 217 32.45 139.44 57.82 77.90 + 219 55.37 139.43 58.68 76.44 + 225 30.55 139.43 56.95 76.41 + 229 86.00 139.34 55.83 43.28 + 231 22.08 139.34 58.21 76.80 + 237 20.92 139.37 54.32 68.44 + 239 59.78 139.32 54.73 42.40 + 241 0.00 139.32 54.73 0.01 + 243 5.82 139.32 54.30 0.00 + 247 94.31 139.31 52.56 0.00 + 249 0.00 139.31 52.56 63.71 + 251 32.37 139.31 47.36 0.00 + 253 73.06 139.26 44.74 0.00 + 255 54.12 139.31 48.66 0.00 + 257 0.00 154.15 59.43 0.00 + 259 0.00 153.72 55.77 0.00 + 261 0.00 152.14 65.92 0.00 + 263 0.00 151.98 65.85 0.00 + 265 0.00 149.90 64.95 0.00 + 267 0.00 148.34 55.18 0.26 + 269 0.00 148.66 64.42 0.00 + 271 0.00 148.03 61.54 0.00 + 273 0.00 142.59 58.32 0.00 + 275 0.00 141.86 57.13 0.03 + River -13087.22 220.00 0.00 0.00 Reservoir + Lake 0.00 167.00 0.00 100.00 Reservoir + 1 262.50 147.69 6.84 7.87 Tank + 2 -140.04 139.46 9.95 0.00 Tank + 3 2184.30 160.27 13.55 0.00 Tank + + + Link Results at 24:00:00 hrs: + ---------------------------------------------- + Flow Velocity Headloss + Link gpm fps /1000ft + ---------------------------------------------- + 20 -2184.30 0.09 0.00 + 40 -262.50 0.01 0.00 + 50 140.04 0.01 0.00 + 60 13087.22 9.28 8.84 + 101 0.00 0.00 0.00 + 103 167.59 0.27 0.02 + 105 -422.12 1.20 0.51 + 107 31.99 0.09 0.00 + 109 -10.90 0.02 0.00 + 111 -320.97 0.91 0.31 + 112 497.32 1.41 0.70 + 113 -82.10 0.23 0.02 + 114 123.98 0.79 0.38 + 115 -41.23 0.26 0.05 + 116 15.07 0.04 0.00 + 117 635.51 1.80 1.09 + 119 -732.35 2.08 1.42 + 120 1157.64 3.28 3.32 + 121 1038.00 2.94 2.72 + 122 367.94 2.35 2.87 + 123 9812.39 4.45 1.73 + 125 13087.22 5.94 2.94 + 129 2851.10 2.02 0.60 + 131 2573.55 1.83 0.50 + 133 -2184.30 2.23 0.89 + 135 365.58 0.26 0.01 + 137 57.28 0.09 0.00 + 145 308.30 1.97 2.07 + 147 300.41 1.92 1.97 + 149 -628.31 4.01 7.72 + 151 -620.00 3.96 7.54 + 153 341.10 0.97 0.35 + 155 378.12 1.07 0.42 + 159 -389.58 1.11 0.44 + 161 -425.86 2.72 3.76 + 163 -157.26 0.45 0.08 + 169 216.45 1.38 1.07 + 171 462.19 1.31 0.61 + 173 7956.54 3.61 1.17 + 175 7887.14 3.58 1.15 + 177 7831.77 3.55 1.14 + 179 7559.70 3.43 1.06 + 180 3.48 0.01 0.00 + 181 3.48 0.01 0.00 + 183 7336.64 3.33 1.01 + 185 -19.51 0.12 0.01 + 186 347.44 2.22 2.58 + 187 6712.10 3.05 0.85 + 189 4855.79 2.20 0.47 + 191 -1803.59 1.28 0.26 + 193 -1637.00 1.16 0.22 + 195 308.73 0.88 0.29 + 197 230.78 0.65 0.17 + 199 -31.72 0.09 0.00 + 201 -262.50 0.74 0.21 + 202 565.23 3.61 6.35 + 203 252.15 1.61 1.42 + 204 565.23 1.60 0.88 + 205 347.44 0.99 0.36 + 207 283.88 0.81 0.25 + 209 165.61 1.06 0.65 + 211 605.03 1.72 1.00 + 213 181.84 0.52 0.11 + 215 131.20 0.37 0.06 + 217 -246.17 0.70 0.19 + 219 -250.90 0.71 0.20 + 221 250.90 1.60 1.41 + 223 45.42 0.13 0.01 + 225 68.25 0.19 0.02 + 229 4855.79 3.44 1.39 + 231 4553.07 3.23 1.23 + 233 4439.00 3.15 1.37 + 235 142.83 0.41 0.07 + 237 506.39 1.44 0.72 + 238 581.79 1.65 0.93 + 239 168.38 0.48 0.09 + 240 581.79 1.65 0.93 + 241 581.79 1.65 0.93 + 243 580.62 0.93 0.23 + 245 333.18 0.53 0.08 + 247 241.91 0.39 0.05 + 249 118.38 0.19 0.01 + 251 55.37 0.12 0.01 + 257 30.55 0.09 0.00 + 261 72.59 0.46 0.14 + 263 22.08 0.06 0.00 + 269 235.83 0.67 0.17 + 271 35.50 0.23 0.04 + 273 179.41 0.51 0.11 + 275 48.42 0.14 0.01 + 277 5.82 0.02 0.00 + 281 42.61 0.17 0.02 + 283 71.21 0.20 0.02 + 285 -48.23 0.14 0.01 + 287 -3.47 0.01 0.00 + 289 140.04 0.57 0.16 + 291 73.06 0.30 0.05 + 293 9.39 0.06 0.00 + 295 22.98 0.07 0.00 + 297 487.59 3.11 4.83 + 299 234.26 1.50 1.24 + 301 234.26 1.50 1.24 + 303 253.33 1.62 1.44 + 305 147.92 0.42 0.07 + 307 401.25 1.14 0.47 + 309 206.95 1.32 0.99 + 311 -75.75 0.21 0.02 + 313 462.89 1.31 0.61 + 315 -1945.73 1.38 0.30 + 317 114.09 0.73 0.33 + 319 28.74 0.08 0.00 + 321 7543.59 3.42 1.06 + 323 54.29 0.15 0.01 + 325 142.14 0.91 0.49 + 329 13087.22 5.94 2.98 + 330 0.00 0.00 0.00 + 333 -0.00 0.00 0.00 + 10 0.00 0.00 0.00 Pump + 335 13087.22 0.00 -94.07 Pump + + Analysis ended Fri Oct 13 13:50:30 2017 diff --git a/tests/epanet-nrtestsuite/benchmark/epanet-2012/Example_3/performance.json b/tests/epanet-nrtestsuite/benchmark/epanet-2012/Example_3/performance.json new file mode 100644 index 0000000..ba3b33b --- /dev/null +++ b/tests/epanet-nrtestsuite/benchmark/epanet-2012/Example_3/performance.json @@ -0,0 +1,4 @@ +{ + "duration": 0.246, + "max_memory_MB": 0.0 +} \ No newline at end of file diff --git a/tests/epanet-nrtestsuite/benchmark/epanet-2012/manifest.json b/tests/epanet-nrtestsuite/benchmark/epanet-2012/manifest.json new file mode 100644 index 0000000..c47181d --- /dev/null +++ b/tests/epanet-nrtestsuite/benchmark/epanet-2012/manifest.json @@ -0,0 +1,42 @@ +{ + "Application": { + "description": "Windows, MS VC 2010, 32 Bit", + "name": "epanet", + "version": "2.0.12" + }, + "Tests": [ + { + "description": "A simple example of modeling chlorine decay", + "error_msg": null, + "name": "Example 1", + "output_files": { + "example1.out": "epanet allclose", + "example1.rpt": "epanet report" + }, + "passed": true, + "version": "1.0" + }, + { + "description": "Example of modeling a 55-hour fluoride tracer study.", + "error_msg": null, + "name": "Example 2", + "output_files": { + "example2.out": "epanet allclose", + "example2.rpt": "epanet report" + }, + "passed": true, + "version": "1.0" + }, + { + "description": "Dual-source system example", + "error_msg": null, + "name": "Example 3", + "output_files": { + "example3.out": "epanet allclose", + "example3.rpt": "epanet report" + }, + "passed": true, + "version": "1.0" + } + ] +} \ No newline at end of file diff --git a/tests/epanet-nrtestsuite/tests/examples/example1.inp b/tests/epanet-nrtestsuite/tests/examples/example1.inp new file mode 100644 index 0000000..085bf15 --- /dev/null +++ b/tests/epanet-nrtestsuite/tests/examples/example1.inp @@ -0,0 +1,180 @@ +[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 + Nodes All + Links All + + +[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/epanet-nrtestsuite/tests/examples/example1.json b/tests/epanet-nrtestsuite/tests/examples/example1.json new file mode 100644 index 0000000..faa127b --- /dev/null +++ b/tests/epanet-nrtestsuite/tests/examples/example1.json @@ -0,0 +1,17 @@ +{ + "name": "Example 1", + "version": "1.0", + "description": "A simple example of modeling chlorine decay", + "args": [ + "example1.inp", + "example1.rpt", + "example1.out" + ], + "input_files": [ + "example1.inp" + ], + "output_files": { + "example1.rpt": "epanet report", + "example1.out": "epanet allclose" + } +} diff --git a/tests/epanet-nrtestsuite/tests/examples/example2.dat b/tests/epanet-nrtestsuite/tests/examples/example2.dat new file mode 100644 index 0000000..6991e89 --- /dev/null +++ b/tests/epanet-nrtestsuite/tests/examples/example2.dat @@ -0,0 +1,80 @@ +;Measured Fluoride for Net2 +;Node Hour MG/L +11 2.50 1.02 +11 5.38 0.87 +11 8.30 0.70 +11 11.75 1.01 +11 13.02 0.62 +11 14.48 0.26 +11 17.43 0.17 +11 20.30 0.20 +11 23.35 0.90 +11 27.00 0.13 +11 29.67 0.13 +11 33.70 0.54 +11 35.40 0.90 +11 38.30 0.17 +11 41.62 0.10 +11 44.42 0.12 +11 47.25 0.71 +11 51.00 0.13 +11 53.27 0.58 +19 0.25 1.04 +19 2.75 1.04 +19 5.70 1.08 +19 8.60 1.00 +19 12.00 0.81 +19 13.25 0.95 +19 14.73 1.02 +19 17.77 1.01 +19 20.52 0.87 +19 23.53 0.28 +19 27.17 0.98 +19 29.87 0.85 +19 33.92 0.12 +19 35.67 0.17 +19 38.48 0.64 +19 42.08 0.79 +19 44.68 0.87 +19 47.50 0.16 +19 51.17 0.56 +19 53.45 0.70 +25 0.58 1.04 + 3.00 1.00 + 5.87 1.03 + 8.80 1.00 + 12.25 1.02 + 13.50 0.88 + 14.97 0.36 + 18.15 0.91 + 20.75 0.92 + 23.73 0.94 + 27.42 0.19 + 30.08 0.12 + 34.12 0.83 + 35.87 0.82 + 38.67 0.72 + 42.40 0.76 + 44.92 0.78 + 47.75 0.87 + 51.50 0.42 + 53.75 0.48 + 34 2.67 1.03 + 5.50 1.04 + 8.45 0.98 + 11.87 1.01 + 13.15 1.02 + 14.62 1.04 + 17.60 1.02 + 20.42 1.02 + 23.43 1.00 + 27.08 1.02 + 29.75 0.94 + 33.82 0.89 + 35.53 0.75 + 38.40 0.80 + 41.85 0.84 + 44.55 0.92 + 47.42 0.91 + 51.08 0.81 + 53.37 0.47 diff --git a/tests/epanet-nrtestsuite/tests/examples/example2.inp b/tests/epanet-nrtestsuite/tests/examples/example2.inp new file mode 100644 index 0000000..8b14982 --- /dev/null +++ b/tests/epanet-nrtestsuite/tests/examples/example2.inp @@ -0,0 +1,310 @@ +[TITLE] +EPANET Example Network 2 +Example of modeling a 55-hour fluoride tracer study. +Measured fluoride data is contained in the file Net2-FL.dat +and should be registered with the project to produce a +Calibration Report (select Calibration Data from the Project +menu). + +[JUNCTIONS] +;ID Elev Demand Pattern + 1 50 -694.4 2 ; + 2 100 8 ; + 3 60 14 ; + 4 60 8 ; + 5 100 8 ; + 6 125 5 ; + 7 160 4 ; + 8 110 9 ; + 9 180 14 ; + 10 130 5 ; + 11 185 34.78 ; + 12 210 16 ; + 13 210 2 ; + 14 200 2 ; + 15 190 2 ; + 16 150 20 ; + 17 180 20 ; + 18 100 20 ; + 19 150 5 ; + 20 170 19 ; + 21 150 16 ; + 22 200 10 ; + 23 230 8 ; + 24 190 11 ; + 25 230 6 ; + 27 130 8 ; + 28 110 0 ; + 29 110 7 ; + 30 130 3 ; + 31 190 17 ; + 32 110 17 ; + 33 180 1.5 ; + 34 190 1.5 ; + 35 110 0 ; + 36 110 1 ; + +[RESERVOIRS] +;ID Head Pattern + +[TANKS] +;ID Elevation InitLevel MinLevel MaxLevel Diameter MinVol VolCurve + 26 235 56.7 50 70 50 0 ; + +[PIPES] +;ID Node1 Node2 Length Diameter Roughness MinorLoss Status + 1 1 2 2400 12 100 0 Open ; + 2 2 5 800 12 100 0 Open ; + 3 2 3 1300 8 100 0 Open ; + 4 3 4 1200 8 100 0 Open ; + 5 4 5 1000 12 100 0 Open ; + 6 5 6 1200 12 100 0 Open ; + 7 6 7 2700 12 100 0 Open ; + 8 7 8 1200 12 140 0 Open ; + 9 7 9 400 12 100 0 Open ; + 10 8 10 1000 8 140 0 Open ; + 11 9 11 700 12 100 0 Open ; + 12 11 12 1900 12 100 0 Open ; + 13 12 13 600 12 100 0 Open ; + 14 13 14 400 12 100 0 Open ; + 15 14 15 300 12 100 0 Open ; + 16 13 16 1500 8 100 0 Open ; + 17 15 17 1500 8 100 0 Open ; + 18 16 17 600 8 100 0 Open ; + 19 17 18 700 12 100 0 Open ; + 20 18 32 350 12 100 0 Open ; + 21 16 19 1400 8 100 0 Open ; + 22 14 20 1100 12 100 0 Open ; + 23 20 21 1300 8 100 0 Open ; + 24 21 22 1300 8 100 0 Open ; + 25 20 22 1300 8 100 0 Open ; + 26 24 23 600 12 100 0 Open ; + 27 15 24 250 12 100 0 Open ; + 28 23 25 300 12 100 0 Open ; + 29 25 26 200 12 100 0 Open ; + 30 25 31 600 12 100 0 Open ; + 31 31 27 400 8 100 0 Open ; + 32 27 29 400 8 100 0 Open ; + 34 29 28 700 8 100 0 Open ; + 35 22 33 1000 8 100 0 Open ; + 36 33 34 400 8 100 0 Open ; + 37 32 19 500 8 100 0 Open ; + 38 29 35 500 8 100 0 Open ; + 39 35 30 1000 8 100 0 Open ; + 40 28 35 700 8 100 0 Open ; + 41 28 36 300 8 100 0 Open ; + +[PUMPS] +;ID Node1 Node2 Parameters + +[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.26 1.04 .97 .97 .89 1.19 + 1 1.28 .67 .67 1.34 2.46 .97 + 1 .92 .68 1.43 .61 .31 .78 + 1 .37 .67 1.26 1.56 1.19 1.26 + 1 .6 1.1 1.03 .73 .88 1.06 + 1 .99 1.72 1.12 1.34 1.12 .97 + 1 1.04 1.15 .91 .61 .68 .46 + 1 .51 .74 1.12 1.34 1.26 .97 + 1 .82 1.37 1.03 .81 .88 .81 + 1 .81 +;Pump Station Outflow Pattern + 2 .96 .96 .96 .96 .96 .96 + 2 .62 0 0 0 0 0 + 2 .8 1 1 1 1 .15 + 2 0 0 0 0 0 0 + 2 .55 .92 .92 .92 .92 .9 + 2 .9 .45 0 0 0 0 + 2 0 .7 1 1 1 1 + 2 .2 0 0 0 0 0 + 2 0 .74 .92 .92 .92 .92 + 2 .92 +;Pump Station Fluoride Pattern + 3 .98 1.02 1.05 .99 .64 .46 + 3 .35 .35 .35 .35 .35 .35 + 3 .17 .17 .13 .13 .13 .15 + 3 .15 .15 .15 .15 .15 .15 + 3 .15 .12 .1 .08 .11 .09 + 3 .09 .08 .08 .08 .08 .08 + 3 .08 .09 .07 .07 .09 .09 + 3 .09 .09 .09 .09 .09 .09 + 3 .09 .08 .35 .72 .82 .92 + 3 1 + +[CURVES] +;ID X-Value Y-Value + +[CONTROLS] + +[RULES] + +[ENERGY] + Global Efficiency 75 + Global Price 0.0 + Demand Charge 0.0 + +[EMITTERS] +;Junction Coefficient + +[QUALITY] +;Node InitQual + 1 1.0 + 2 1.0 + 3 1.0 + 4 1.0 + 5 1.0 + 6 1.0 + 7 1.0 + 8 1.0 + 9 1.0 + 10 1.0 + 11 1.0 + 12 1.0 + 13 1.0 + 14 1.0 + 15 1.0 + 16 1.0 + 17 1.0 + 18 1.0 + 19 1.0 + 20 1.0 + 21 1.0 + 22 1.0 + 23 1.0 + 24 1.0 + 25 1.0 + 27 1.0 + 28 1.0 + 29 1.0 + 30 1.0 + 31 1.0 + 32 1.0 + 33 1.0 + 34 1.0 + 35 1.0 + 36 1.0 + 26 1.0 + +[SOURCES] +;Node Type Quality Pattern + 1 CONCEN 1.0 3 + +[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 55: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 8 am + Statistic None + +[REPORT] + Status Yes + Summary No + Nodes All + Links All + +[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 Fluoride mg/L + Diffusivity 1.0 + Tolerance 0.01 + +[COORDINATES] +;Node X-Coord Y-Coord + 1 21.00 4.00 + 2 19.00 20.00 + 3 11.00 21.00 + 4 14.00 28.00 + 5 19.00 25.00 + 6 28.00 23.00 + 7 36.00 39.00 + 8 38.00 30.00 + 9 36.00 42.00 + 10 37.00 23.00 + 11 37.00 49.00 + 12 39.00 60.00 + 13 38.00 64.00 + 14 38.00 66.00 + 15 37.00 69.00 + 16 27.00 65.00 + 17 27.00 69.00 + 18 23.00 68.00 + 19 21.00 59.00 + 20 45.00 68.00 + 21 51.00 62.00 + 22 54.00 69.00 + 23 35.00 74.00 + 24 37.00 71.00 + 25 35.00 76.00 + 27 39.00 87.00 + 28 49.00 85.00 + 29 42.00 86.00 + 30 47.00 80.00 + 31 37.00 80.00 + 32 23.00 64.00 + 33 56.00 73.00 + 34 56.00 77.00 + 35 43.00 81.00 + 36 53.00 87.00 + 26 33.00 76.00 + +[VERTICES] +;Link X-Coord Y-Coord + +[LABELS] +;X-Coord Y-Coord Label & Anchor Node + 24.00 7.00 "Pump" + 24.00 4.00 "Station" + 26.76 77.42 "Tank" + +[BACKDROP] + DIMENSIONS 8.75 -0.15 58.25 91.15 + UNITS None + FILE + OFFSET 0.00 0.00 + +[END] diff --git a/tests/epanet-nrtestsuite/tests/examples/example2.json b/tests/epanet-nrtestsuite/tests/examples/example2.json new file mode 100644 index 0000000..cbc3286 --- /dev/null +++ b/tests/epanet-nrtestsuite/tests/examples/example2.json @@ -0,0 +1,17 @@ +{ + "name": "Example 2", + "version": "1.0", + "description": "Example of modeling a 55-hour fluoride tracer study.", + "args": [ + "example2.inp", + "example2.rpt", + "example2.out" + ], + "input_files": [ + "example2.inp" + ], + "output_files": { + "example2.rpt": "epanet report", + "example2.out": "epanet allclose" + } +} diff --git a/tests/epanet-nrtestsuite/tests/examples/example3.inp b/tests/epanet-nrtestsuite/tests/examples/example3.inp new file mode 100644 index 0000000..da24ba6 --- /dev/null +++ b/tests/epanet-nrtestsuite/tests/examples/example3.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 + Nodes All + Links All + +[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 + 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/epanet-nrtestsuite/tests/examples/example3.json b/tests/epanet-nrtestsuite/tests/examples/example3.json new file mode 100644 index 0000000..124d183 --- /dev/null +++ b/tests/epanet-nrtestsuite/tests/examples/example3.json @@ -0,0 +1,17 @@ +{ + "name": "Example 3", + "version": "1.0", + "description": "Dual-source system example", + "args": [ + "example3.inp", + "example3.rpt", + "example3.out" + ], + "input_files": [ + "example3.inp" + ], + "output_files": { + "example3.rpt": "epanet report", + "example3.out": "epanet allclose" + } +} diff --git a/tools/.gitignore b/tools/.gitignore new file mode 100644 index 0000000..8b82c98 --- /dev/null +++ b/tools/.gitignore @@ -0,0 +1,27 @@ +# Python compiler files +*.py[cd] + +# Python distribution and packaging +build/ +dist/ +temp/ +*.cfg +*.egg-info/ +*.whl + +# SWIG generated files +epanet_output_wrap.c +epanet_output.py + +# C compiler +*.o +*.dll +*.exe + +# Eclipse project files and directories +.metadata/ +.settings/ +Release/ +.project +.cproject +.pydevproject diff --git a/tools/epanet-output/setup.py b/tools/epanet-output/setup.py new file mode 100644 index 0000000..90d57e0 --- /dev/null +++ b/tools/epanet-output/setup.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +# +# setup.py +# +# Created: 9/20/2017 +# Author: Michael E. Tryby +# US EPA - ORD/NRMRL +# +# Setup up script for en_outputapi python extension +# +# Requires: +# Platform C language compiler +# Python packages: numpy +# + +try: + from setuptools import setup, Extension + from setuptools.command.build_ext import build_ext +except ImportError: + from distutils.core import setup, Extension + from distutils.command.build_ext import build_ext + +setup( + name = "epanet-output", + version = "1.0", + ext_modules = [ + Extension("_epanet_output", + sources = ['src/epanet_output.i', 'src/epanet_output.c', 'src/errormanager.c'], + swig_opts=['-modern'], + language = 'C' + ) + ], + package_dir = {'':'src'}, + py_modules = ['epanet_output'], + + install_requires = [ + 'enum34' + ] +) diff --git a/tools/epanet-output/src/epanet_output.c b/tools/epanet-output/src/epanet_output.c new file mode 100644 index 0000000..486ef81 --- /dev/null +++ b/tools/epanet-output/src/epanet_output.c @@ -0,0 +1,969 @@ +//----------------------------------------------------------------------------- +// +// epanet_output.c -- API for reading results from EPANET binary output file +// +// Version: 0.30 +// Date 09/06/2017 +// 06/17/2016 +// 08/05/2014 +// 05/21/2014 +// +// Author: Michael E. Tryby +// US EPA - ORD/NRMRL +// +// Modified: Maurizio Cingi +// University of Modena +// +// Purpose: Output API provides an interface for retrieving results from an +// EPANET binary output file. +// +// Output data in the binary file are aligned on a 4 byte word size. +// Therefore all values both integers and reals are 32 bits in length. +// +// All values returned by the output API are indexed from 0 to n-1. This +// differs from how node and link elements are indexed by the binary file +// writer found in EPANET. Times correspond to reporting periods are indexed +// from 0 to number of reporting periods minus one. Node and link elements +// are indexed from 0 to nodeCount minus one and 0 to linkCount minus one +// respectively. +// +// The Output API functions provide a convenient way to select "slices" of +// data from the output file. As such they return arrays of data. The API +// functions automatically allocate memory for the array to be returned. The +// caller is responsible for deallocating memory. The function ENR_free() is +// provided to deallocate memory. +// +//----------------------------------------------------------------------------- + +#include "epanet_output.h" + +#include +#include +#include + +#include "errormanager.h" +#include "messages.h" + +// NOTE: These depend on machine data model and may change when porting +// F_OFF Must be a 8 byte / 64 bit integer for large file support +#ifdef _WIN32 // Windows (32-bit and 64-bit) +#define F_OFF __int64 +#else // Other platforms +#define F_OFF off_t +#endif +#define INT4 int // Must be a 4 byte / 32 bit integer type +#define REAL4 float // Must be a 4 byte / 32 bit real type +#define WORDSIZE 4 // Memory alignment 4 byte word size for both int and real + +#define MINNREC 14 // Minimum allowable number of records +#define PROLOGUE 884 // Preliminary fixed length section of header +#define MAXID_P1 32 // Max. # characters in ID name + +#define NELEMENTTYPES 5 // Number of element types +#define NENERGYRESULTS 6 // Number of energy results +#define NNODERESULTS 4 // number of result fields for nodes +#define NLINKRESULTS 8 // number of result fields for links +#define NREACTRESULTS 4 // number of net reaction results + +#define MEMCHECK(x) (((x) == NULL) ? 411 : 0 ) + +// Typedefs for opaque pointer +typedef struct data_s { + char name[MAXFNAME+1]; // file path/name + FILE* file; // FILE structure pointer + INT4 nodeCount, tankCount, linkCount, pumpCount, valveCount, nPeriods; + F_OFF outputStartPos; // starting file position of output data + F_OFF bytesPerPeriod; // bytes saved per simulation time period + + error_handle_t* error_handle; +} data_t; + +//----------------------------------------------------------------------------- +// Local functions +//----------------------------------------------------------------------------- +void errorLookup(int errcode, char* errmsg, int length); +int validateFile(ENR_Handle); +float getNodeValue(ENR_Handle, int, int, int); +float getLinkValue(ENR_Handle, int, int, int); + +int _fopen(FILE **f, const char *name, const char *mode); +int _fseek(FILE* stream, F_OFF offset, int whence); +F_OFF _ftell(FILE* stream); + +float* newFloatArray(int n); +int* newIntArray(int n); +char* newCharArray(int n); + + +int DLLEXPORT ENR_init(ENR_Handle* dp_handle) +// Purpose: Initialized pointer for the opaque ENR_Handle. +// +// Returns: Error code 0 on success, -1 on failure +// +// Note: The existence of this function has been carefully considered. +// Don't change it. +// +{ + int errorcode = 0; + data_t* p_data; + + // Allocate memory for private data + p_data = (data_t*)calloc(1, sizeof(data_t)); + + if (p_data != NULL){ + p_data->error_handle = new_errormanager(&errorLookup); + *dp_handle = p_data; + } + else + errorcode = -1; + + // TODO: Need to handle errors during initialization better. + return errorcode; +} + +int DLLEXPORT ENR_close(ENR_Handle* p_handle) +/*------------------------------------------------------------------------ + ** Input: *p_handle = pointer to ENR_Handle struct + ** + ** Returns: Error code 0 on success, -1 on failure + ** + ** Purpose: Close the output binary file, dellocate ENR_Handle struc + ** and nullify pointer to ENR_Handle struct + ** + ** NOTE: ENR_close must be called before program end + ** after calling ENR_close data in ENR_Handle struct are no more + ** accessible + **------------------------------------------------------------------------- + */ +{ + data_t* p_data; + int errorcode = 0; + + p_data = (data_t*)(*p_handle); + + if (p_data == NULL || p_data->file == NULL) + errorcode = -1; + + else + { + dst_errormanager(p_data->error_handle); + fclose(p_data->file); + free(p_data); + + *p_handle = NULL; + } + + return errorcode; +} + +int DLLEXPORT ENR_open(ENR_Handle p_handle, const char* path) +/*------------------------------------------------------------------------ + ** Input: path + ** Output: p_handle = pointer to ENR_Handle struct + ** Returns: warning / error code + ** Purpose: Opens the output binary file and reads prologue and epilogue + ** + ** NOTE: ENR_init must be called before anyother ENR_* functions + **------------------------------------------------------------------------- + */ +{ + int err, errorcode = 0; + F_OFF bytecount; + data_t* p_data; + + p_data = (data_t*)p_handle; + + if (p_data == NULL) return -1; + else + { + strncpy(p_data->name, path, MAXFNAME); + // Attempt to open binary output file for reading only + if ((_fopen(&(p_data->file), path, "rb")) != 0) errorcode = 434; + + // Perform checks to insure the file is valid + else if ((err = validateFile(p_data)) != 0) errorcode = err; + + // If a warning is encountered read file header + if (errorcode < 400 ) { + + // read network size + fseek(p_data->file, 2*WORDSIZE, SEEK_SET); + fread(&(p_data->nodeCount), WORDSIZE, 1, p_data->file); + fread(&(p_data->tankCount), WORDSIZE, 1, p_data->file); + fread(&(p_data->linkCount), WORDSIZE, 1, p_data->file); + fread(&(p_data->pumpCount), WORDSIZE, 1, p_data->file); + fread(&(p_data->valveCount), WORDSIZE, 1, p_data->file); + + // Compute positions and offsets for retrieving data + // fixed portion of header + title section + filenames + chem names + bytecount = PROLOGUE; + // node names + link names + bytecount += MAXID_P1*p_data->nodeCount + MAXID_P1*p_data->linkCount; + // network connectivity + tank nodes + tank areas + bytecount += 3*WORDSIZE*p_data->linkCount + 2*WORDSIZE*p_data->tankCount; + // node elevations + link lengths and link diameters + bytecount += WORDSIZE*p_data->nodeCount + 2*WORDSIZE*p_data->linkCount; + // pump energy summary + bytecount += 7*WORDSIZE*p_data->pumpCount + WORDSIZE; + p_data->outputStartPos= bytecount; + + p_data->bytesPerPeriod = NNODERESULTS*WORDSIZE*p_data->nodeCount + + NLINKRESULTS*WORDSIZE*p_data->linkCount; + } + } + // If error close the binary file + if (errorcode > 400) { + set_error(p_data->error_handle, errorcode); + ENR_close(&p_handle); + } + + return errorcode; +} + +int DLLEXPORT ENR_getVersion(ENR_Handle p_handle, int* version) +/*------------------------------------------------------------------------ + ** Input: p_handle = pointer to ENR_Handle struct + ** Output: version Epanet version + ** Returns: error code + ** + ** Purpose: Returns Epanet version that wrote EBOFile + **--------------element codes------------------------------------------- + */ +{ + int errorcode = 0; + data_t* p_data; + + p_data = (data_t*)p_handle; + + if (p_data == NULL) return -1; + else + { + fseek(p_data->file, 1*WORDSIZE, SEEK_SET); + if (fread(version, WORDSIZE, 1, p_data->file) != 1) + errorcode = 436; + } + + return set_error(p_data->error_handle, errorcode); +} + +int DLLEXPORT ENR_getNetSize(ENR_Handle p_handle, int** elementCount, int* length) +/*------------------------------------------------------------------------ + ** Input: p_handle = pointer to ENR_Handle struct + ** Output: array of element counts (nodes, tanks, links, pumps, valves) + ** Returns: error code + ** Purpose: Returns an array of count values + **------------------------------------------------------------------------- + */ +{ + int errorcode = 0; + int* temp = newIntArray(NELEMENTTYPES); + data_t* p_data; + + p_data = (data_t*)p_handle; + + if (p_data == NULL) return -1; + else + { + temp[0] = p_data->nodeCount; + temp[1] = p_data->tankCount; + temp[2] = p_data->linkCount; + temp[3] = p_data->pumpCount; + temp[4] = p_data->valveCount; + + *elementCount = temp; + *length = NELEMENTTYPES; + } + + return set_error(p_data->error_handle, errorcode); +} + +int DLLEXPORT ENR_getUnits(ENR_Handle p_handle, ENR_Units code, int* unitFlag) +/*------------------------------------------------------------------------ + ** Input: p_handle = pointer to ENR_Handle struct + ** code + ** Output: count + ** Returns: unitFlag + ** Purpose: Returns pressure or flow unit flag + **--------------pressure unit flags---------------------------------------- + ** 0 = psi + ** 1 = meters + ** 2 = kPa + **------------------flow unit flags---------------------------------------- + ** 0 = cubic feet/second + ** 1 = gallons/minute + ** 2 = million gallons/day + ** 3 = Imperial million gallons/day + ** 4 = acre-ft/day + ** 5 = liters/second + ** 6 = liters/minute + ** 7 = megaliters/day + ** 8 = cubic meters/hour + ** 9 = cubic meters/day + **------------------------------------------------------------------------- + */ +{ + int errorcode = 0; + data_t* p_data; + + *unitFlag = -1; + + p_data = (data_t*)p_handle; + + if (p_data == NULL) return -1; + else + { + switch (code) + { + case ENR_flowUnits: + fseek(p_data->file, 9*WORDSIZE, SEEK_SET); + fread(unitFlag, WORDSIZE, 1, p_data->file); + break; + + case ENR_pressUnits: + fseek(p_data->file, 10*WORDSIZE, SEEK_SET); + fread(unitFlag, WORDSIZE, 1, p_data->file); + break; + + default: errorcode = 421; + } + } + return set_error(p_data->error_handle, errorcode); +} + +int DLLEXPORT ENR_getTimes(ENR_Handle p_handle, ENR_Time code, int* time) +/*------------------------------------------------------------------------ + ** Input: p_handle = pointer to ENR_Handle struct + ** code = element code + ** Output: time + ** Returns: error code + ** Purpose: Returns report and simulation time related parameters. + **------------------------------------------------------------------------- + */ +{ + int errorcode = 0; + data_t* p_data; + + *time = -1; + + p_data = (data_t*)p_handle; + + if (p_data == NULL) return -1; + else + { + switch (code) + { + case ENR_reportStart: + fseek(p_data->file, 12*WORDSIZE, SEEK_SET); + fread(time, WORDSIZE, 1, p_data->file); + break; + + case ENR_reportStep: + fseek(p_data->file, 13*WORDSIZE, SEEK_SET); + fread(time, WORDSIZE, 1, p_data->file); + break; + + case ENR_simDuration: + fseek(p_data->file, 14*WORDSIZE, SEEK_SET); + fread(time, WORDSIZE, 1, p_data->file); + break; + + case ENR_numPeriods: + *time = p_data->nPeriods; + break; + + default: + errorcode = 421; + } + } + return set_error(p_data->error_handle, errorcode); +} + +int DLLEXPORT ENR_getElementName(ENR_Handle p_handle, ENR_ElementType type, + int elementIndex, char** name, int* length) +/*------------------------------------------------------------------------ + ** Input: p_handle = pointer to ENR_Handle struct + ** type = ENR_node or ENR_link + ** elementIndex from 1 to nodeCount or 1 to linkCount + ** Output: name = elementName + ** Returns: error code + ** Purpose: Retrieves Name of a specified node or link element + ** NOTE: 'name' must be able to hold MAXID characters + ** TODO: Takes EPANET indexing from 1 to n not 0 to n-1 + **------------------------------------------------------------------------- + */ +{ + F_OFF offset; + int errorcode = 0; + char* temp; + data_t* p_data; + + p_data = (data_t*)p_handle; + + if (p_data == NULL) return -1; + /* Allocate memory for name */ + else if MEMCHECK(temp = newCharArray(MAXID_P1)) errorcode = 411; + + else + { + switch (type) + { + case ENR_node: + if (elementIndex < 1 || elementIndex > p_data->nodeCount) + errorcode = 423; + else offset = PROLOGUE + (elementIndex - 1)*MAXID_P1; + break; + + case ENR_link: + if (elementIndex < 1 || elementIndex > p_data->linkCount) + errorcode = 423; + else + offset = PROLOGUE + p_data->nodeCount*MAXID_P1 + + (elementIndex - 1)*MAXID_P1; + break; + + default: + errorcode = 421; + } + + if (!errorcode) + { + _fseek(p_data->file, offset, SEEK_SET); + fread(temp, 1, MAXID_P1, p_data->file); + + *name = temp; + *length = MAXID_P1; + } + } + + return set_error(p_data->error_handle, errorcode); +} + +int DLLEXPORT ENR_getEnergyUsage(ENR_Handle p_handle, int pumpIndex, + int* linkIndex, float** outValues, int* length) +/* + * Purpose: Returns pump energy usage statistics. + * + * Energy usage statistics: + * 0 = pump utilization + * 1 = avg. efficiency + * 2 = avg. kW/flow + * 3 = avg. kwatts + * 4 = peak kwatts + * 5 = cost/day + */ +{ + F_OFF offset; + int errorcode = 0; + float* temp; + data_t* p_data; + + p_data = (data_t*)p_handle; + + if (p_data == NULL) return -1; + // Check for valid pump index + else if (pumpIndex < 1 || pumpIndex > p_data->pumpCount) errorcode = 423; + // Check memory for outValues + else if MEMCHECK(temp = newFloatArray(NENERGYRESULTS)) errorcode = 411; + + else + { + // Position offset to start of pump energy summary + offset = p_data->outputStartPos - (p_data->pumpCount*(WORDSIZE + 6*WORDSIZE) + WORDSIZE); + // Adjust offset by pump index + offset += (pumpIndex - 1)*(WORDSIZE + 6*WORDSIZE); + + // Power summary is 1 int and 6 floats for each pump + _fseek(p_data->file, offset, SEEK_SET); + fread(linkIndex, WORDSIZE, 1, p_data->file); + fread(temp, WORDSIZE, 6, p_data->file); + + *outValues = temp; + *length = NENERGYRESULTS; + } + return set_error(p_data->error_handle, errorcode); +} + +int DLLEXPORT ENR_getNetReacts(ENR_Handle p_handle, float** outValues, int* length) +/* + * Purpose: Returns network wide average reaction rates and average + * source mass inflow: + * 0 = bulk + * 1 = wall + * 2 = tank + * 3 = source + */ +{ + F_OFF offset; + int errorcode = 0; + float* temp; + data_t* p_data; + + p_data = (data_t*)p_handle; + + if (p_data == NULL) return -1; + // Check memory for outValues + else if MEMCHECK(temp = newFloatArray(NREACTRESULTS)) errorcode = 411; + + else + { + // Reaction summary is 4 floats located right before epilogue. + // This offset is relative to the end of the file. + offset = - 3*WORDSIZE - 4*WORDSIZE; + _fseek(p_data->file, offset, SEEK_END); + fread(temp, WORDSIZE, 4, p_data->file); + + *outValues = temp; + *length = NREACTRESULTS; + } + return set_error(p_data->error_handle, errorcode); +} + +void DLLEXPORT ENR_free(void** array) +// +// Purpose: Frees memory allocated by API calls +// +{ + if (array != NULL) { + free(*array); + *array = NULL; + } +} + +int DLLEXPORT ENR_getNodeSeries(ENR_Handle p_handle, int nodeIndex, ENR_NodeAttribute attr, + int startPeriod, int endPeriod, float** outValueSeries, int* dim) +// +// Purpose: Get time series results for particular attribute. Specify series +// start and length using seriesStart and seriesLength respectively. +// +// NOTE: The node index argument corresponds to the EPANET node index from 1 to +// nnodes. The series returned is indexed from 0 to nperiods - 1. +// +{ + int k, length, errorcode = 0; + float* temp; + data_t* p_data; + + p_data = (data_t*)p_handle; + + if (p_data == NULL) return -1; + else if (nodeIndex < 1 || nodeIndex > p_data->nodeCount) errorcode = 423; + else if (startPeriod < 0 || endPeriod >= p_data->nPeriods || + endPeriod <= startPeriod) errorcode = 422; + // Check memory for outValues + else if MEMCHECK(temp = newFloatArray(length = endPeriod - startPeriod)) errorcode = 411; + else + { + // loop over and build time series + for (k = 0; k < length; k++) + temp[k] = getNodeValue(p_handle, startPeriod + k, + nodeIndex, attr); + + *outValueSeries = temp; + *dim = length; + } + return set_error(p_data->error_handle, errorcode); +} + +int DLLEXPORT ENR_getLinkSeries(ENR_Handle p_handle, int linkIndex, ENR_LinkAttribute attr, + int startPeriod, int endPeriod, float** outValueSeries, int* dim) +// +// Purpose: Get time series results for particular attribute. Specify series +// start and length using seriesStart and seriesLength respectively. +// +// NOTE: +// The link index argument corresponds to the EPANET link index from 1 to +// nlinks. The series returned is indexed from 0 to nperiods - 1. +// +{ + int k, length, errorcode = 0; + float* temp; + data_t* p_data; + + p_data = (data_t*)p_handle; + + if (p_data == NULL) return -1; + else if (linkIndex < 1 || linkIndex > p_data->linkCount) errorcode = 423; + else if (startPeriod < 0 || endPeriod >= p_data->nPeriods || + endPeriod <= startPeriod) errorcode = 422; + // Check memory for outValues + else if MEMCHECK(temp = newFloatArray(length = endPeriod - startPeriod)) errorcode = 411; + else + { + // loop over and build time series + for (k = 0; k < length; k++) + temp[k] = getLinkValue(p_handle, startPeriod + k, linkIndex, attr); + + *outValueSeries = temp; + *dim = length; + } + return set_error(p_data->error_handle, errorcode); +} + +int DLLEXPORT ENR_getNodeAttribute(ENR_Handle p_handle, int periodIndex, + ENR_NodeAttribute attr, float** outValueArray, int* length) +// +// Purpose: +// For all nodes at given time, get a particular attribute +// +// Returns: +// Error code +// OutValueArray of results is indexed from 0 to nodeCount +// +// Warning: +// Caller must free memory allocated for outValueArray +// +// NOTE: +// The array returned is indexed from 0 to nnodes - 1. So to access +// node values by their EPANET index, the index value must be +// decremented by one. +// +{ + F_OFF offset; + int errorcode = 0; + float * temp; + data_t* p_data; + + p_data = (data_t*)p_handle; + + if (p_data == NULL) return -1; + // if the time index is out of range return an error + else if (periodIndex < 0 || periodIndex >= p_data->nPeriods) errorcode = 422; + // Check memory for outValues + else if MEMCHECK(temp = newFloatArray(p_data->nodeCount)) errorcode = 411; + + else + { + // calculate byte offset to start time for series + offset = p_data->outputStartPos + (periodIndex)*p_data->bytesPerPeriod; + // add offset for node and attribute + offset += ((attr - 1)*p_data->nodeCount)*WORDSIZE; + + _fseek(p_data->file, offset, SEEK_SET); + fread(temp, WORDSIZE, p_data->nodeCount, p_data->file); + + *outValueArray = temp; + *length = p_data->nodeCount; + } + + return set_error(p_data->error_handle, errorcode); +} + +int DLLEXPORT ENR_getLinkAttribute(ENR_Handle p_handle, int periodIndex, + ENR_LinkAttribute attr, float** outValueArray, int* length) +// +// Purpose: +// For all links at given time, get a particular attribute +// +// Returns: +// Error code +// OutValueArray of results is indexed from 0 to linkCount +// +// Warning: +// Caller must free memory allocated for outValueArray +// +// NOTE: +// The array returned is indexed from 0 to nlinks - 1. So to access +// link values by their EPANET index, the index value must be +// decremented by one. +// +{ + F_OFF offset; + int errorcode = 0; + float* temp; + data_t* p_data; + + p_data = (data_t*)p_handle; + + if (p_data == NULL) return -1; + // if the time index is out of range return an error + else if (periodIndex < 0 || periodIndex >= p_data->nPeriods) errorcode = 422; + // Check memory for outValues + else if MEMCHECK(temp = newFloatArray(p_data->linkCount)) errorcode = 411; + + else + { + // calculate byte offset to start time for series + offset = p_data->outputStartPos + (periodIndex)*p_data->bytesPerPeriod + + (NNODERESULTS*p_data->nodeCount)*WORDSIZE; + // add offset for link and attribute + offset += ((attr - 1)*p_data->linkCount)*WORDSIZE; + + _fseek(p_data->file, offset, SEEK_SET); + fread(temp, WORDSIZE, p_data->linkCount, p_data->file); + + *outValueArray = temp; + *length = p_data->linkCount; + } + + return set_error(p_data->error_handle, errorcode); +} + +int DLLEXPORT ENR_getNodeResult(ENR_Handle p_handle, int periodIndex, + int nodeIndex, float** outValueArray, int* length) +// +// Purpose: For a node at given time, get all attributes. +// +// NOTE: +// +{ + int j, errorcode = 0; + float* temp; + data_t* p_data; + + p_data = (data_t*)p_handle; + + if (p_data == NULL) return -1; + else if (periodIndex < 0 || periodIndex >= p_data->nPeriods) errorcode = 422; + else if (nodeIndex < 1 || nodeIndex > p_data->nodeCount) errorcode = 423; + else if MEMCHECK(temp = newFloatArray(NNODERESULTS)) errorcode = 411; + else + { + for (j = 0; j < NNODERESULTS; j++) + temp[j] = getNodeValue(p_handle, periodIndex, nodeIndex, j); + + *outValueArray = temp; + *length = NNODERESULTS; + } + + return set_error(p_data->error_handle, errorcode); +} + +int DLLEXPORT ENR_getLinkResult(ENR_Handle p_handle, int periodIndex, + int linkIndex, float** outValueArray, int* length) +// +// Purpose: For a link at given time, get all attributes +// +{ + int j, errorcode = 0; + float* temp; + data_t* p_data; + + p_data = (data_t*)p_handle; + + if (p_data == NULL) return -1; + else if (periodIndex < 0 || periodIndex >= p_data->nPeriods) errorcode = 422; + else if (linkIndex < 1 || linkIndex > p_data->linkCount) errorcode = 423; + else if MEMCHECK(temp = newFloatArray(NLINKRESULTS)) errorcode = 411; + else + { + for (j = 0; j < NLINKRESULTS; j++) + temp[j] = getLinkValue(p_handle, periodIndex, linkIndex, j); + + *outValueArray = temp; + *length = NLINKRESULTS; + } + return set_error(p_data->error_handle, errorcode); +} + +void DLLEXPORT ENR_clearError(ENR_Handle p_handle) +{ + data_t* p_data; + + p_data = (data_t*)p_handle; + clear_error(p_data->error_handle); +} + +int DLLEXPORT ENR_checkError(ENR_Handle p_handle, char** msg_buffer) +{ + int errorcode = 0; + char *temp = NULL; + data_t* p_data; + + p_data = (data_t*)p_handle; + + if (p_data == NULL) return -1; + else + { + errorcode = p_data->error_handle->error_status; + if (errorcode) + temp = check_error(p_data->error_handle); + + *msg_buffer = temp; + } + + return errorcode; +} + + +void errorLookup(int errcode, char* dest_msg, int dest_len) +// +// Purpose: takes error code returns error message +// +{ + const char* msg; + + switch (errcode) + { + case 10: msg = WARN10; + break; + case 411: msg = ERR411; + break; + case 412: msg = ERR412; + break; + case 421: msg = ERR421; + break; + case 422: msg = ERR422; + break; + case 423: msg = ERR423; + break; + case 434: msg = ERR434; + break; + case 435: msg = ERR435; + break; + case 436: msg = ERR436; + break; + default: msg = ERRERR; + } + + strncpy(dest_msg, msg, MAXMSG); +} + +int validateFile(ENR_Handle p_handle) +// Returns: +// Error code: 435, 436 +// Warning code: 10 +{ + INT4 magic1, magic2, hydcode; + int errorcode = 0; + F_OFF filepos; + data_t* p_data; + + p_data = (data_t*)p_handle; + + // Read magic number from beginning of file + fseek(p_data->file, 0L, SEEK_SET); + fread(&magic1, WORDSIZE, 1, p_data->file); + + // Fast forward to end and read file epilogue + fseek(p_data->file, -3*WORDSIZE, SEEK_END); + fread(&(p_data->nPeriods), WORDSIZE, 1, p_data->file); + fread(&hydcode, WORDSIZE, 1, p_data->file); + fread(&magic2, WORDSIZE, 1, p_data->file); + + filepos = _ftell(p_data->file); + + // Is the file an EPANET binary file? + if (magic1 != magic2) errorcode = 435; + // Does the binary file contain results? + else if (filepos < MINNREC*WORDSIZE || p_data->nPeriods == 0) + errorcode = 436; + // Issue warning if there were problems with the model run. + else if (hydcode != 0) errorcode = 10; + + return errorcode; +} + +float getNodeValue(ENR_Handle p_handle, int periodIndex, int nodeIndex, + int attr) +// +// Purpose: Retrieves an attribute value at a specified node and time +// +{ + F_OFF offset; + REAL4 y; + data_t* p_data; + + p_data = (data_t*)p_handle; + + // calculate byte offset to start time for series + offset = p_data->outputStartPos + periodIndex*p_data->bytesPerPeriod; + // add byte position for attribute and node + offset += ((attr - 1)*p_data->nodeCount + (nodeIndex - 1))*WORDSIZE; + + _fseek(p_data->file, offset, SEEK_SET); + fread(&y, WORDSIZE, 1, p_data->file); + + return y; +} + +float getLinkValue(ENR_Handle p_handle, int periodIndex, int linkIndex, + int attr) +// +// Purpose: Retrieves an attribute value at a specified link and time +// +{ + F_OFF offset; + REAL4 y; + data_t* p_data; + + p_data = (data_t*)p_handle; + + // Calculate byte offset to start time for series + offset = p_data->outputStartPos + periodIndex*p_data->bytesPerPeriod + + (NNODERESULTS*p_data->nodeCount)*WORDSIZE; + // add byte position for attribute and link + offset += ((attr - 1)*p_data->linkCount + (linkIndex - 1))*WORDSIZE; + + _fseek(p_data->file, offset, SEEK_SET); + fread(&y, WORDSIZE, 1, p_data->file); + + return y; +} + +int _fopen(FILE **f, const char *name, const char *mode) { + // + // Purpose: Substitute for fopen_s on platforms where it doesn't exist + // Note: fopen_s is part of C++11 standard + // + int ret = 0; +#ifdef _WIN32 + ret = (int)fopen_s(f, name, mode); +#else + *f = fopen(name, mode); + if (!*f) + ret = -1; +#endif + return ret; +} + +int _fseek(FILE* stream, F_OFF offset, int whence) +// +// Purpose: Selects platform fseek() for large file support +// +{ +#ifdef _WIN32 // Windows (32-bit and 64-bit) +#define FSEEK64 _fseeki64 +#else // Other platforms +#define FSEEK64 fseeko +#endif + + return FSEEK64(stream, offset, whence); +} + +F_OFF _ftell(FILE* stream) +// +// Purpose: Selects platform ftell() for large file support +// +{ +#ifdef _WIN32 // Windows (32-bit and 64-bit) +#define FTELL64 _ftelli64 +#else // Other platforms +#define FTELL64 ftello +#endif + + return FTELL64(stream); +} + +float* newFloatArray(int n) +// +// Warning: Caller must free memory allocated by this function. +// +{ + return (float*) malloc((n)*sizeof(float)); +} + +int* newIntArray(int n) +// +// Warning: Caller must free memory allocated by this function. +// +{ + return (int*) malloc((n)*sizeof(int)); +} + +char* newCharArray(int n) +// +// Warning: Caller must free memory allocated by this function. +// +{ + return (char*) malloc((n)*sizeof(char)); +} diff --git a/tools/epanet-output/src/epanet_output.h b/tools/epanet-output/src/epanet_output.h new file mode 100644 index 0000000..36f826c --- /dev/null +++ b/tools/epanet-output/src/epanet_output.h @@ -0,0 +1,129 @@ +/* + * epanet_output.h - EPANET Output API + * + * Created on: Jun 4, 2014 + * + * Author: Michael E. Tryby + * US EPA - ORD/NRMRL + */ + +#ifndef EPANET_OUTPUT_H_ +#define EPANET_OUTPUT_H_ +/* Epanet Results binary file API */ + +#define MAXFNAME 259 // Max characters in file name +#define MAXID 31 // Max characters in ID name + +// This is an opaque pointer to struct. Do not access variables. +typedef void* ENR_Handle; + +typedef enum { + ENR_node = 1, + ENR_link = 2 +} ENR_ElementType; + +typedef enum { + ENR_getSeries = 1, + ENR_getAttribute = 2, + ENR_getResult = 3, + ENR_getReacts = 4, + ENR_getEnergy = 5 +} ENR_ApiFunction; + +typedef enum { + ENR_flowUnits = 1, + ENR_pressUnits = 2 +} ENR_Units; + +typedef enum { + ENR_reportStart = 1, + ENR_reportStep = 2, + ENR_simDuration = 3, + ENR_numPeriods = 4 +}ENR_Time; + +typedef enum { + ENR_demand = 1, + ENR_head = 2, + ENR_pressure = 3, + ENR_quality = 4 +} ENR_NodeAttribute; + +typedef enum { + ENR_flow = 1, + ENR_velocity = 2, + ENR_headloss = 3, + ENR_avgQuality = 4, + ENR_status = 5, + ENR_setting = 6, + ENR_rxRate = 7, + ENR_frctnFctr = 8 +} ENR_LinkAttribute; + + +#ifdef WINDOWS +#ifdef __cplusplus +#define DLLEXPORT __declspec(dllexport) __cdecl +#else +#define DLLEXPORT __declspec(dllexport) __stdcall +#endif +#else +#define DLLEXPORT +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +int DLLEXPORT ENR_init(ENR_Handle* p_handle_out); + +int DLLEXPORT ENR_open(ENR_Handle p_handle_in, const char* path); + +int DLLEXPORT ENR_getVersion(ENR_Handle p_handle_in, int* int_out); + +int DLLEXPORT ENR_getNetSize(ENR_Handle p_handle_in, int** int_out, int* int_dim); + +int DLLEXPORT ENR_getUnits(ENR_Handle p_handle_in, ENR_Units t_enum, int* int_out); + +int DLLEXPORT ENR_getTimes(ENR_Handle p_handle_in, ENR_Time t_enum, int* int_out); + +int DLLEXPORT ENR_getElementName(ENR_Handle p_handle_in, ENR_ElementType t_enum, + int elementIndex, char** string_out, int* slen); + +int DLLEXPORT ENR_getEnergyUsage(ENR_Handle p_handle_in, int pumpIndex, + int* int_out, float** float_out, int* int_dim); + +int DLLEXPORT ENR_getNetReacts(ENR_Handle p_handle_in, float** float_out, int* int_dim); + + +int DLLEXPORT ENR_getNodeSeries(ENR_Handle p_handle_in, int nodeIndex, ENR_NodeAttribute t_enum, + int startPeriod, int endPeriod, float** outValueSeries, int* dim); + +int DLLEXPORT ENR_getLinkSeries(ENR_Handle p_handle_in, int linkIndex, ENR_LinkAttribute t_enum, + int startPeriod, int endPeriod, float** outValueSeries, int* dim); + +int DLLEXPORT ENR_getNodeAttribute(ENR_Handle p_handle_in, int periodIndex, + ENR_NodeAttribute t_enum, float** outValueArray, int* dim); + +int DLLEXPORT ENR_getLinkAttribute(ENR_Handle p_handle_in, int periodIndex, + ENR_LinkAttribute t_enum, float** outValueArray, int* dim); + +int DLLEXPORT ENR_getNodeResult(ENR_Handle p_handle_in, int periodIndex, int nodeIndex, + float** float_out, int* int_dim); + +int DLLEXPORT ENR_getLinkResult(ENR_Handle p_handle_in, int periodIndex, int linkIndex, + float** float_out, int* int_dim); + +int DLLEXPORT ENR_close(ENR_Handle* p_handle_out); + +void DLLEXPORT ENR_free(void** array); + +void DLLEXPORT ENR_clearError(ENR_Handle p_handle_in); + +int DLLEXPORT ENR_checkError(ENR_Handle p_handle_in, char** msg_buffer); + +#ifdef __cplusplus +} +#endif + +#endif /* EPANET_OUTPUT_H_ */ diff --git a/tools/epanet-output/src/epanet_output.i b/tools/epanet-output/src/epanet_output.i new file mode 100644 index 0000000..d9eb3e1 --- /dev/null +++ b/tools/epanet-output/src/epanet_output.i @@ -0,0 +1,254 @@ +/* + * epanet_output.i - SWIG interface description file for EPANET Output API + * + * Created: 9/20/2017 + * + * Author: Michael E. Tryby + * US EPA - ORD/NRMRL + * +*/ +%module epanet_output +%{ +#include "errormanager.h" +#include "messages.h" +#include "epanet_output.h" + +#define SWIG_FILE_WITH_INIT +%} + +%include "typemaps.i" + +/* DEFINE AND TYPEDEF MUST BE INCLUDED */ +#define MAXMSG 53 + +typedef void* ENR_Handle; + +typedef enum { + ENR_node = 1, + ENR_link = 2 +} ENR_ElementType; + +/* +typedef enum { + ENR_nodeCount = 1, + ENR_tankCount = 2, + ENR_linkCount = 3, + ENR_pumpCount = 4, + ENR_valveCount = 5 +} ENR_ElementCount; +*/ + +typedef enum { + ENR_flowUnits = 1, + ENR_pressUnits = 2 +} ENR_Units; + +typedef enum { + ENR_reportStart = 1, + ENR_reportStep = 2, + ENR_simDuration = 3, + ENR_numPeriods = 4 +}ENR_Time; + +typedef enum { + ENR_demand = 1, + ENR_head = 2, + ENR_pressure = 3, + ENR_quality = 4 +} ENR_NodeAttribute; + +typedef enum { + ENR_flow = 1, + ENR_velocity = 2, + ENR_headloss = 3, + ENR_avgQuality = 4, + ENR_status = 5, + ENR_setting = 6, + ENR_rxRate = 7, + ENR_frctnFctr = 8 +} ENR_LinkAttribute; + +#ifdef WINDOWS + #ifdef __cplusplus + #define DLLEXPORT __declspec(dllexport) __cdecl + #else + #define DLLEXPORT __declspec(dllexport) __stdcall + #endif +#else + #define DLLEXPORT +#endif + +/* TYPEMAPS FOR OPAQUE POINTER */ +/* Used for functions that output a new opaque pointer */ +%typemap(in, numinputs=0) ENR_Handle* p_handle_out (ENR_Handle retval) +{ + /* OUTPUT in */ + retval = NULL; + $1 = &retval; +} +/* used for functions that take in an opaque pointer (or NULL) +and return a (possibly) different pointer */ +%typemap(argout) ENR_Handle* p_handle_out +{ + /* OUTPUT argout */ + %append_output(SWIG_NewPointerObj(SWIG_as_voidptr(retval$argnum), $1_descriptor, 0)); +} +/* No need for special IN typemap for opaque pointers, it works anyway */ + + +/* TYPEMAP FOR IGNORING INT ERROR CODE RETURN VALUE */ +%typemap(out) int { + $result = Py_None; + Py_INCREF($result); +} + +/* TYPEMAPS FOR INT ARGUMENT AS RETURN VALUE */ +%typemap(in, numinputs=0) int* int_out (int temp) { + $1 = &temp; +} +%typemap(argout) int* int_out { + %append_output(PyInt_FromLong(*$1)); +} + +/* TYPEMAP FOR MEMORY MANAGEMENT AND ENCODING OF STRINGS */ +%typemap(in, numinputs=0)char** string_out (char* temp), int* slen (int temp){ + $1 = &temp; +} +%typemap(argout)(char** string_out, int* slen) { + if (*$1) { + PyObject* o; + o = PyUnicode_FromStringAndSize(*$1, *$2); + + $result = SWIG_Python_AppendOutput($result, o); + free(*$1); + } +} + +/* TYPEMAPS FOR MEMORY MANAGEMNET OF FLOAT ARRAYS */ +%typemap(in, numinputs=0)float** float_out (float* temp), int* int_dim (int temp){ + $1 = &temp; +} +%typemap(argout) (float** float_out, int* int_dim) { + if (*$1) { + PyObject *o = PyList_New(*$2); + int i; + float* temp = *$1; + for(i=0; i<*$2; i++) { + PyList_SetItem(o, i, PyFloat_FromDouble((double)temp[i])); + } + $result = SWIG_Python_AppendOutput($result, o); + free(*$1); + } +} + +/* TYPEMAPS FOR MEMORY MANAGEMENT OF INT ARRAYS */ +%typemap(in, numinputs=0)int** int_out (long* temp), int* int_dim (int temp){ + $1 = &temp; +} +%typemap(argout) (int** int_out, int* int_dim) { + if (*$1) { + PyObject *o = PyList_New(*$2); + int i; + long* temp = *$1; + for(i=0; i<*$2; i++) { + PyList_SetItem(o, i, PyInt_FromLong(temp[i])); + } + $result = SWIG_Python_AppendOutput($result, o); + free(*$1); + } +} + +/* TYPEMAP FOR ENUMERATED TYPES */ +%typemap(in) EnumeratedType (int val, int ecode = 0) { + if (PyObject_HasAttrString($input,"value")) { + PyObject* o; + o = PyObject_GetAttrString($input, "value"); + ecode = SWIG_AsVal_int(o, &val); + } + else { + SWIG_exception_fail(SWIG_ArgError(ecode), "in method '" "$symname" "', argument " "$argnum"" of type '" "$ltype""'"); + } + + $1 = ($1_type)(val); +} +%apply EnumeratedType {ENR_ElementType, ENR_Units, ENR_Time, ENR_NodeAttribute, ENR_LinkAttribute} + + +/* RENAME FUNCTIONS PYTHON STYLE */ +%rename("%(undercase)s") ""; + +/* INSERTS CUSTOM EXCEPTION HANDLING IN WRAPPER */ +%exception +{ + char* err_msg; + ENR_clearError(arg1); + $function + if (ENR_checkError(arg1, &err_msg)) + { + PyErr_SetString(PyExc_Exception, err_msg); + SWIG_fail; + } +} +/* INSERT EXCEPTION HANDLING FOR THESE FUNCTIONS */ +int DLLEXPORT ENR_open(ENR_Handle p_handle, const char* path); + +int DLLEXPORT ENR_getVersion(ENR_Handle p_handle, int* int_out); +int DLLEXPORT ENR_getNetSize(ENR_Handle p_handle, int** int_out, int* int_dim); +int DLLEXPORT ENR_getUnits(ENR_Handle p_handle, ENR_Units t_enum, int* int_out); +int DLLEXPORT ENR_getTimes(ENR_Handle p_handle, ENR_Time t_enum, int* int_out); +int DLLEXPORT ENR_getElementName(ENR_Handle p_handle, ENR_ElementType t_enum, + int elementIndex, char** string_out, int* slen); +int DLLEXPORT ENR_getEnergyUsage(ENR_Handle p_handle, int pumpIndex, + int* int_out, float** float_out, int* int_dim); +int DLLEXPORT ENR_getNetReacts(ENR_Handle p_handle, float** float_out, int* int_dim); + + +int DLLEXPORT ENR_getNodeAttribute(ENR_Handle p_handle, int periodIndex, + ENR_NodeAttribute t_enum, float** float_out, int* int_dim); +int DLLEXPORT ENR_getLinkAttribute(ENR_Handle p_handle, int periodIndex, + ENR_LinkAttribute t_enum, float** float_out, int* int_dim); +%exception; + +/* NO EXCEPTION HANDLING FOR THESE FUNCTIONS */ +int DLLEXPORT ENR_init(ENR_Handle* p_handle_out); +int DLLEXPORT ENR_close(ENR_Handle* p_handle_out); +void DLLEXPORT ENR_free(void** array); + +void DLLEXPORT ENR_clearError(ENR_Handle p_handle); +int DLLEXPORT ENR_checkError(ENR_Handle p_handle, char** msg_buffer); + + +/* CODE ADDED DIRECTLY TO SWIGGED INTERFACE MODULE */ +%pythoncode%{ +import enum + +class ElementType(enum.Enum): + NODE = ENR_node + LINK = ENR_link + +class Units(enum.Enum): + FLOW_UNIT = ENR_flowUnits + PRESS_UNIT = ENR_pressUnits + +class Time(enum.Enum): + REPORT_START = ENR_reportStart + REPORT_STEP = ENR_reportStep + SIM_DURATION = ENR_simDuration + NUM_PERIODS = ENR_numPeriods + +class NodeAttribute(enum.Enum): + DEMAND = ENR_demand + HEAD = ENR_head + PRESSURE = ENR_pressure + QUALITY = ENR_quality + +class LinkAttribute(enum.Enum): + FLOW = ENR_flow + VELOCITY = ENR_velocity + HEADLOSS = ENR_headloss + AVG_QUALITY = ENR_avgQuality + STATUS = ENR_status + SETTING = ENR_setting + RX_RATE = ENR_rxRate + FRCTN_FCTR = ENR_frctnFctr +%} diff --git a/tools/epanet-output/src/errormanager.c b/tools/epanet-output/src/errormanager.c new file mode 100644 index 0000000..49e41db --- /dev/null +++ b/tools/epanet-output/src/errormanager.c @@ -0,0 +1,74 @@ +//----------------------------------------------------------------------------- +// +// errormanager.c +// +// Purpose: Provides a simple interface for managing runtime error messages. +// +// Date: 08/25/2017 +// +// Author: Michael E. Tryby +// US EPA - ORD/NRMRL +//----------------------------------------------------------------------------- +#include +#include +#include "errormanager.h" + +error_handle_t* new_errormanager(void (*p_error_message)(int, char*, int)) +// +// Purpose: Constructs a new error handle. +// +{ + error_handle_t* error_handle; + error_handle = (error_handle_t*)calloc(1, sizeof(error_handle_t)); + + error_handle->p_msg_lookup = p_error_message; + + return error_handle; +} + +void dst_errormanager(error_handle_t* error_handle) +// +// Purpose: Destroys the error handle. +// +{ + free(error_handle); +} + +int set_error(error_handle_t* error_handle, int errorcode) +// +// Purpose: Sets an error code in the handle. +// +{ + // If the error code is 0 no action is taken and 0 is returned. + // This is a feature not a bug. + if (errorcode) + error_handle->error_status = errorcode; + + return errorcode; +} + +char* check_error(error_handle_t* error_handle) +// +// Purpose: Returns the error message or NULL. +// +// Note: Caller must free memory allocated by check_error +// +{ + char* temp = NULL; + + if (error_handle->error_status != 0) { + temp = (char*) calloc(ERR_MAXMSG, sizeof(char)); + + if (temp) + error_handle->p_msg_lookup(error_handle->error_status, temp, ERR_MAXMSG); + } + return temp; +} + +void clear_error(error_handle_t* error_handle) +// +// Purpose: Clears the error from the handle. +// +{ + error_handle->error_status = 0; +} diff --git a/tools/epanet-output/src/errormanager.h b/tools/epanet-output/src/errormanager.h new file mode 100644 index 0000000..d62b6a3 --- /dev/null +++ b/tools/epanet-output/src/errormanager.h @@ -0,0 +1,27 @@ +/* + * errormanager.h + * + * Created on: Aug 25, 2017 + * + * Author: Michael E. Tryby + * US EPA - ORD/NRMRL + */ + +#ifndef ERRORMANAGER_H_ +#define ERRORMANAGER_H_ + +#define ERR_MAXMSG 256 + +typedef struct error_s { + int error_status; + void (*p_msg_lookup)(int, char*, int); +} error_handle_t; + +error_handle_t* new_errormanager(void (*p_error_message)(int, char*, int)); +void dst_errormanager(error_handle_t* error_handle); + +int set_error(error_handle_t* error_handle, int errorcode); +char* check_error(error_handle_t* error_handle); +void clear_error(error_handle_t* error_handle); + +#endif /* ERRORMANAGER_H_ */ diff --git a/tools/epanet-output/src/messages.h b/tools/epanet-output/src/messages.h new file mode 100644 index 0000000..78725a1 --- /dev/null +++ b/tools/epanet-output/src/messages.h @@ -0,0 +1,29 @@ +/* + * messages.h - EPANET + * + * Created on: June 1, 2017 + * + * Author: Michael E. Tryby + * US EPA - ORD/NRMRL + */ + +#ifndef MESSAGES_H_ +#define MESSAGES_H_ +/*------------------- Error Messages --------------------*/ +#define MAXMSG 53 + +#define WARN10 "Warning: model run issued warnings" + +#define ERR411 "Input Error 411: no memory allocated for results" +#define ERR412 "Input Error 412: binary file hasn't been opened" +#define ERR421 "Input Error 421: invalid parameter code" +#define ERR422 "Input Error 422: reporting period index out of range" +#define ERR423 "Input Error 423: element index out of range" + +#define ERR434 "File Error 434: unable to open binary file" +#define ERR435 "File Error 435: invalid binary file type" +#define ERR436 "File Error 436: no results in binary file" + +#define ERRERR "Error: An unknown error has occurred" + +#endif /* MESSAGES_H_ */ diff --git a/tools/epanet-output/test/data/__init__.py b/tools/epanet-output/test/data/__init__.py new file mode 100644 index 0000000..ca2e575 --- /dev/null +++ b/tools/epanet-output/test/data/__init__.py @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- + +# __init__.py +# +# Created: 11/13/2017 +# Author: Michael E. Tryby +# US EPA - ORD/NRMRL +# + +import os + +DATA_PATH = os.path.abspath(os.path.dirname(__file__)) + +OUTPUT_FILE_EXAMPLE1 = os.path.join(DATA_PATH, 'net1.out') diff --git a/tools/epanet-output/test/data/net1.out b/tools/epanet-output/test/data/net1.out new file mode 100644 index 0000000000000000000000000000000000000000..6cede26b23767e4bd91a30d2ccaba14f34904ba3 GIT binary patch literal 16832 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 VZ{1BMO!v?feK@&oH21$7{s%(sdMW?_ literal 0 HcmV?d00001 diff --git a/tools/epanet-output/test/epanet_output_test.py b/tools/epanet-output/test/epanet_output_test.py new file mode 100644 index 0000000..fa23d62 --- /dev/null +++ b/tools/epanet-output/test/epanet_output_test.py @@ -0,0 +1,102 @@ +# +# epanet_output_test.py +# +# Created: 11/8/2017 +# Author: Michael E. Tryby +# US EPA - ORD/NRMRL +# +# Unit testing for EPANET Output API using pytest. +# + +import pytest +import numpy as np + +import epanet_output as oapi + +from data import OUTPUT_FILE_EXAMPLE1 + +@pytest.fixture() +def enr_handle(request): + _handle = oapi.enr_init() + oapi.enr_open(_handle, OUTPUT_FILE_EXAMPLE1) + + def enr_close(): + oapi.enr_close() + + request.addfinalizer(enr_close) + return _handle + + +def test_get_times(enr_handle): + num_periods = oapi.enr_get_times(enr_handle, oapi.Time.NUM_PERIODS) + assert num_periods == 25 + + +# def test_get_size(file_path): +# handle = oapi.enr_init() +# oapi.enr_open(handle, file_path) +# +# size = oapi.enr_get_net_size(handle) +# +# print(size) +# +# handle = oapi.enr_close() +# +# def test_get_names(file_path): +# handle = oapi.enr_init() +# oapi.enr_open(handle, file_path) +# +# name = oapi.enr_get_element_name(handle, oapi.ElementType.NODE, 10) +# +# print(name) +# +# handle = oapi.enr_close() +# +# def test_get_energy(file_path): +# handle = oapi.enr_init() +# oapi.enr_open(handle, file_path) +# +# result = oapi.enr_get_energy_usage(handle, 1) +# +# print(result) +# +# handle = oapi.enr_close() +# +# def test_get_react(file_path): +# handle = oapi.enr_init() +# oapi.enr_open(handle, file_path) +# +# result = oapi.enr_get_net_reacts(handle) +# +# print(result) +# +# handle = oapi.enr_close() +# +def test_get_node_attribute(enr_handle): + ref_array = np.array([ 1., 0.44407997, 0.43766347, 0.42827705, 0.41342604, + 0.42804748, 0.44152543, 0.40502965, 0.38635802, 1., 0.96745253]) + + array = oapi.enr_get_node_attribute(enr_handle, 1, oapi.NodeAttribute.QUALITY) + assert len(array) == 11 + assert np.allclose(array, ref_array) + +def test_get_link_attribute(enr_handle): + ref_array = np.array([ 1848.58117676, 1220.42736816, 130.11161804, + 187.68930054, 119.88839722, 40.46448898, -748.58111572, 478.15377808, + 191.73458862, 30.11160851, 140.4644928, 59.53551483, 1848.58117676]) + + array = oapi.enr_get_link_attribute(enr_handle, 1, oapi.LinkAttribute.FLOW) + assert len(array) == 13 + assert np.allclose(array, ref_array) + +# if __name__ == "__main__": +# +# file_path = "M:\\net mydocuments\\EPA Projects\\EPAnet Examples\\net1.out" +# test_get_times(file_path) +# test_get_size(file_path) +# test_get_names(file_path) +# test_get_energy(file_path) +# test_get_react(file_path) +# test_get_node_attribute(file_path) +# test_get_link_attribute(file_path) +# \ No newline at end of file diff --git a/tools/epanet-output/test/test_epanet_output.cpp b/tools/epanet-output/test/test_epanet_output.cpp new file mode 100644 index 0000000..ed4f79d --- /dev/null +++ b/tools/epanet-output/test/test_epanet_output.cpp @@ -0,0 +1,251 @@ +/* + * test_epanet_output.cpp + * + * Created: 8/4/2017 + * Author: Michael E. Tryby + * US EPA - ORD/NRMRL + * + * Unit testing for EPANET Output API using google test. +*/ + +#include +#include +#include +#include "gtest/gtest.h" + +#include "../src/epanet_output.h" + +#define PROJECT_HOME "C:/Users/mtryby/Workspace/GitRepo/michaeltryby/epanet/" +#define DATA_PATH "tools/epanet-output/test/data/net1.out" + + +namespace { + +TEST(ENR_init, InitTest) { + ENR_Handle p_handle; + + int error = ENR_init(&p_handle); + ASSERT_EQ(0, error); + ASSERT_TRUE(p_handle != NULL); +} + +TEST(ENR_open, OpenTest) { + std::string path = std::string(PROJECT_HOME) + std::string(DATA_PATH); + ENR_Handle p_handle; + ENR_init(&p_handle); + + int error = ENR_open(p_handle, path.c_str()); + ASSERT_EQ(0, error); + ENR_close(&p_handle); +} + +TEST(ENR_close, CloseTest) { + ENR_Handle p_handle; + int error = ENR_init(&p_handle); + + error = ENR_close(&p_handle); + ASSERT_EQ(-1, error); + ASSERT_TRUE(p_handle != NULL); +} + + + +class OutputapiTest : public testing::Test { +protected: + // SetUp for OutputapiTest fixture + virtual void SetUp() { + std::string path = std::string(PROJECT_HOME) + std::string(DATA_PATH); + + error = ENR_init(&p_handle); + ENR_clearError(p_handle); + error = ENR_open(p_handle, path.c_str()); + } + + // TearDown for OutputapiTest fixture + virtual void TearDown() { + ENR_free((void**)&array); + error = ENR_close(&p_handle); + } + + int error = 0; + ENR_Handle p_handle = NULL; + + float* array = NULL; + int array_dim = 0; +}; + +TEST_F(OutputapiTest, getNetSizeTest) { + int* i_array = NULL; + // nodes, tanks, links, pumps, valves + int ref_array[5] = {11,2,13,1,0}; + + error = ENR_getNetSize(p_handle, &i_array, &array_dim); + ASSERT_EQ(0, error); + + for (int i = 0; i < array_dim; i++) + EXPECT_EQ(ref_array[i], i_array[i]); + + ENR_free((void**)&i_array); +} + +TEST_F(OutputapiTest, getElementName) { + char* name = new char[MAXID]; + int length, index = 1; + + error = ENR_getElementName(p_handle, ENR_node, index, &name, &length); + ASSERT_EQ(0, error); + + EXPECT_STREQ("10", name); + + delete(name); +} + +TEST_F(OutputapiTest, getNodeAttributeTest) { + float ref_array[11] = { 1.0, + 0.44407997, + 0.43766347, + 0.42827705, + 0.41342604, + 0.42804748, + 0.44152543, + 0.40502965, + 0.38635802, + 1.0, + 0.96745253 }; + + error = ENR_getNodeAttribute(p_handle, 1, ENR_quality, &array, &array_dim); + ASSERT_EQ(0, error); + + for (int i = 0; i < array_dim; i++) + EXPECT_FLOAT_EQ(ref_array[i], array[i]); +} + +TEST_F(OutputapiTest, getLinkAttributeTest) { + float ref_array[13] = { 1848.5812, + 1220.4274, + 130.11162, + 187.6893, + 119.8884, + 40.464489, + -748.58112, + 478.15378, + 191.73459, + 30.111609, + 140.46449, + 59.535515, + 1848.5812}; + + error = ENR_getLinkAttribute(p_handle, 1, ENR_flow, &array ,&array_dim); + ASSERT_EQ(0, error); + + for (int i = 0; i < array_dim; i++) + EXPECT_FLOAT_EQ(ref_array[i], array[i]); +} + +TEST_F(OutputapiTest, getNodeResultTest) { + float ref_array[4] = {0.041142918, + 150.0, + 987.98358, + 120.45029}; + + error = ENR_getNodeResult(p_handle, 1, 2, &array, &array_dim); + ASSERT_EQ(0, error); + + for (int i = 0; i < array_dim; i++) + EXPECT_FLOAT_EQ(ref_array[i], array[i]); +} + +TEST_F(OutputapiTest, getLinkResultTest) { + float ref_array[8] = {0.58586824, + 1892.2433, + 0.0, + -200.71875, + 1.0, + 3.0, + 1.0, + 0.0}; + + error = ENR_getLinkResult(p_handle, 24, 13, &array, &array_dim); + ASSERT_EQ(0, error); + + for (int i = 0; i < array_dim; i++) + EXPECT_FLOAT_EQ(ref_array[i], array[i]); +} + +TEST_F(OutputapiTest, getNodeSeriesTest){ + float ref_array[10] = {119.25731, + 120.45029, + 121.19854, + 122.00622, + 122.37414, + 122.8122, + 122.82034, + 122.90379, + 123.40434, + 123.81807}; + + error = ENR_getNodeSeries(p_handle, 2, ENR_pressure, 0, 10, &array, &array_dim); + ASSERT_EQ(0, error); + + for (int i = 0; i < array_dim; i++) + EXPECT_FLOAT_EQ(ref_array[i], array[i]); +} + +TEST_F(OutputapiTest, getLinkSeriesTest) { + float ref_array[10] = {1234.2072, + 1220.4274, + 1164.4, + 1154.8175, + 1100.0635, + 1094.759, + 1041.7854, + 1040.7617, + 1087.556, + 1082.5011}; + + error = ENR_getLinkSeries(p_handle, 2, ENR_flow, 0, 10, &array, &array_dim); + ASSERT_EQ(0, error); + + for (int i = 0; i < array_dim; i++) + EXPECT_FLOAT_EQ(ref_array[i], array[i]); +} + +TEST_F(OutputapiTest, getNetReactsTest) { + float ref_array[4] = {18806.59, + 85424.438, + 115174.05, + 238972.66}; + + error = ENR_getNetReacts(p_handle, &array, &array_dim); + ASSERT_EQ(0, error); + + for (int i = 0; i < array_dim; i++) + EXPECT_FLOAT_EQ(ref_array[i], array[i]); +} + +TEST_F(OutputapiTest, getEnergyUsageTest) { + float ref_array[6] = {57.712959, + 75.0, + 880.41583, + 96.254318, + 96.707115, + 0.0}; + + int linkIdx; + + error = ENR_getEnergyUsage(p_handle, 1, &linkIdx, &array, &array_dim); + ASSERT_EQ(0, error); + + for (int i = 0; i < array_dim; i++) + EXPECT_FLOAT_EQ(ref_array[i], array[i]); +} + +} + +GTEST_API_ int main(int argc, char **argv) { + + printf("Running main() from gtest_main.cc\n"); + testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/tools/gen-config.sh b/tools/gen-config.sh new file mode 100755 index 0000000..a937054 --- /dev/null +++ b/tools/gen-config.sh @@ -0,0 +1,46 @@ +#! /bin/bash + +# +# gen-config.sh - Generates nrtest app configuration file for test executable +# +# Date Created: 10/16/2017 +# +# Author: Michael E. Tryby +# US EPA - ORD/NRMRL +# +# Arguments: +# 1 - absolute path to test executable +# +# NOT IMPLEMENTED YET +# 2 - test executable version number +# 3 - build description +# + +unameOut="$(uname -s)" +case "${unameOut}" in + Linux*) ;& + Darwin*) abs_build_path=$1 + test_cmd="runepanet" + ;; + + MINGW*) ;& + MSYS*) # Remove leading '/c' from file path for nrtest + abs_build_path="$( echo "$1" | sed -e 's#/c##' )" + test_cmd="runepanet.exe" + ;; + + *) # Machine unknown +esac + +version="" +build_description="" + +cat< 0: + print('%d differences found\n' % notclose) + isclose = False + + return isclose + + + +from nrtest.testsuite import TestSuite +from nrtest.compare import compare_testsuite, validate_testsuite + +def nrtest_compare(path_test, path_ref, (comp_args)): + + ts_new = TestSuite.read_benchmark(path_test) + ts_old = TestSuite.read_benchmark(path_ref) + + if not validate_testsuite(ts_new) or not validate_testsuite(ts_old): + exit(1) + + try: +# logging.info('Found %i tests' % len(ts_new.tests)) + compatible = compare_testsuite(ts_new, ts_old, comp_args[0], comp_args[1]) + except KeyboardInterrupt: +# logging.warning('Process interrupted by user') + compatible = False +# else: +# logging.info('Finished') + + # Non-zero exit code indicates failure + exit(not compatible) + +def nrtest_execute(app_path, test_path, output_path): + import logging + import glob + from os import listdir + from os.path import exists, isfile, isdir, join + from nrtest.execute import execute_testsuite, validate_testsuite + +# for path in test_path + [app_path]: +# if not exists(path): +# logging.error('Could not find path: "%s"' % path) + + test_dirs = glob.glob(test_path) + test_files = [p for p in test_path if isfile(p)] + test_files += [p for d in test_dirs for p in glob.glob(d + '*.json')] +# if p.endswith('.json')] + + test_files = list(set(test_files)) # remove duplicates + + ts = TestSuite.read_config(app_path, test_files, output_path) + + if not validate_testsuite(ts): + exit(1) + + try: + logging.info('Found %i tests' % len(test_files)) + success = execute_testsuite(ts) + ts.write_manifest() + except KeyboardInterrupt: + logging.warning('Process interrupted by user') + success = False + else: + logging.info('Finished') + + # Non-zero exit code indicates failure + exit(not success) + + +if __name__ == "__main__": +# app_path = "apps\\swmm-5.1.11.json" +# test_path = "tests\\examples\\example1.json" +# output_path = "benchmarks\\test\\" +# nrtest_execute(app_path, test_path, output_path) + +# test_path = "C:\\Users\\mtryby\\Workspace\\GitRepo\\Local\\epanet-testsuite\\benchmarks\\v2011a" +# ref_path = "C:\\Users\\mtryby\\Workspace\\GitRepo\\Local\\epanet-testsuite\\benchmarks\\v2012" +# print(nrtest_compare(test_path, ref_path, (0.001, 0.0))) + + + path_test = "C:\\Users\\mtryby\\Workspace\\GitRepo\\Local\\epanet-testsuite\\benchmarks\\v2011a\\Example_3\\example3.out" + path_ref = "C:\\Users\\mtryby\\Workspace\\GitRepo\\Local\\epanet-testsuite\\benchmarks\\v2012\\Example_3\\example3.out" + result_compare(path_test, path_ref, (0.001, 0.0)) diff --git a/tools/nrtest-epanet/nrtest_epanet/__init__.py b/tools/nrtest-epanet/nrtest_epanet/__init__.py new file mode 100644 index 0000000..3ed86d6 --- /dev/null +++ b/tools/nrtest-epanet/nrtest_epanet/__init__.py @@ -0,0 +1,121 @@ +# -*- coding: utf-8 -*- + +# +# __init__.py - nrtest_epanet module +# +# Author: Michael E. Tryby +# US EPA - ORD/NRMRL +# + +''' +Numerical regression testing (nrtest) plugin for comparing EPANET binary results +files and EPANET text based report files. +''' + +# system imports +import itertools as it + +# third party imports +import header_detail_footer as hdf +import numpy as np + +# project import +import nrtest_epanet.output_reader as ordr + + +__author__ = "Michael Tryby" +__copyright__ = "None" +__credits__ = "Colleen Barr, Maurizio Cingi, Mark Gray, David Hall, Bryant McDonnell" +__license__ = "CC0 1.0 Universal" + +__version__ = "0.4.0" +__date__ = "September 6, 2017" + +__maintainer__ = "Michael Tryby" +__email__ = "tryby.michael@epa.gov" +__status = "Development" + + +def epanet_allclose_compare(path_test, path_ref, rtol, atol): + ''' + Compares results in two EPANET binary files. Using the comparison criteria + described in the numpy assert_allclose documentation. + + (test_value - ref_value) <= atol + rtol * abs(ref_value) + + Returns true if all of the results in the two binary files meet the + comparison criteria; otherwise, an AssertionError is thrown. + + Numpy allclose is quite expensive to evaluate. Test and reference results + are checked to see if they are equal before being compared using the + allclose criteria. This reduces comparison times significantly. + + Arguments: + path_test - path to result file being tested + path_ref - path to reference result file + rtol - relative tolerance + atol - absolute tolerance + + Returns: + True + + Raises: + ValueError() + AssertionError() + ... + ''' + for (test, ref) in it.izip(ordr.output_generator(path_test), + ordr.output_generator(path_ref)): + + if len(test) != len(ref): + raise ValueError('Inconsistent lengths') + + # Skip over arrays that are equal + if np.array_equal(test, ref): + continue + else: + np.testing.assert_allclose(test, ref, rtol, atol) + + return True + +# def epanet_better_compare(path_test, path_ref, rtol, atol): +# ''' +# If you don't like assert_allclose you can add another function here. +# ''' +# pass + +def epanet_report_compare(path_test, path_ref, rtol, atol): + ''' + Compares results in two report files ignoring contents of header and footer. + + Note: Header is 11 lines with report summary turned off. This test will fail + if the report summary is turned on because a time stamp is being written + immediately after it. + + Arguments: + path_test - path to result file being tested + path_ref - path to reference result file + rtol - ignored + atol - ignored + + Returns: + True or False + + Raises: + HeaderError() + FooterError() + RunTimeError() + ... + ''' + HEADER = 11 + FOOTER = 3 + + with open(path_test ,'r') as ftest, open(path_ref, 'r') as fref: + + for (test_line, ref_line) in it.izip(hdf.parse(ftest, HEADER, FOOTER)[1], + hdf.parse(fref, HEADER, FOOTER)[1]): + + if test_line != ref_line: + return False + + return True diff --git a/tools/nrtest-epanet/nrtest_epanet/output_reader.py b/tools/nrtest-epanet/nrtest_epanet/output_reader.py new file mode 100644 index 0000000..0bf640f --- /dev/null +++ b/tools/nrtest-epanet/nrtest_epanet/output_reader.py @@ -0,0 +1,69 @@ +# -*- coding: utf-8 -*- + +# +# output_reader.py +# +# Date Created: Aug 31, 2016 +# +# Author: Michael E. Tryby +# US EPA - ORD/NRMRL +# +''' +The module output_reader provides the class used to implement the output +generator. +''' + +# project import +import epanet_output as oapi + + +def output_generator(path_ref): + ''' + The output_generator is designed to iterate over an EPANET binary file and + yield element attributes. It is useful for comparing contents of binary + files for numerical regression testing. + + The generator yields a Python list containing element attributes. + + Arguments: + path_ref - path to result file + + Raises: + Exception() + ... + ''' + with OutputReader(path_ref) as br: + + for period_index in range(0, br.report_periods()): + for element_type in oapi.ElementType: + for attribute in br.elementAttributes[element_type]: + + yield br.element_attribute(element_type, period_index, attribute) + + +class OutputReader(): + ''' + Provides a minimal API used to implement output_generator. + ''' + def __init__(self, filename): + self.filepath = filename + self.handle = None + self.elementAttributes = {oapi.ElementType.NODE: oapi.NodeAttribute, + oapi.ElementType.LINK: oapi.LinkAttribute} + + self.getElementAttribute = {oapi.ElementType.NODE: oapi.enr_get_node_attribute, + oapi.ElementType.LINK: oapi.enr_get_link_attribute} + + def __enter__(self): + self.handle = oapi.enr_init() + oapi.enr_open(self.handle, self.filepath.encode()) + return self + + def __exit__(self, type, value, traceback): + self.handle = oapi.enr_close() + + def report_periods(self): + return oapi.enr_get_times(self.handle, oapi.Time.NUM_PERIODS) + + def element_attribute(self, element_type, time_index, attribute): + return self.getElementAttribute[element_type](self.handle, time_index, attribute) diff --git a/tools/nrtest-epanet/setup.py b/tools/nrtest-epanet/setup.py new file mode 100644 index 0000000..0318ab2 --- /dev/null +++ b/tools/nrtest-epanet/setup.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- + +# +# setup.py +# +# Created on Aug 30, 2016 +# Author: Michael E. Tryby +# US EPA - ORD/NRMRL +# +''' Setup up script for nrtest_epanet package. ''' + +try: + from setuptools import setup +except ImportError: + from distutils.core import setup + +entry_points = { + 'nrtest.compare': [ + 'epanet allclose = nrtest_epanet:epanet_allclose_compare', + 'epanet report = nrtest_epanet:epanet_report_compare', + # Add entry point for new comparison functions here + ] +} + +setup( + name='nrtest-epanet', + version='0.3.0', + description="EPANET extension for nrtest", + + author="Michael E. Tryby", + author_email='tryby.michael@epa.gov', + url='https://github.com/USEPA', + + packages=['nrtest_epanet',], + entry_points=entry_points, + + install_requires=[ + 'header_detail_footer>=2.3', + 'nrtest>=0.2.0', + 'numpy>=1.7.0', + 'epanet_output>=0.4.0' + ], + keywords='nrtest_epanet' +) diff --git a/tools/outputapi/ENBinaryOutDiff.py b/tools/outputapi/ENBinaryOutDiff.py deleted file mode 100644 index 9754a2a..0000000 --- a/tools/outputapi/ENBinaryOutDiff.py +++ /dev/null @@ -1 +0,0 @@ -''' Compares Two EPANET binary output files. Author: Bryant E. McDonnell Date: 12/16/2015 Compares the absolute value of two values against a given threshold value. ******************* Command Line Arguments: python <*.out 1> <*.out 2> Returns True / False (Pass / Fail, respectively) ******************* ''' import sys import os from math import log from ENOutputWrapper import * def BinCompare(args): # Some Error Checking for Command Line Arguments.... if len(args) < 3: raise Exception("Not Enough Input Arguments: python <*.out 1> <*.out 2>") # if not args[1].endswith('.out') or not args[2].endswith('.out'): # raise Exception("Wrong file extension: python <*.out 1> <*.out 2>") print(sys.argv[1],sys.argv[2]) dllLoc = '' if (sys.platform == 'linux2' or sys.platform == 'darwin'): dllLoc = os.getcwd() + '/libENBinaryOut.so' else: raise Exception("only implemented on mac/linux") BinFile1 = OutputObject(dllLoc) BinFile1.OpenOutputFile(args[1]) BinFile1.get_NetSize() BinFile1.get_Times() BinFile2 = OutputObject(dllLoc) BinFile2.OpenOutputFile(args[2]) ####ENR_NodeAttribute; ##ENR_demand = 0 ##ENR_head = 1 ##ENR_pressure = 2 ##ENR_quality = 3 NumberOfNodeAttr = 4 ####ENR_LinkAttribute; ##ENR_flow = 0 ##ENR_velocity = 1 ##ENR_headloss = 2 ##ENR_avgQuality = 3 ##ENR_status = 4 ##ENR_setting = 5 ##ENR_rxRate = 6 ##ENT_frctnFctr = 7 NumberOfLinkAttr = 8 NumberOfPeriods = BinFile1.numPeriods # Set Tolerances for each attribute # demand, head, pressure, quality NodeAttributeTolerances = [1e-6, 1e-6, 1e-6, 1e-6] # flow, velocity, headloss, avgQuality, status, setting, rxRate, frctnFctr LinkAttributeTolerances = [1e-6, 1e-6, 1e-6, 1e-6, 1e-6, 1e-6, 1e-6, 1e-6] #Compare Node Attributes for nodeAttrInd in range(NumberOfNodeAttr): for TSind in range(NumberOfPeriods): #Get 1 attribute for all nodes at time t NodeAttributeOut1 = BinFile1.get_NodeAttribute(nodeAttrInd, TSind) #Get 1 attribute for all nodes at time t NodeAttributeOut2 = BinFile2.get_NodeAttribute(nodeAttrInd, TSind) for Nodeind, NodeAttrVal in enumerate(NodeAttributeOut1): if abs(NodeAttrVal - NodeAttributeOut2[Nodeind]) > 0: diff = abs(NodeAttrVal - NodeAttributeOut2[Nodeind] ) if diff > NodeAttributeTolerances[nodeAttrInd]: print("Node {0} attribute {1} exceeded tolerance. value 1 = {2} :: value 2 = {3} :: delta = {4}".format(Nodeind, nodeAttrInd, NodeAttrVal, NodeAttributeOut2[Nodeind], diff) ) return False #Compare Link Attributes for linkAttrInd in range(NumberOfLinkAttr): for TSind in range(NumberOfPeriods): #Get 1 attribute for all links at time t LinkAttributeOut1 = BinFile1.get_LinkAttribute(linkAttrInd, TSind) #Get 1 attribute for all links at time t LinkAttributeOut2 = BinFile2.get_LinkAttribute(linkAttrInd, TSind) for linkind, LinkAttrVal in enumerate(LinkAttributeOut1): if abs(LinkAttrVal - LinkAttributeOut2[linkind]) > 0: diff = abs(LinkAttrVal - LinkAttributeOut2[linkind] ) if diff > LinkAttributeTolerances[linkAttrInd]: print("Link {0} attribute {1} exceeded tolerance. value 1 = {2} :: value 2 = {3} :: delta = {4}".format(linkind, linkAttrInd, LinkAttrVal, LinkAttributeOut2[linkind], diff) ) return False return True if __name__ == '__main__': if(BinCompare(sys.argv)): sys.exit(0) else: sys.exit(1) \ No newline at end of file diff --git a/tools/outputapi/ENOutputWrapper.py b/tools/outputapi/ENOutputWrapper.py deleted file mode 100644 index 8f934d4..0000000 --- a/tools/outputapi/ENOutputWrapper.py +++ /dev/null @@ -1 +0,0 @@ -''' Wrapper for EPANET Output API. Author: Bryant E. McDonnell Date: 12/7/2015 Language: Anglais ''' from ctypes import * ##ENR_ElementType; ENR_node = 1 ENR_link = 2 ##ENR_ApiFunction; ENR_getSeries = 1 ENR_getAttribute = 2 ENR_getResult = 3 ##ENR_ElementCount; ENR_nodeCount = 1 ENR_tankCount = 2 ENR_linkCount = 3 ENR_pumpCount = 4 ENR_valveCount = 5 ##ENR_Unit; ENR_flowUnits = 1 ENR_pressUnits = 2 ##ENR_Time; ENR_reportStart = 1 ENR_reportStep = 2 ENR_simDuration = 3 ENR_numPeriods = 4 ##ENR_NodeAttribute; ENR_demand = 0 ENR_head = 1 ENR_pressure = 2 ENR_quality = 3 ##ENR_LinkAttribute; ENR_flow = 0 ENR_velocity = 1 ENR_headloss = 2 ENR_avgQuality = 3 ENR_status = 4 ENR_setting = 5 ENR_rxRate = 6 ENT_frctnFctr = 7 #Used just to pull the Pointer of the ENResultsAPI struct class _Opaque(Structure): ''' Used soley for passing the pointer to the enrapi struct to API ''' pass class OutputObject: def __init__(self, dllLoc): ''' Instantiate python Wrapper Object and build Wrapper functions. ''' try: self.DLL = CDLL(dllLoc) except: raise Exception('Failed to Open Linked Library') ###ENResultsAPI* DLLEXPORT ENR_alloc(void); self._enrapiFunc = self.DLL.ENR_alloc self._enrapiFunc.restype = POINTER(_Opaque) ###int DLLEXPORT ENR_open(ENResultsAPI* enrapi, const char* path); self._OpenFunc = self.DLL.ENR_open ###int DLLEXPORT ENR_getNetSize(ENResultsAPI* enrapi, ENR_ElementCount code, int* count); self._GetNetSize = self.DLL.ENR_getNetSize ###int DLLEXPORT ENR_getUnits(ENResultsAPI* enrapi, ENR_Unit code, int* unitFlag); self._GetUnits = self.DLL.ENR_getUnits self._GetUnits.argtypes = [POINTER(_Opaque), c_int, POINTER(c_int)] self._GetUnits.restype = c_int ###int DLLEXPORT ENR_getTimes(ENResultsAPI* enrapi, ENR_Time code, int* time) self._getTimes = self.DLL.ENR_getTimes ###float* ENR_newOutValueSeries(ENResultsAPI* enrapi, int seriesStart, ### int seriesLength, int* length, int* errcode); self._newOutValueSeries = self.DLL.ENR_newOutValueSeries self._newOutValueSeries.argtypes = [POINTER(_Opaque), c_int, c_int, POINTER(c_int), POINTER(c_int)] self._newOutValueSeries.restype = POINTER(c_float) ###float* ENR_newOutValueArray(ENResultsAPI* enrapi, ENR_ApiFunction func, ### ENR_ElementType type, int* length, int* errcode); self._newOutValueArray = self.DLL.ENR_newOutValueArray self._newOutValueArray.argtypes = [POINTER(_Opaque), c_int, c_int, POINTER(c_int), POINTER(c_int)] self._newOutValueArray.restype = POINTER(c_float) ###int DLLEXPORT ENR_getNodeSeries(ENResultsAPI* enrapi, int nodeIndex, ENR_NodeAttribute attr, ### int timeIndex, int length, float* outValueSeries, int* len); self._getNodeSeries = self.DLL.ENR_getNodeSeries ###int DLLEXPORT ENR_getLinkSeries(ENResultsAPI* enrapi, int linkIndex, ENR_LinkAttribute attr, ### int timeIndex, int length, float* outValueSeries); self._getLinkSeries = self.DLL.ENR_getLinkSeries ###int DLLEXPORT ENR_getNodeAttribute(ENResultsAPI* enrapi, int timeIndex, ### ENR_NodeAttribute attr, float* outValueArray); self._getNodeAttribute = self.DLL.ENR_getNodeAttribute ###int DLLEXPORT ENT_getLinkAttribute(ENResultsAPI* enrapi, int timeIndex, ### ENR_LinkAttribute attr, float* outValueArray); self._getLinkAttribute = self.DLL.ENR_getLinkAttribute ###int DLLEXPORT ENR_getNodeResult(ENResultsAPI* enrapi, int timeIndex, int nodeIndex, ### float* outValueArray); self._getNodeResult = self.DLL.ENR_getNodeResult ###int DLLEXPORT ENR_getLinkResult(ENResultsAPI* enrapi, int timeIndex, int linkIndex, ### float* outValueArray); self._getLinkResult = self.DLL.ENR_getLinkResult ###int DLLEXPORT ENR_free(float *array); self._free = self.DLL.ENR_free ###int DLLEXPORT ENR_close(ENResultsAPI* enrapi); self._CloseOut = self.DLL.ENR_close ###int DLLEXPORT ENR_errMessage(int errcode, char* errmsg, int n); self._RetErrMessage = self.DLL.ENR_errMessage def OpenOutputFile(self, binfile): ''' 1) Initializes the opaque pointer to enrapi struct. 2) Opens the output file. ''' self.enrapi = self._enrapiFunc() ret = self._OpenFunc(self.enrapi, binfile) if ret != 0: self.CloseOutputFile() raise Exception('Failed to open OutputFile') ##Update Function at a later date ## def RaiseError(self, ErrNo): ## ErMsg = c_char_p(256) ## ## self._RetErrMessage(ErrNo , ErMsg, 256) ## ## print ErMsg def get_Units(self): ''' Purpose: Returns pressure and flow units ''' unit = c_int() self._GetUnits(self.enrapi, ENR_flowUnits, unit) self.flowUnits = unit.value unit = c_int() self._GetUnits(self.enrapi, ENR_pressUnits, unit) self.pressUnits = unit.value def get_NetSize(self): ''' Populates object attributes with the water object counts ''' count = c_int() self._GetNetSize(self.enrapi, ENR_nodeCount, byref(count)) self.nodeCount = count.value count = c_int() self._GetNetSize(self.enrapi, ENR_tankCount, byref(count)) self.tankCount = count.value count = c_int() self._GetNetSize(self.enrapi, ENR_linkCount, byref(count)) self.linkCount = count.value count = c_int() self._GetNetSize(self.enrapi, ENR_pumpCount, byref(count)) self.pumpCount = count.value count = c_int() self._GetNetSize(self.enrapi, ENR_valveCount, byref(count)) self.valveCount = count.value def get_Times(self): ''' Purpose: Returns report and simulation time related parameters. ''' temp = c_int() self._getTimes(self.enrapi, ENR_reportStart, byref(temp)) self.reportStart = temp.value temp = c_int() self._getTimes(self.enrapi, ENR_reportStep, byref(temp)) self.reportStep = temp.value temp = c_int() self._getTimes(self.enrapi, ENR_simDuration, byref(temp)) self.simDuration = temp.value temp = c_int() self._getTimes(self.enrapi, ENR_numPeriods, byref(temp)) self.numPeriods = temp.value def get_NodeSeries(self, NodeInd, NodeAttr, SeriesStartInd = 0, SeriesLen = -1): ''' Purpose: Get time series results for particular attribute. Specify series start and length using seriesStart and seriesLength respectively. SeriesLen = -1 Default input: Gets data from Series Start Ind to end ''' if not hasattr(self, 'numPeriods'): self.get_Times() if SeriesLen > self.numPeriods : raise Exception("Outside Number of TimeSteps") elif SeriesLen == -1: SeriesLen = self.numPeriods sLength = c_int() ErrNo1 = c_int() SeriesPtr = self._newOutValueSeries(self.enrapi, SeriesStartInd,\ SeriesLen, byref(sLength), byref(ErrNo1)) ErrNo2 = self._getNodeSeries(self.enrapi, NodeInd, NodeAttr, \ SeriesStartInd, sLength.value, SeriesPtr) BldArray = [SeriesPtr[i] for i in range(sLength.value)] self._free(SeriesPtr) return BldArray def get_LinkSeries(self, LinkInd, LinkAttr, SeriesStartInd = 0, SeriesLen = -1): ''' Purpose: Get time series results for particular attribute. Specify series start and length using seriesStart and seriesLength respectively. SeriesLen = -1 Default input: Gets data from Series Start Ind to end ''' if not hasattr(self, 'numPeriods'): self.get_Times() if SeriesLen > self.numPeriods : raise Exception("Outside Number of TimeSteps") elif SeriesLen == -1: SeriesLen = self.numPeriods sLength = c_int() ErrNo1 = c_int() SeriesPtr = self._newOutValueSeries(self.enrapi, SeriesStartInd,\ SeriesLen, byref(sLength), byref(ErrNo1)) ErrNo2 = self._getLinkSeries(self.enrapi, LinkInd, LinkAttr, \ SeriesStartInd, sLength.value, SeriesPtr) BldArray = [SeriesPtr[i] for i in range(sLength.value)] ret = self._free(SeriesPtr) return BldArray def get_NodeAttribute(self, NodeAttr, TimeInd): ''' Purpose: For all nodes at given time, get a particular attribute ''' if not hasattr(self, 'nodeCount'): self.get_NetSize() alength = c_int() ErrNo1 = c_int() ValArrayPtr = self._newOutValueArray(self.enrapi, ENR_getAttribute,\ ENR_node, byref(alength), byref(ErrNo1)) ErrNo2 = self._getNodeAttribute(self.enrapi, TimeInd, NodeAttr, ValArrayPtr) BldArray = [ValArrayPtr[i] for i in range(alength.value)] self._free(ValArrayPtr) return BldArray def get_LinkAttribute(self, LinkAttr, TimeInd): ''' Purpose: For all links at given time, get a particular attribute ''' if not hasattr(self, 'linkCount'): self.get_NetSize() alength = c_int() ErrNo1 = c_int() ValArrayPtr = self._newOutValueArray(self.enrapi, ENR_getAttribute,\ ENR_link, byref(alength), byref(ErrNo1)) ErrNo2 = self._getLinkAttribute(self.enrapi, TimeInd, LinkAttr, ValArrayPtr) BldArray = [ValArrayPtr[i] for i in range(alength.value)] self._free(ValArrayPtr) return BldArray def get_NodeResult(self, NodeInd, TimeInd): ''' Purpose: For a node at given time, get all attributes ''' alength = c_int() ErrNo1 = c_int() ValArrayPtr = self._newOutValueArray(self.enrapi, ENR_getResult,\ ENR_node, byref(alength), byref(ErrNo1)) ErrNo2 = self._getNodeResult(self.enrapi, TimeInd, NodeInd, ValArrayPtr) BldArray = [ValArrayPtr[i] for i in range(alength.value)] self._free(ValArrayPtr) return BldArray def get_LinkResult(self, LinkInd, TimeInd): ''' Purpose: For a link at given time, get all attributes ''' alength = c_int() ErrNo1 = c_int() ValArrayPtr = self._newOutValueArray(self.enrapi, ENR_getResult,\ ENR_link, byref(alength), byref(ErrNo1)) ErrNo2 = self._getLinkResult(self.enrapi, TimeInd, LinkInd, ValArrayPtr) BldArray = [ValArrayPtr[i] for i in range(alength.value)] self._free(ValArrayPtr) return BldArray def CloseOutputFile(self): ''' Call to close binary file. ''' ret = self._CloseOut(self.enrapi) if ret != 0: raise Exception('Failed to Close *.out file') if __name__ in "__main__": dllLoc = 'outputAPI.dll' binfile = 'C:\\PROJECTCODE\\EPANEToutputAPI\\Net3.out' print("Testing Wrapper...") print("Test Network 3...") print("-->Instantiating DLL Object / Opening DLL") self = OutputObject(dllLoc) print("...Opened DLL") print("\n-->Reading Binary File") self.OpenOutputFile(binfile) print("...Opened Bin File") print("\n-->get_NodeSeries") LENTest = -1 demands = self.get_NodeSeries(0,ENR_demand, 0, LENTest) head = self.get_NodeSeries(0,ENR_head, 0, LENTest) pressure = self.get_NodeSeries(0,ENR_pressure, 0, LENTest) quality = self.get_NodeSeries(0,ENR_quality, 0, LENTest) print('\t'.join(["ind","demand","head","pressure","quality"])) for ind, val in enumerate(demands): print ind, val, head[ind], pressure[ind], quality[ind] print("\n-->get_LinkSeries") flow = self.get_LinkSeries(0,ENR_flow) velocity =self.get_LinkSeries(0,ENR_velocity) headloss =self.get_LinkSeries(0,ENR_headloss) avgQuality=self.get_LinkSeries(0,ENR_avgQuality) status=self.get_LinkSeries(0,ENR_status) setting=self.get_LinkSeries(0,ENR_setting) rxRate=self.get_LinkSeries(0,ENR_rxRate) frctnFctr =self.get_LinkSeries(0,ENT_frctnFctr) print('\t'.join(["ind","flow","velocity","headloss","avgQuality",\ "status","setting","rxRate","frctnFctr"])) for ind, val in enumerate(flow): print ind, val, velocity[ind],headloss[ind],avgQuality[ind],status[ind],setting[ind],rxRate[ind],frctnFctr[ind] print("\n-->get_NodeAttribute") demand_1 = self.get_NodeAttribute(ENR_demand,0) head_1 = self.get_NodeAttribute(ENR_head,0) pressure_1 = self.get_NodeAttribute(ENR_pressure,0) quality_1 = self.get_NodeAttribute(ENR_quality,0) print('\t'.join(["ind","demand","head","pressure","quality"])) for ind, val in enumerate(demand_1): print ind, val, head_1[ind], pressure_1[ind], quality_1[ind] print("\n-->get_LinkAttribute") flow1 = self.get_LinkAttribute(ENR_flow,0) velocity1 =self.get_LinkAttribute(ENR_velocity,0) headloss1 =self.get_LinkAttribute(ENR_headloss,0) avgQuality1=self.get_LinkAttribute(ENR_avgQuality,0) status1=self.get_LinkAttribute(ENR_status,0) setting1=self.get_LinkAttribute(ENR_setting,0) rxRate1=self.get_LinkAttribute(ENR_rxRate,0) frctnFctr1 =self.get_LinkAttribute(ENT_frctnFctr,0) print('\t'.join(["ind","flow","velocity","headloss","avgQuality",\ "status","setting","rxRate","frctnFctr"])) for ind, val in enumerate(flow1): print ind, val, velocity1[ind],headloss1[ind],avgQuality1[ind],status1[ind],setting1[ind],rxRate1[ind],frctnFctr1[ind] print("\n-->Object Counts") print("Nodes") print(self.nodeCount) print("Nodes") print(self.nodeCount) print("Tanks") print(self.tankCount) print("Links") print(self.linkCount) print("Pumps") print(self.pumpCount) print("Values") print(self.valveCount) print("\n-->get_NodeResult") print(self.get_NodeResult(96,24)) print("\n-->get_LinkResult") print(self.get_LinkResult(0,0)) print("\n-->get_Units") self.get_Units() print("...flow unit code") print(self.flowUnits) print("...pressure unit code") print(self.pressUnits) print("\n-->Closing Binary File") self.CloseOutputFile() print("...Closed Binary File") \ No newline at end of file diff --git a/tools/outputapi/outputapi.c b/tools/outputapi/outputapi.c deleted file mode 100644 index 3c99440..0000000 --- a/tools/outputapi/outputapi.c +++ /dev/null @@ -1,502 +0,0 @@ -//----------------------------------------------------------------------------- -// -// outputapi.c -- API for reading results from EPANet binary output file -// -// Version: 0.10 -// Date: 08/05/14 -// Date: 05/21/14 -// -// Author: Michael E. Tryby -// US EPA - NRMRL -// -// Purpose: Output API provides an interface for retrieving results from -// an EPANet binary output file. -// -//----------------------------------------------------------------------------- - -#include -#include -#include -#include -#include "outputapi.h" - -#define INT4 int -#define REAL4 float -#define RECORDSIZE 4 // number of bytes per file record - -#define MEMCHECK(x) (((x) == NULL) ? 411 : 0 ) - -#define MINNREC 14 // minimum allowable number of records -#define NNODERESULTS 4 // number of result fields for nodes -#define NLINKRESULTS 8 // number of result fields for links - - -struct ENResultsAPI { - char name[MAXFNAME + 1]; // file path/name - bool isOpened; // current state (CLOSED = 0, OPEN = 1) - FILE *file; // FILE structure pointer - - INT4 nodeCount, tankCount, linkCount, pumpCount, valveCount; - INT4 reportStart, reportStep, simDuration, nPeriods; - - INT4 flowFlag, pressFlag; - - INT4 outputStartPos; // starting file position of output data - INT4 bytesPerPeriod; // bytes saved per simulation time period -}; - -//----------------------------------------------------------------------------- -// Local functions -//----------------------------------------------------------------------------- -float getNodeValue(ENResultsAPI*, int, int, ENR_NodeAttribute); -float getLinkValue(ENResultsAPI*, int, int, ENR_LinkAttribute); - - -ENResultsAPI* DLLEXPORT ENR_alloc(void) -{ - return malloc(sizeof(struct ENResultsAPI)); -} - -int DLLEXPORT ENR_open(ENResultsAPI* enrapi, const char* path) -// -// Purpose: Open the output binary file and read epilogue -// -{ - int magic1, magic2, errCode, version; - - strncpy(enrapi->name, path, MAXFNAME); - enrapi->isOpened = false; - - // Attempt to open binary output file for reading only - if ((enrapi->file = fopen(path, "rb")) == NULL) - return 434; - else - enrapi->isOpened = true; - - // Fast forward to end and check for minimum number of records - fseek(enrapi->file, 0L, SEEK_END); - if (ftell(enrapi->file) < MINNREC*RECORDSIZE) { - fclose(enrapi->file); - // Error run terminated no results in binary file - return 435; - } - - // Fast forward to end and read file epilogue - fseek(enrapi->file, -3*RECORDSIZE, SEEK_END); - fread(&(enrapi->nPeriods), RECORDSIZE, 1, enrapi->file); - fread(&errCode, RECORDSIZE, 1, enrapi->file); - fread(&magic2, RECORDSIZE, 1, enrapi->file); - - // Rewind and read magic number from beginning of file - fseek(enrapi->file, 0L, SEEK_SET); - fread(&magic1, RECORDSIZE, 1, enrapi->file); - - // Perform error checks - if (magic1 != magic2 || errCode != 0 || enrapi->nPeriods == 0) { - fclose(enrapi->file); - // Error run terminated no results in binary file - return 435; - } - - // Otherwise read network size - fread(&version, RECORDSIZE, 1, enrapi->file); - fread(&(enrapi->nodeCount), RECORDSIZE, 1, enrapi->file); - fread(&(enrapi->tankCount), RECORDSIZE, 1, enrapi->file); - fread(&(enrapi->linkCount), RECORDSIZE, 1, enrapi->file); - fread(&(enrapi->pumpCount), RECORDSIZE, 1, enrapi->file); - - // Jump ahead and read flow and pressure units - fseek(enrapi->file, 3*RECORDSIZE, SEEK_CUR); - fread(&(enrapi->flowFlag), RECORDSIZE, 1, enrapi->file); - fread(&(enrapi->pressFlag), RECORDSIZE, 1, enrapi->file); - - // Jump ahead and read time information - fseek(enrapi->file, RECORDSIZE, SEEK_CUR); - fread(&(enrapi->reportStart), RECORDSIZE, 1, enrapi->file); - fread(&(enrapi->reportStep), RECORDSIZE, 1, enrapi->file); - fread(&(enrapi->simDuration), RECORDSIZE, 1, enrapi->file); - - // Compute positions and offsets for retrieving data - enrapi->outputStartPos = 884; - enrapi->outputStartPos += 32*enrapi->nodeCount + 32*enrapi->linkCount; - enrapi->outputStartPos += 12*enrapi->linkCount+ 8*enrapi->tankCount - + 4*enrapi->nodeCount + 8*enrapi->linkCount; - enrapi->outputStartPos += 28*enrapi->pumpCount + 4; - - enrapi->bytesPerPeriod = 16*enrapi->nodeCount + 32*enrapi->linkCount; - - return 0; -} - -int DLLEXPORT ENR_getNetSize(ENResultsAPI* enrapi, ENR_ElementCount code, int* count) -// -// Purpose: Returns network size -// -{ - *count = -1; - if (enrapi->isOpened) { - switch (code) - { - case ENR_nodeCount: *count = enrapi->nodeCount; break; - case ENR_tankCount: *count = enrapi->tankCount; break; - case ENR_linkCount: *count = enrapi->linkCount; break; - case ENR_pumpCount: *count = enrapi->pumpCount; break; - case ENR_valveCount: *count = enrapi->valveCount; break; - default: return 421; - } - return 0; - } - return 412; -} - -int DLLEXPORT ENR_getUnits(ENResultsAPI* enrapi, ENR_Unit code, int* unitFlag) -// -// Purpose: Returns pressure and flow units -// -{ - *unitFlag = -1; - if (enrapi->isOpened) { - switch (code) - { - case ENR_flowUnits: *unitFlag = enrapi->flowFlag; break; - case ENR_pressUnits: *unitFlag = enrapi->pressFlag; break; - default: return 421; - } - return 0; - } - return 412; -} - -int DLLEXPORT ENR_getTimes(ENResultsAPI* enrapi, ENR_Time code, int* time) -// -// Purpose: Returns report and simulation time related parameters. -// -{ - *time = -1; - if (enrapi->isOpened) { - switch (code) - { - case ENR_reportStart: *time = enrapi->reportStart; break; - case ENR_reportStep: *time = enrapi->reportStep; break; - case ENR_simDuration: *time = enrapi->simDuration; break; - case ENR_numPeriods: *time = enrapi->nPeriods; break; - default: return 421; - } - return 0; - } - return 412; -} - - -float* ENR_newOutValueSeries(ENResultsAPI* enrapi, int seriesStart, - int seriesLength, int* length, int* errcode) -// -// Purpose: Allocates memory for outValue Series. -// -// Warning: Caller must free memory allocated by this function using ENR_free(). -// -{ - int size; - float* array; - - if (enrapi->isOpened) { - - size = seriesLength - seriesStart; - if (size > enrapi->nPeriods) - size = enrapi->nPeriods; - - // Allocate memory for outValues - array = (float*) calloc(size + 1, sizeof(float)); - *errcode = (MEMCHECK(array)); - - *length = size; - return array; - } - *errcode = 412; - return NULL; -} - -float* ENR_newOutValueArray(ENResultsAPI* enrapi, ENR_ApiFunction func, - ENR_ElementType type, int* length, int* errcode) -// -// Purpose: Allocates memory for outValue Array. -// -// Warning: Caller must free memory allocated by this function using ENR_free(). -// -{ - int size; - float* array; - - if (enrapi->isOpened) { - switch (func) - { - case ENR_getAttribute: - if (type == ENR_node) - size = enrapi->nodeCount; - else - size = enrapi->linkCount; - break; - case ENR_getResult: - if (type == ENR_node) - size = NNODERESULTS; - else - size = NLINKRESULTS; - break; - default: *errcode = 421; - return NULL; - } - - // Allocate memory for outValues - array = (float*) calloc(size, sizeof(float)); - *errcode = (MEMCHECK(array)); - - *length = size; - return array; - } - *errcode = 412; - return NULL; -} - - -int DLLEXPORT ENR_getNodeSeries(ENResultsAPI* enrapi, int nodeIndex, ENR_NodeAttribute attr, - int seriesStart, int seriesLength, float* outValueSeries, int* length) -// -// What if timeIndex 0? length 0? -// -// Purpose: Get time series results for particular attribute. Specify series -// start and length using seriesStart and seriesLength respectively. -// -{ - int k; - - if (enrapi->isOpened) { - - // Check memory for outValues - if (outValueSeries == NULL) return 411; - - // loop over and build time series - for (k = 0; k <= seriesLength; k++) - outValueSeries[k] = getNodeValue(enrapi, seriesStart + 1 + k, - nodeIndex, attr); - - return 0; - } - // Error no results to report on binary file not opened - return 412; -} - -int DLLEXPORT ENR_getLinkSeries(ENResultsAPI* enrapi, int linkIndex, ENR_LinkAttribute attr, - int seriesStart, int seriesLength, float* outValueSeries) -// -// What if timeIndex 0? length 0? -// -// Purpose: Get time series results for particular attribute. Specify series -// start and length using seriesStart and seriesLength respectively. -// -{ - int k; - - if (enrapi->isOpened) { - // Check memory for outValues - if (outValueSeries == NULL) return 411; - - // loop over and build time series - for (k = 0; k <= seriesLength; k++) - outValueSeries[k] = getLinkValue(enrapi, seriesStart +1 + k, - linkIndex, attr); - - return 0; - } - // Error no results to report on binary file not opened - return 412; -} - - -int DLLEXPORT ENR_getNodeAttribute(ENResultsAPI* enrapi, int timeIndex, - ENR_NodeAttribute attr, float* outValueArray) -// -// Purpose: For all nodes at given time, get a particular attribute -// -{ - INT4 offset; - - if (enrapi->isOpened) { - // Check memory for outValues - if (outValueArray == NULL) return 411; - - // calculate byte offset to start time for series - offset = enrapi->outputStartPos + (timeIndex)*enrapi->bytesPerPeriod; - // add offset for node and attribute - offset += (attr*enrapi->nodeCount)*RECORDSIZE; - - fseek(enrapi->file, offset, SEEK_SET); - fread(outValueArray, RECORDSIZE, enrapi->nodeCount, enrapi->file); - - return 0; - } - // Error no results to report on binary file not opened - return 412; -} - -int DLLEXPORT ENR_getLinkAttribute(ENResultsAPI* enrapi, int timeIndex, - ENR_LinkAttribute attr, float* outValueArray) -// -// Purpose: For all links at given time, get a particular attribute -// -{ - INT4 offset; - - if (enrapi->isOpened) { - // Check memory for outValues - if (outValueArray == NULL) return 411; - - // calculate byte offset to start time for series - offset = enrapi->outputStartPos + (timeIndex)*enrapi->bytesPerPeriod - + (NNODERESULTS*enrapi->nodeCount)*RECORDSIZE; - // add offset for link and attribute - offset += (attr*enrapi->linkCount)*RECORDSIZE; - - fseek(enrapi->file, offset, SEEK_SET); - fread(outValueArray, RECORDSIZE, enrapi->linkCount, enrapi->file); - - return 0; - } - // Error no results to report on binary file not opened - return 412; -} - -int DLLEXPORT ENR_getNodeResult(ENResultsAPI* enrapi, int timeIndex, int nodeIndex, - float* outValueArray) -// -// Purpose: For a node at given time, get all attributes -// -{ - int j; - - if (enrapi->isOpened) { - // Check memory for outValues - if (outValueArray == NULL) return 411; - - for (j = 0; j < NNODERESULTS; j++) - outValueArray[j] = getNodeValue(enrapi, timeIndex + 1, nodeIndex, j); - - return 0; - } - // Error no results to report on binary file not opened - return 412; -} - -int DLLEXPORT ENR_getLinkResult(ENResultsAPI* enrapi, int timeIndex, int linkIndex, - float* outValueArray) -// -// Purpose: For a link at given time, get all attributes -// -{ - int j; - - if (enrapi->isOpened) { - // Check memory for outValues - if (outValueArray == NULL) return 411; - - for (j = 0; j < NLINKRESULTS; j++) - outValueArray[j] = getLinkValue(enrapi, timeIndex + 1, linkIndex, j); - - return 0; - } - // Error no results to report on binary file not opened - return 412; -} - -int DLLEXPORT ENR_free(float* array) -// -// Purpose: frees memory allocated using ENR_newOutValueSeries() or -// ENR_newOutValueArray() -// -{ - if (array != NULL) - free(array); - - return 0; -} - - -int DLLEXPORT ENR_close(ENResultsAPI* enrapi) -// -// Purpose: Clean up after and close Output API -// -{ - if (enrapi->isOpened) { - fclose(enrapi->file); - free(enrapi); - } - // Error binary file not opened - else return 412; - - return 0; -} - - -int DLLEXPORT ENR_errMessage(int errcode, char* errmsg, int n) -// -// Purpose: takes error code returns error message -// -// Input Error 411: no memory allocated for results -// Input Error 412: no results binary file hasn't been opened -// Input Error 421: invalid parameter code -// File Error 434: unable to open binary output file -// File Error 435: run terminated no results in binary file -{ - switch (errcode) - { - case 411: strncpy(errmsg, ERR411, n); break; - case 412: strncpy(errmsg, ERR412, n); break; - case 421: strncpy(errmsg, ERR421, n); break; - case 434: strncpy(errmsg, ERR434, n); break; - case 435: strncpy(errmsg, ERR435, n); break; - default: return 421; - } - - return 0; -} - - -float getNodeValue(ENResultsAPI* enrapi, int timeIndex, int nodeIndex, - ENR_NodeAttribute attr) -// -// Purpose: Retrieves an attribute value at a specified node and time -// -{ - REAL4 y; - INT4 offset; - - // calculate byte offset to start time for series - offset = enrapi->outputStartPos + (timeIndex - 1)*enrapi->bytesPerPeriod; - // add bytepos for node and attribute - offset += (nodeIndex + attr*enrapi->nodeCount)*RECORDSIZE; - - fseek(enrapi->file, offset, SEEK_SET); - fread(&y, RECORDSIZE, 1, enrapi->file); - - return y; -} - -float getLinkValue(ENResultsAPI* enrapi, int timeIndex, int linkIndex, - ENR_LinkAttribute attr) -// -// Purpose: Retrieves an attribute value at a specified link and time -// -{ - REAL4 y; - INT4 offset; - - // Calculate byte offset to start time for series - offset = enrapi->outputStartPos + (timeIndex - 1)*enrapi->bytesPerPeriod - + (NNODERESULTS*enrapi->nodeCount)*RECORDSIZE; - // add bytepos for link and attribute - offset += (linkIndex + attr*enrapi->linkCount)*RECORDSIZE; - - fseek(enrapi->file, offset, SEEK_SET); - fread(&y, RECORDSIZE, 1, enrapi->file); - - return y; -} diff --git a/tools/outputapi/outputapi.h b/tools/outputapi/outputapi.h deleted file mode 100644 index 3494c07..0000000 --- a/tools/outputapi/outputapi.h +++ /dev/null @@ -1,122 +0,0 @@ -/* - * outputapi.h - * - * Created on: Jun 4, 2014 - * Author: mtryby - */ - -#ifndef OUTPUTAPI_H_ -#define OUTPUTAPI_H_ - -#define MAXFNAME 259 - -/*------------------- Error Messages --------------------*/ -#define ERR411 "Input Error 411: no memory allocated for results." -#define ERR412 "Input Error 412: no results; binary file hasn't been opened." -#define ERR421 "Input Error 421: invalid parameter code." -#define ERR434 "File Error 434: unable to open binary output file." -#define ERR435 "File Error 435: run terminated; no results in binary file." - - -/* Epanet Results binary file API */ -typedef struct ENResultsAPI ENResultsAPI; // opaque struct object - -typedef enum { - ENR_node = 1, - ENR_link = 2 -} ENR_ElementType; - -typedef enum { - ENR_getSeries = 1, - ENR_getAttribute = 2, - ENR_getResult = 3 -} ENR_ApiFunction; - -typedef enum { - ENR_nodeCount = 1, - ENR_tankCount = 2, - ENR_linkCount = 3, - ENR_pumpCount = 4, - ENR_valveCount = 5 -} ENR_ElementCount; - -typedef enum { - ENR_flowUnits = 1, - ENR_pressUnits = 2 -} ENR_Unit; - -typedef enum { - ENR_reportStart = 1, - ENR_reportStep = 2, - ENR_simDuration = 3, - ENR_numPeriods = 4 -}ENR_Time; - -typedef enum { - ENR_demand = 0, - ENR_head = 1, - ENR_pressure = 2, - ENR_quality = 3 -} ENR_NodeAttribute; - -typedef enum { - ENR_flow = 0, - ENR_velocity = 1, - ENR_headloss = 2, - ENR_avgQuality = 3, - ENR_status = 4, - ENR_setting = 5, - ENR_rxRate = 6, - ENT_frctnFctr = 7 -} ENR_LinkAttribute; - - -#ifdef WINDOWS - #ifdef __cplusplus - #define DLLEXPORT extern "C" __declspec(dllexport) __stdcall - #else - #define DLLEXPORT __declspec(dllexport) __stdcall - #endif -#else - #ifdef __cplusplus - #define DLLEXPORT extern "C" - #else - #define DLLEXPORT - #endif -#endif - - -ENResultsAPI* DLLEXPORT ENR_alloc(void); - -int DLLEXPORT ENR_open(ENResultsAPI* enrapi, const char* path); - -int DLLEXPORT ENR_getNetSize(ENResultsAPI* enrapi, ENR_ElementCount code, int* count); -int DLLEXPORT ENR_getUnits(ENResultsAPI* enrapi, ENR_Unit code, int* unitFlag); - -float* ENR_newOutValueSeries(ENResultsAPI* enrapi, int seriesStart, - int seriesLength, int* length, int* errcode); -float* ENR_newOutValueArray(ENResultsAPI* enrapi, ENR_ApiFunction func, - ENR_ElementType type, int* length, int* errcode); - -int DLLEXPORT ENR_getNodeSeries(ENResultsAPI* enrapi, int nodeIndex, ENR_NodeAttribute attr, - int timeIndex, int length, float* outValueSeries, int* len); -int DLLEXPORT ENR_getLinkSeries(ENResultsAPI* enrapi, int linkIndex, ENR_LinkAttribute attr, - int timeIndex, int length, float* outValueSeries); - -int DLLEXPORT ENR_getNodeAttribute(ENResultsAPI* enrapi, int timeIndex, - ENR_NodeAttribute attr, float* outValueArray); -int DLLEXPORT ENT_getLinkAttribute(ENResultsAPI* enrapi, int timeIndex, - ENR_LinkAttribute attr, float* outValueArray); - -int DLLEXPORT ENR_getNodeResult(ENResultsAPI* enrapi, int timeIndex, int nodeIndex, - float* outValueArray); -int DLLEXPORT ENR_getLinkResult(ENResultsAPI* enrapi, int timeIndex, int linkIndex, - float* outValueArray); - -int DLLEXPORT ENR_free(float *array); -int DLLEXPORT ENR_close(ENResultsAPI* enrapi); -int DLLEXPORT ENR_errMessage(int errcode, char* errmsg, int n); - - -#endif /* OUTPUTAPI_H_ */ - diff --git a/tools/requirements.txt b/tools/requirements.txt new file mode 100644 index 0000000..68b8c20 --- /dev/null +++ b/tools/requirements.txt @@ -0,0 +1,16 @@ +# +# requirements.txt +# +# Date Created: 10/10/2017 +# Author: Michael E. Tryby +# US EPA ORD/NRMRL +# +# Useful for configuring a python environment to run epanet-nrtestsuite. +# +# command: +# $ pip install --src build/packages -r tools/requirements.txt +# + +-e git+https://github.com/OpenWaterAnalytics/nrtest.git@master#egg=nrtest +-e ./tools/epanet-output +-e ./tools/nrtest-epanet diff --git a/tools/run-nrtest.sh b/tools/run-nrtest.sh new file mode 100755 index 0000000..f975067 --- /dev/null +++ b/tools/run-nrtest.sh @@ -0,0 +1,58 @@ +#! /bin/bash + +# +# run-nrtest.sh - Runs numerical regression test +# +# Date Created: 10/16/2017 +# +# Author: Michael E. Tryby +# US EPA - ORD/NRMRL +# +# Arguments: +# 1 - test suite path +# 2 - version/build identifier +# + +run-nrtest() +{ + +return_value=0 + +test_suite_path=$1 + +nrtest_execute_cmd="nrtest execute" +test_app_path="apps/epanet-$2.json" +tests="tests/examples" +test_output_path="benchmark/epanet-$2" + +nrtest_compare_cmd="nrtest compare" +ref_output_path="benchmark/epanet-2012" +rtol_value=0.1 +atol_value=0.0 + + +# change current directory to test_suite +cd ${test_suite_path} + +# clean test benchmark results +rm -rf ${test_output_path} + +echo INFO: Creating test benchmark +nrtest_command="${nrtest_execute_cmd} ${test_app_path} ${tests} -o ${test_output_path}" +echo INFO: "$nrtest_command" +if ! [ $( $nrtest_command ) ]; then + echo + echo INFO: Comparing test and ref benchmarks + nrtest_command="${nrtest_compare_cmd} ${test_output_path} ${ref_output_path} --rtol ${rtol_value} --atol ${atol_value}" + echo INFO: "$nrtest_command" + return_value=$( $nrtest_command ) +else + echo ERROR: Test benchmark creation failed + exit 1 +fi + +return $return_value + +} + +run-nrtest $1 $2