From e4bd1653057e87073fe600f4debcb2c7d48d6ef8 Mon Sep 17 00:00:00 2001 From: Assela Pathirana Date: Sun, 17 Apr 2022 22:00:46 +0200 Subject: [PATCH] upgraded to version 5.2.0 (post2) --- .gitignore | 3 + README.build | 63 +- README.md | 4 +- swmm/MANIFEST.in | 2 +- swmm/README.txt | 73 +- swmm/run.bat | 59 +- swmm/setup.py | 38 +- swmm/swmm5/swmm5.i | 4 +- swmm/swmm5/swmm5.py | 360 +---- swmm/swmm5/swmm5/climate.c | 206 ++- swmm/swmm5/swmm5/consts.h | 19 +- swmm/swmm5/swmm5/controls.c | 572 +++++-- swmm/swmm5/swmm5/culvert.c | 16 +- swmm/swmm5/swmm5/datetime.c | 31 +- swmm/swmm5/swmm5/datetime.h | 10 +- swmm/swmm5/swmm5/dwflow.c | 57 +- swmm/swmm5/swmm5/dynwave.c | 99 +- swmm/swmm5/swmm5/enums.h | 81 +- swmm/swmm5/swmm5/error.c | 229 +-- swmm/swmm5/swmm5/error.h | 334 ++-- swmm/swmm5/swmm5/error.txt | 135 ++ swmm/swmm5/swmm5/exfil.c | 99 +- swmm/swmm5/swmm5/exfil.h | 10 +- swmm/swmm5/swmm5/findroot.h | 6 +- swmm/swmm5/swmm5/flowrout.c | 43 +- swmm/swmm5/swmm5/forcmain.c | 4 +- swmm/swmm5/swmm5/funcs.h | 109 +- swmm/swmm5/swmm5/gage.c | 132 +- swmm/swmm5/swmm5/globals.h | 38 +- swmm/swmm5/swmm5/gwater.c | 20 +- swmm/swmm5/swmm5/hash.c | 8 +- swmm/swmm5/swmm5/hash.h | 10 +- swmm/swmm5/swmm5/headers.h | 15 +- swmm/swmm5/swmm5/hotstart.c | 51 +- swmm/swmm5/swmm5/iface.c | 80 +- swmm/swmm5/swmm5/infil.c | 51 +- swmm/swmm5/swmm5/infil.h | 18 +- swmm/swmm5/swmm5/inflow.c | 148 +- swmm/swmm5/swmm5/inlet.c | 1945 +++++++++++++++++++++++ swmm/swmm5/swmm5/inlet.h | 30 + swmm/swmm5/swmm5/input.c | 76 +- swmm/swmm5/swmm5/inputrpt.c | 43 +- swmm/swmm5/swmm5/keywords.c | 50 +- swmm/swmm5/swmm5/keywords.h | 11 +- swmm/swmm5/swmm5/kinwave.c | 14 +- swmm/swmm5/swmm5/landuse.c | 12 +- swmm/swmm5/swmm5/lid.c | 277 ++-- swmm/swmm5/swmm5/lid.h | 53 +- swmm/swmm5/swmm5/lidproc.c | 100 +- swmm/swmm5/swmm5/link.c | 154 +- swmm/swmm5/swmm5/macros.h | 4 +- swmm/swmm5/swmm5/main.c | 22 +- swmm/swmm5/swmm5/massbal.c | 32 +- swmm/swmm5/swmm5/mathexpr.c | 100 +- swmm/swmm5/swmm5/mathexpr.h | 2 +- swmm/swmm5/swmm5/mempool.c | 1 - swmm/swmm5/swmm5/node.c | 359 +++-- swmm/swmm5/swmm5/objects.h | 162 +- swmm/swmm5/swmm5/odesolve.c | 12 +- swmm/swmm5/swmm5/output.c | 198 +-- swmm/swmm5/swmm5/project.c | 207 ++- swmm/swmm5/swmm5/qualrout.c | 44 +- swmm/swmm5/swmm5/rain.c | 30 +- swmm/swmm5/swmm5/rdii.c | 41 +- swmm/swmm5/swmm5/report.c | 306 ++-- swmm/swmm5/swmm5/roadway.c | 8 +- swmm/swmm5/swmm5/routing.c | 348 +++-- swmm/swmm5/swmm5/runoff.c | 27 +- swmm/swmm5/swmm5/shape.c | 4 +- swmm/swmm5/swmm5/snow.c | 15 +- swmm/swmm5/swmm5/stats.c | 238 +-- swmm/swmm5/swmm5/statsrpt.c | 72 +- swmm/swmm5/swmm5/street.c | 162 ++ swmm/swmm5/swmm5/street.h | 21 + swmm/swmm5/swmm5/subcatch.c | 102 +- swmm/swmm5/swmm5/surfqual.c | 8 +- swmm/swmm5/swmm5/swmm5.c | 1132 ++++++++++++-- swmm/swmm5/swmm5/swmm5.def | 9 + swmm/swmm5/swmm5/swmm5.h | 139 +- swmm/swmm5/swmm5/table.c | 206 +-- swmm/swmm5/swmm5/text.h | 67 +- swmm/swmm5/swmm5/toposort.c | 12 +- swmm/swmm5/swmm5/transect.c | 200 ++- swmm/swmm5/swmm5/treatmnt.c | 27 +- swmm/swmm5/swmm5/xsect.c | 156 +- swmm/swmm5/swmm5/xsect.dat | 4 +- swmm/swmm5/swmm5_wrap.c | 2918 ++++++++++------------------------- swmm/swmm5/swmm5tools.py | 71 +- swmm/tests/test_1.py | 2 +- 89 files changed, 7964 insertions(+), 5506 deletions(-) create mode 100644 swmm/swmm5/swmm5/error.txt create mode 100644 swmm/swmm5/swmm5/inlet.c create mode 100644 swmm/swmm5/swmm5/inlet.h create mode 100644 swmm/swmm5/swmm5/street.c create mode 100644 swmm/swmm5/swmm5/street.h diff --git a/.gitignore b/.gitignore index 668f7ef..3ba65b6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +*.bak +.vscode/ + # Packages # ############ # it's better to unpack these files and commit the raw source diff --git a/README.build b/README.build index debdfa6..9cbe1d1 100644 --- a/README.build +++ b/README.build @@ -3,49 +3,28 @@ https://wiki.python.org/moin/WindowsCompilers#Compilers_Installation_and_configu (Open a visual studio command prompt and see if cl.exe command works. If so, there is no need to do anything else. winpython knows how to find it) - -1. New swmm version: replace files in swmm5\swmm5 with the new version. - -THEN comment all definitions CLE, SOL and DLL (they will be defined by the compiler options depending on the OS) - -*** NOTE as of 2021 sept this step was not needed. *** - -/********************************************************** -// Leave only one of the following 3 lines un-commented, -// depending on the choice of compilation target -//********************************************************** -//#define CLE /* Compile as a command line executable */ -//#define SOL /* Compile as a shared object library */ -//#define DLL /* Compile as a Windows DLL */ - - -2. Upload to pypi - -delete directory dist if exists. - -python setup.py bdist_wininst -python setup.py bdist_wheel -python setup.py sdsit - -the binaries has to be done with ALL python versions supported. - -install a binary and test -TESTS: - -python -m doctest -v README.txt -python ./tests\test_multithreading.py - - -3. Then upload them with twine +*** When new files are added by swmm upstream, make sure they are updated in MANIFEST.in file! +*** if you change the swmm5.i file go to swmm5 directory and run +c:\_NO_INSTALL\swig\swigwin-4.0.2\swig.exe -python swmm5.i + +Now build : +pip install build +[ hacky way I test: +pip install --force-reinstall dist\.....whl +python -m doctest README.txt -v +python tests\test_1.py +python tests\test_multithreading.py + +then repeat again with +pip install --force-reinstall dist\.....tar.gz +... +... +... +] + + +2. Then upload them with twine twine upload dist/* -4. Compilers for windows: Now we (again) use microsoft compilers! So, if python defaults to gcc (mingw) as in winpython 3.4 - -edit pydistutils.cfg file to have ONLY following content ( - -[config] - - -(During compilation (setup.py) you will be asked to download certain compilers from microsfot) That's it. diff --git a/README.md b/README.md index 8068407..5824b59 100644 --- a/README.md +++ b/README.md @@ -72,9 +72,9 @@ One should always use the new interface. The old interface (below) is left only :: >>> st.SWMM5_Version() # Version of underlying SWMM5 engine. - '5.1.015' + '5.2.000' >>> st.SWMM5_VERSION # same thing as an integer - 51015 + 52000 >>> st.Flow_Units() # Flow units. 'LPS' >>> st.SWMM_FlowUnits # returns flow units as an index. 0 = CFS, 1 = GPM, 2 = MGD, 3 = CMS, 4 = LPS, and 5 = LPD diff --git a/swmm/MANIFEST.in b/swmm/MANIFEST.in index 13ec6ff..c1dbbcf 100644 --- a/swmm/MANIFEST.in +++ b/swmm/MANIFEST.in @@ -1,2 +1,2 @@ -include swmm5/swmm5/*.h swmm5/*.h swmm5/swmm5/*.c swmm5/*.c +include swmm5/swmm5/*.h swmm5/*.h swmm5/swmm5/*.c swmm5/*.c swmm5/swmm5/*.txt include swmm5/swmm5/xsect.dat diff --git a/swmm/README.txt b/swmm/README.txt index 40d23fc..892cad8 100644 --- a/swmm/README.txt +++ b/swmm/README.txt @@ -6,6 +6,8 @@ Released under GNU GPL v.3 Release History: ---------------- +version 5.2.000 released in 2022 + version 5.1.015 released in 2021 version 1.0.0.1 first production (non-beta) release. @@ -72,9 +74,9 @@ One should always use the new interface. The old interface (below) is left only :: >>> st.SWMM5_Version() # Version of underlying SWMM5 engine. - '5.1.015' + '5.2.000' >>> st.SWMM5_VERSION # same thing as an integer - 51015 + 52000 >>> st.Flow_Units() # Flow units. 'LPS' >>> st.SWMM_FlowUnits # returns flow units as an index. 0 = CFS, 1 = GPM, 2 = MGD, 3 = CMS, 4 = LPS, and 5 = LPD @@ -101,13 +103,21 @@ One should always use the new interface. The old interface (below) is left only >>> st.entityList() ['SUBCATCH', 'NODE', 'LINK', 'SYS'] - >>> st.Subcatch() + >>> st.Subcatch() # Deprecated + ['A2', 'A1', 'A3', 'A4', 'A5', 'E1'] + >>> st.entityList(within='SUBCATCH') # use these new since version 5.2 ['A2', 'A1', 'A3', 'A4', 'A5', 'E1'] - >>> st.Node() + >>> st.Node() # Deprecated + ['J1', 'J2', 'J3', 'J4', 'J5', 'J6', 'J7', 'J8', 'J9', 'J10', 'J11', 'J12'] + >>> st.entityList(within='NODE') # use these new since version 5.2 ['J1', 'J2', 'J3', 'J4', 'J5', 'J6', 'J7', 'J8', 'J9', 'J10', 'J11', 'J12'] - >>> st.Link() + >>> st.Link() # Deprecated ['T4-1', 'T4-2', 'T4-3', 'T1-1', 'T1-2', 'T2-1', 'T2-2', 'T2-3', 'T3-1', 'T3-2', 'T5'] - >>> st.Sys() + >>> st.entityList(within='LINK') + ['T4-1', 'T4-2', 'T4-3', 'T1-1', 'T1-2', 'T2-1', 'T2-2', 'T2-3', 'T3-1', 'T3-2', 'T5'] + >>> st.Sys() # Deprecated + ['SYS'] + >>> st.entityList(within='SYS') # use these new since version 5.2 ['SYS'] >>> st.Pollutants() # no pollutants in this file. [] @@ -174,6 +184,7 @@ One should always use the new interface. The old interface (below) is left only 11 Flow leaving through outfalls (flow units) 12 Volume of stored water (ft3 or m3) 13 Evaporation rate (in/day or mm/day) + 14 Potential evaporation rate (PET) in/day or mm/day) @@ -212,7 +223,7 @@ One should always use the new interface. The old interface (below) is left only :: - >>> wq.Subcatch() + >>> wq.entityList(within='SUBCATCH') ['S1', 'S2', 'S3', 'S4', 'S5', 'S6', 'S7'] >>> r=list(wq.Results('SUBCATCH','S3', 8)) # TSS out of catchment 'S3'. We convert it to a list. @@ -231,7 +242,7 @@ One should always use the new interface. The old interface (below) is left only :: - >>> wq.Node() + >>> wq.entityList(within='NODE') ['J1', 'J2', 'J3', 'J4', 'J5', 'J6', 'J7', 'J8', 'J9', 'J10', 'J11', 'O1'] >>> r=list(wq.Results('NODE','J3', 6)) # TSS out of Node 'J3'. We convert it to a list. @@ -247,11 +258,15 @@ One should always use the new interface. The old interface (below) is left only 15.14 15.64 - >>> wq.Link() + >>> wq.entityList(within='LINK') ['C1', 'C2', 'C3', 'C4', 'C5', 'C6', 'C7', 'C8', 'C9', 'C10', 'C11'] >>> r=list(wq.Results('LINK','C11', 5)) # TSS out of Link 'C11'. We convert it to a list. - >>> print ("\n".join( "%5.2f"% (i) for i in r)) # Lets print the first 10 items. #doctest: +ELLIPSIS + >>> print ("\n".join( "%5.2f"% (i) for i in r)) # #doctest: +ELLIPSIS + 0.00 + 0.00 + 0.00 + 0.00 0.00 0.00 0.00 @@ -259,14 +274,40 @@ One should always use the new interface. The old interface (below) is left only 0.00 0.00 0.00 - 5.55 - 9.98 - 12.72 + 0.00 + 6.40 + 12.64 + 16.57 + 19.60 + 22.53 + 25.40 ... - 44.95 + 0.00 - -:Example 5: Tracking output files +:Example 5: Get all results once (inefficient, but easy) + +:: + + >>> simtemp=SWMM5Simulation("swmm5/examples/simple/swmm5Example.inp") + >>> ar=simtemp.allResults() + >>> len(ar) + 4 + >>> list(ar.keys()) + ['SUBCATCH', 'NODE', 'LINK', 'SYS'] + >>> len(ar['NODE']) + 12 + >>> list(ar['NODE'].keys()) + ['J1', 'J2', 'J3', 'J4', 'J5', 'J6', 'J7', 'J8', 'J9', 'J10', 'J11', 'J12'] + >>> list(ar['NODE']['J1'].keys())[5] + 'Flow lost to flooding (flow units)' + >>> max(list(ar['NODE']['J1']['Flow lost to flooding (flow units)'])) # doctest: +ELLIPSIS + 4267.9... + >>> # or we can use the index for shorthand + >>> max(list(list(ar['NODE']['J10'].values())[5])) # doctest: +ELLIPSIS + 2252.7... + + +:Example 6: Tracking output files :: diff --git a/swmm/run.bat b/swmm/run.bat index c6d4be2..8090f74 100644 --- a/swmm/run.bat +++ b/swmm/run.bat @@ -1,4 +1,5 @@ :: echo off +:: RUN this script in winpython command prompt. :: download epa-swmm binary and install it. Open the examples *inp files using this binary and save them back. (Important to make sure the version compatibility of the library) :: Download and extract swmm sourcecode to swmm/swmm5/swmm5 directory. :: first step both in swmm5.c and swmm5.h #undef WINDOWS after its definition @@ -32,15 +33,16 @@ if not "%1" == "" ( if not %good%=="true" ( - echo "USAGE: %0% + echo "USAGE: %0% + echo "Example: %0% SWMM5 64 3980 5.2.0" goto ERROR ) ::::::::::::::::::::::::::::::::::::::::::::::::::::::: -set swig=c:\swig\swigwin-3.0.5\swig.exe +set swig=C:\_NO_INSTALL\swig\swigwin-4.0.2\swig.exe set loc=%~dp0 -set py3=3.3.5.0 -set py2=2.7.6.4 +:: set py3=3.9.8.0 +:: set py2=2.7.6.4 ::2.7.6.4 @@ -51,7 +53,7 @@ set pyverwhl=%pyver:.=% -if %arch% == 64bit ( +if %arch% == 64 ( set arch1=x64 set arch2=win-amd64 set arch3=win_amd64 @@ -77,7 +79,7 @@ pause echo on cd /d %loc% -call D:\WinPython-%arch%-%pyversion%\scripts\env.bat || goto ERROR +call C:\_NO_INSTALL\python\WPy%arch%-%pyversion%\scripts\env.bat || goto ERROR pip install virtualenv echo on cd %loc%\%name% || goto ERROR @@ -86,40 +88,37 @@ echo on cd .. rd /s /q build dist env1 pip install wheel -python setup.py bdist_wininst bdist_wheel || goto ERROR +python setup.py bdist_wheel || goto ERROR cd /d %loc% || goto ERROR -:: test wininst -rd /s /q env1 -virtualenv --no-site-packages --clear env1 || goto ERROR -echo on -call %loc%\env1\Scripts\activate.bat || goto ERROR -set OLDPATH=%PATH% -set PATH=%loc%\env1\Scripts -echo on -cd /d %loc% || goto ERROR -%loc%env1\Scripts\easy_install.exe %loc%\dist\%name%-%version%.%arch2%-py%pyver%.exe || goto ERROR -cd /d %loc% || goto ERROR -echo on -python -m doctest -f -v README.txt|| goto ERROR -python tests\test_1.py -v || goto ERROR -pip install nose -python tests\test_multithreading.py || goto ERROR -set PATH=%OLDPATH% - -call env1\Scripts\deactivate.bat || goto ERROR +:: :: test wininst +::rd /s /q env1 +::virtualenv --clear env1 || goto ERROR +::echo on +::call %loc%\env1\Scripts\activate.bat || goto ERROR +::set OLDPATH=%PATH% +::set PATH=%loc%\env1\Scripts +::echo on +::cd /d %loc% || goto ERROR +::%loc%env1\Scripts\easy_install.exe %loc%\dist\%name%-%version%.%arch2%-py%pyver%.exe || goto ERROR +::cd /d %loc% || goto ERROR +::echo on +::python -m doctest -f -v README.txt|| goto ERROR +::python tests\test_1.py -v || goto ERROR +::pip install nose +::python tests\test_multithreading.py || goto ERROR +::set PATH=%OLDPATH% +:: call env1\Scripts\deactivate.bat || goto ERROR del env1 /f /q :: test wheel - - -virtualenv --no-site-packages --clear env1 || goto ERROR +virtualenv --clear env1 || goto ERROR echo on call %loc%\env1\Scripts\activate.bat || goto ERROR set OLDPATH=%PATH% set PATH=%loc%\env1\Scripts echo on cd /d %loc% || goto ERROR -%loc%env1\Scripts\pip install %loc%\dist\%name%-%version%-cp%pyverwhl%-none-%arch3%.whl || goto ERROR +%loc%env1\Scripts\pip install %loc%dist\%name%-%version%-cp%pyverwhl%-none-%arch3%.whl || goto ERROR cd /d %loc% || goto ERROR echo on python -m doctest -v README.txt|| goto ERROR diff --git a/swmm/setup.py b/swmm/setup.py index 5ad4455..cc12ffa 100644 --- a/swmm/setup.py +++ b/swmm/setup.py @@ -5,12 +5,11 @@ from distutils.core import setup, Extension from itertools import product -from setuptools import setup, Extension, Command +#from setuptools import setup, Extension import os -import glob NAME='SWMM5' -VERSION='5.1.015-1' +VERSION='5.2.0post2' # in windows use microsoft compilers if os.name == 'nt': @@ -23,30 +22,9 @@ with open("README.txt","r") as f: README=f.read() - -ccodefiles=[ 'climate.c', 'controls.c', 'culvert.c', - 'datetime.c', 'dwflow.c', - 'dynwave.c', 'error.c', 'exfil.c','findroot.c', 'flowrout.c', - 'forcmain.c', 'gage.c', 'gwater.c', 'hash.c', - 'hotstart.c','iface.c', - 'infil.c', 'inflow.c', 'input.c', 'inputrpt.c', 'keywords.c', - 'kinwave.c', 'landuse.c', 'lid.c', 'lidproc.c', - 'link.c', 'massbal.c', - 'mathexpr.c', 'mempool.c', 'node.c', 'odesolve.c', 'output.c', - 'project.c', 'qualrout.c', 'rain.c', 'rdii.c', 'report.c', 'roadway.c', - 'routing.c', 'runoff.c', 'shape.c', 'snow.c', 'stats.c', 'surfqual.c', - 'statsrpt.c', 'subcatch.c', 'swmm5.c', - 'table.c', 'toposort.c', - 'transect.c', 'treatmnt.c', 'xsect.c' , ] - -# headers now -#"consts.h", "datetime.h", "enums.h", "error.h", -#"findroot.h", "funcs.h", "globals.h", "hash.h", -#"headers.h", "infil.h", "keywords.h", "lid.h", -#"macros.h", "mathexpr.h", "mempool.h", "objects.h", -#"odesolve.h", "swmm5.h", "swmm5_iface.h", "text.h" - -csources=['swmm5/swmm5/'+x for x in ccodefiles] +src_path="swmm5/swmm5" +allsrc=[os.path.join(src_path, ff) for ff in os.listdir(src_path) if os.path.isfile(os.path.join(src_path, ff))] +csources = [file for file in allsrc if file[-2:].lower()==".c"] csources.extend(['swmm5/swmm5_wrap.c','swmm5/swmm5_interface.c']) swmm5_module = Extension('_swmm5', sources=csources, @@ -54,7 +32,6 @@ extra_link_args=linkerargs, ) - EXAMPLES=["simple"] EXTS=["inp", "py"] EXTS.extend([x.upper() for x in EXTS]) @@ -64,8 +41,6 @@ KEYWORDS=["Hydraulics", "Hydrology", "Urban Drainage", "Sewerage", "Water Engineering", "Numerical Methods","Computer Model","Environmental Science", "Engineering", "Science"] - - SETUPNAME=NAME+"-"+VERSION LICENSE="GNU General Public License version 3" LONGDISC="""%(rm)s""" % {"lc": LICENSE, "rm": README} @@ -101,5 +76,6 @@ #download_url="http://swmm5-ea.googlecode.com/files/"+SETUPNAME+".zip", long_description = LONGDISC, classifiers=CLASSIFY, - keywords=KEYWORDS + keywords=KEYWORDS, + #options={'build_ext':{'inplace':True}} ) diff --git a/swmm/swmm5/swmm5.i b/swmm/swmm5/swmm5.i index 6713097..8c44411 100644 --- a/swmm/swmm5/swmm5.i +++ b/swmm/swmm5/swmm5.i @@ -1,4 +1,6 @@ -%module swmm5 +/* %module swmm5 */ + %module(package="swmm5", moduleimport="import $module") swmm5 +/* ABOVE fix was needed when swig upgraded to verson 4. thanks: https://github.com/swig/swig/issues/1486 */ %include "typemaps.i" %include "cstring.i" /* read http://www.swig.org/Doc1.3/Arguments.html */ diff --git a/swmm/swmm5/swmm5.py b/swmm/swmm5/swmm5.py index 5d4ec3c..dd12d71 100644 --- a/swmm/swmm5/swmm5.py +++ b/swmm/swmm5/swmm5.py @@ -1,451 +1,201 @@ # This file was automatically generated by SWIG (http://www.swig.org). -# Version 3.0.5 +# Version 4.0.2 # # Do not make changes to this file unless you know what you are doing--modify # the SWIG interface file instead. +from sys import version_info as _swig_python_version_info +if _swig_python_version_info < (2, 7, 0): + raise RuntimeError("Python 2.7 or later required") +import _swmm5 - - -from sys import version_info -if version_info >= (2, 6, 0): - def swig_import_helper(): - from os.path import dirname - import imp - fp = None - try: - fp, pathname, description = imp.find_module('_swmm5', [dirname(__file__)]) - except ImportError: - import _swmm5 - return _swmm5 - if fp is not None: - try: - _mod = imp.load_module('_swmm5', fp, pathname, description) - finally: - fp.close() - return _mod - _swmm5 = swig_import_helper() - del swig_import_helper -else: - import _swmm5 -del version_info try: - _swig_property = property -except NameError: - pass # Python < 2.2 doesn't have 'property'. + import builtins as __builtin__ +except ImportError: + import __builtin__ +def _swig_repr(self): + try: + strthis = "proxy of " + self.this.__repr__() + except __builtin__.Exception: + strthis = "" + return "<%s.%s; %s >" % (self.__class__.__module__, self.__class__.__name__, strthis,) -def _swig_setattr_nondynamic(self, class_type, name, value, static=1): - if (name == "thisown"): - return self.this.own(value) - if (name == "this"): - if type(value).__name__ == 'SwigPyObject': - self.__dict__[name] = value - return - method = class_type.__swig_setmethods__.get(name, None) - if method: - return method(self, value) - if (not static): - if _newclass: - object.__setattr__(self, name, value) - else: - self.__dict__[name] = value - else: - raise AttributeError("You cannot add attributes to %s" % self) +def _swig_setattr_nondynamic_instance_variable(set): + def set_instance_attr(self, name, value): + if name == "thisown": + self.this.own(value) + elif name == "this": + set(self, name, value) + elif hasattr(self, name) and isinstance(getattr(type(self), name), property): + set(self, name, value) + else: + raise AttributeError("You cannot add instance attributes to %s" % self) + return set_instance_attr -def _swig_setattr(self, class_type, name, value): - return _swig_setattr_nondynamic(self, class_type, name, value, 0) +def _swig_setattr_nondynamic_class_variable(set): + def set_class_attr(cls, name, value): + if hasattr(cls, name) and not isinstance(getattr(cls, name), property): + set(cls, name, value) + else: + raise AttributeError("You cannot add class attributes to %s" % cls) + return set_class_attr -def _swig_getattr_nondynamic(self, class_type, name, static=1): - if (name == "thisown"): - return self.this.own() - method = class_type.__swig_getmethods__.get(name, None) - if method: - return method(self) - if (not static): - return object.__getattr__(self, name) - else: - raise AttributeError(name) -def _swig_getattr(self, class_type, name): - return _swig_getattr_nondynamic(self, class_type, name, 0) +def _swig_add_metaclass(metaclass): + """Class decorator for adding a metaclass to a SWIG wrapped class - a slimmed down version of six.add_metaclass""" + def wrapper(cls): + return metaclass(cls.__name__, cls.__bases__, cls.__dict__.copy()) + return wrapper -def _swig_repr(self): - try: - strthis = "proxy of " + self.this.__repr__() - except: - strthis = "" - return "<%s.%s; %s >" % (self.__class__.__module__, self.__class__.__name__, strthis,) - -try: - _object = object - _newclass = 1 -except AttributeError: - class _object: - pass - _newclass = 0 +class _SwigNonDynamicMeta(type): + """Meta class to enforce nondynamic attributes (no new attributes) for a class""" + __setattr__ = _swig_setattr_nondynamic_class_variable(type.__setattr__) def RunSwmmDll(inpFile, rptFile, outFile): return _swmm5.RunSwmmDll(inpFile, rptFile, outFile) -RunSwmmDll = _swmm5.RunSwmmDll def OpenSwmmOutFile(outFile): return _swmm5.OpenSwmmOutFile(outFile) -OpenSwmmOutFile = _swmm5.OpenSwmmOutFile def GetSwmmResult(iType, iIndex, vIndex, period): return _swmm5.GetSwmmResult(iType, iIndex, vIndex, period) -GetSwmmResult = _swmm5.GetSwmmResult def CloseSwmmOutFile(): return _swmm5.CloseSwmmOutFile() -CloseSwmmOutFile = _swmm5.CloseSwmmOutFile def GetIDName(): return _swmm5.GetIDName() -GetIDName = _swmm5.GetIDName def InitGetIDName(): return _swmm5.InitGetIDName() -InitGetIDName = _swmm5.InitGetIDName def GetInt(): return _swmm5.GetInt() -GetInt = _swmm5.GetInt - -_swmm5.ERR_NONE_swigconstant(_swmm5) ERR_NONE = _swmm5.ERR_NONE - -_swmm5.ERR_MEMORY_swigconstant(_swmm5) ERR_MEMORY = _swmm5.ERR_MEMORY - -_swmm5.ERR_KINWAVE_swigconstant(_swmm5) ERR_KINWAVE = _swmm5.ERR_KINWAVE - -_swmm5.ERR_ODE_SOLVER_swigconstant(_swmm5) ERR_ODE_SOLVER = _swmm5.ERR_ODE_SOLVER - -_swmm5.ERR_TIMESTEP_swigconstant(_swmm5) ERR_TIMESTEP = _swmm5.ERR_TIMESTEP - -_swmm5.ERR_SUBCATCH_OUTLET_swigconstant(_swmm5) ERR_SUBCATCH_OUTLET = _swmm5.ERR_SUBCATCH_OUTLET - -_swmm5.ERR_AQUIFER_PARAMS_swigconstant(_swmm5) ERR_AQUIFER_PARAMS = _swmm5.ERR_AQUIFER_PARAMS - -_swmm5.ERR_GROUND_ELEV_swigconstant(_swmm5) ERR_GROUND_ELEV = _swmm5.ERR_GROUND_ELEV - -_swmm5.ERR_LENGTH_swigconstant(_swmm5) ERR_LENGTH = _swmm5.ERR_LENGTH - -_swmm5.ERR_ELEV_DROP_swigconstant(_swmm5) ERR_ELEV_DROP = _swmm5.ERR_ELEV_DROP - -_swmm5.ERR_ROUGHNESS_swigconstant(_swmm5) ERR_ROUGHNESS = _swmm5.ERR_ROUGHNESS - -_swmm5.ERR_BARRELS_swigconstant(_swmm5) ERR_BARRELS = _swmm5.ERR_BARRELS - -_swmm5.ERR_SLOPE_swigconstant(_swmm5) ERR_SLOPE = _swmm5.ERR_SLOPE - -_swmm5.ERR_NO_XSECT_swigconstant(_swmm5) ERR_NO_XSECT = _swmm5.ERR_NO_XSECT - -_swmm5.ERR_XSECT_swigconstant(_swmm5) ERR_XSECT = _swmm5.ERR_XSECT - -_swmm5.ERR_NO_CURVE_swigconstant(_swmm5) ERR_NO_CURVE = _swmm5.ERR_NO_CURVE - -_swmm5.ERR_PUMP_LIMITS_swigconstant(_swmm5) ERR_PUMP_LIMITS = _swmm5.ERR_PUMP_LIMITS - -_swmm5.ERR_LOOP_swigconstant(_swmm5) ERR_LOOP = _swmm5.ERR_LOOP - -_swmm5.ERR_MULTI_OUTLET_swigconstant(_swmm5) ERR_MULTI_OUTLET = _swmm5.ERR_MULTI_OUTLET - -_swmm5.ERR_DUMMY_LINK_swigconstant(_swmm5) ERR_DUMMY_LINK = _swmm5.ERR_DUMMY_LINK - -_swmm5.ERR_DIVIDER_swigconstant(_swmm5) ERR_DIVIDER = _swmm5.ERR_DIVIDER - -_swmm5.ERR_DIVIDER_LINK_swigconstant(_swmm5) ERR_DIVIDER_LINK = _swmm5.ERR_DIVIDER_LINK - -_swmm5.ERR_WEIR_DIVIDER_swigconstant(_swmm5) ERR_WEIR_DIVIDER = _swmm5.ERR_WEIR_DIVIDER - -_swmm5.ERR_NODE_DEPTH_swigconstant(_swmm5) ERR_NODE_DEPTH = _swmm5.ERR_NODE_DEPTH - -_swmm5.ERR_REGULATOR_swigconstant(_swmm5) ERR_REGULATOR = _swmm5.ERR_REGULATOR - -_swmm5.ERR_OUTFALL_swigconstant(_swmm5) +ERR_STORAGE_VOLUME = _swmm5.ERR_STORAGE_VOLUME ERR_OUTFALL = _swmm5.ERR_OUTFALL - -_swmm5.ERR_REGULATOR_SHAPE_swigconstant(_swmm5) ERR_REGULATOR_SHAPE = _swmm5.ERR_REGULATOR_SHAPE - -_swmm5.ERR_NO_OUTLETS_swigconstant(_swmm5) ERR_NO_OUTLETS = _swmm5.ERR_NO_OUTLETS - -_swmm5.ERR_UNITHYD_TIMES_swigconstant(_swmm5) ERR_UNITHYD_TIMES = _swmm5.ERR_UNITHYD_TIMES - -_swmm5.ERR_UNITHYD_RATIOS_swigconstant(_swmm5) ERR_UNITHYD_RATIOS = _swmm5.ERR_UNITHYD_RATIOS - -_swmm5.ERR_RDII_AREA_swigconstant(_swmm5) ERR_RDII_AREA = _swmm5.ERR_RDII_AREA - -_swmm5.ERR_RAIN_FILE_CONFLICT_swigconstant(_swmm5) ERR_RAIN_FILE_CONFLICT = _swmm5.ERR_RAIN_FILE_CONFLICT - -_swmm5.ERR_RAIN_GAGE_FORMAT_swigconstant(_swmm5) ERR_RAIN_GAGE_FORMAT = _swmm5.ERR_RAIN_GAGE_FORMAT - -_swmm5.ERR_RAIN_GAGE_TSERIES_swigconstant(_swmm5) ERR_RAIN_GAGE_TSERIES = _swmm5.ERR_RAIN_GAGE_TSERIES - -_swmm5.ERR_RAIN_GAGE_INTERVAL_swigconstant(_swmm5) ERR_RAIN_GAGE_INTERVAL = _swmm5.ERR_RAIN_GAGE_INTERVAL - -_swmm5.ERR_CYCLIC_TREATMENT_swigconstant(_swmm5) ERR_CYCLIC_TREATMENT = _swmm5.ERR_CYCLIC_TREATMENT - -_swmm5.ERR_CURVE_SEQUENCE_swigconstant(_swmm5) ERR_CURVE_SEQUENCE = _swmm5.ERR_CURVE_SEQUENCE - -_swmm5.ERR_TIMESERIES_SEQUENCE_swigconstant(_swmm5) ERR_TIMESERIES_SEQUENCE = _swmm5.ERR_TIMESERIES_SEQUENCE - -_swmm5.ERR_SNOWMELT_PARAMS_swigconstant(_swmm5) ERR_SNOWMELT_PARAMS = _swmm5.ERR_SNOWMELT_PARAMS - -_swmm5.ERR_SNOWPACK_PARAMS_swigconstant(_swmm5) ERR_SNOWPACK_PARAMS = _swmm5.ERR_SNOWPACK_PARAMS - -_swmm5.ERR_LID_TYPE_swigconstant(_swmm5) ERR_LID_TYPE = _swmm5.ERR_LID_TYPE - -_swmm5.ERR_LID_LAYER_swigconstant(_swmm5) ERR_LID_LAYER = _swmm5.ERR_LID_LAYER - -_swmm5.ERR_LID_PARAMS_swigconstant(_swmm5) ERR_LID_PARAMS = _swmm5.ERR_LID_PARAMS - -_swmm5.ERR_SUBCATCH_LID_swigconstant(_swmm5) -ERR_SUBCATCH_LID = _swmm5.ERR_SUBCATCH_LID - -_swmm5.ERR_LID_AREAS_swigconstant(_swmm5) ERR_LID_AREAS = _swmm5.ERR_LID_AREAS - -_swmm5.ERR_LID_CAPTURE_AREA_swigconstant(_swmm5) ERR_LID_CAPTURE_AREA = _swmm5.ERR_LID_CAPTURE_AREA - -_swmm5.ERR_START_DATE_swigconstant(_swmm5) ERR_START_DATE = _swmm5.ERR_START_DATE - -_swmm5.ERR_REPORT_DATE_swigconstant(_swmm5) ERR_REPORT_DATE = _swmm5.ERR_REPORT_DATE - -_swmm5.ERR_REPORT_STEP_swigconstant(_swmm5) ERR_REPORT_STEP = _swmm5.ERR_REPORT_STEP - -_swmm5.ERR_INPUT_swigconstant(_swmm5) ERR_INPUT = _swmm5.ERR_INPUT - -_swmm5.ERR_LINE_LENGTH_swigconstant(_swmm5) ERR_LINE_LENGTH = _swmm5.ERR_LINE_LENGTH - -_swmm5.ERR_ITEMS_swigconstant(_swmm5) ERR_ITEMS = _swmm5.ERR_ITEMS - -_swmm5.ERR_KEYWORD_swigconstant(_swmm5) ERR_KEYWORD = _swmm5.ERR_KEYWORD - -_swmm5.ERR_DUP_NAME_swigconstant(_swmm5) ERR_DUP_NAME = _swmm5.ERR_DUP_NAME - -_swmm5.ERR_NAME_swigconstant(_swmm5) ERR_NAME = _swmm5.ERR_NAME - -_swmm5.ERR_NUMBER_swigconstant(_swmm5) ERR_NUMBER = _swmm5.ERR_NUMBER - -_swmm5.ERR_DATETIME_swigconstant(_swmm5) ERR_DATETIME = _swmm5.ERR_DATETIME - -_swmm5.ERR_RULE_swigconstant(_swmm5) ERR_RULE = _swmm5.ERR_RULE - -_swmm5.ERR_TRANSECT_UNKNOWN_swigconstant(_swmm5) ERR_TRANSECT_UNKNOWN = _swmm5.ERR_TRANSECT_UNKNOWN - -_swmm5.ERR_TRANSECT_SEQUENCE_swigconstant(_swmm5) ERR_TRANSECT_SEQUENCE = _swmm5.ERR_TRANSECT_SEQUENCE - -_swmm5.ERR_TRANSECT_TOO_FEW_swigconstant(_swmm5) ERR_TRANSECT_TOO_FEW = _swmm5.ERR_TRANSECT_TOO_FEW - -_swmm5.ERR_TRANSECT_TOO_MANY_swigconstant(_swmm5) ERR_TRANSECT_TOO_MANY = _swmm5.ERR_TRANSECT_TOO_MANY - -_swmm5.ERR_TRANSECT_MANNING_swigconstant(_swmm5) ERR_TRANSECT_MANNING = _swmm5.ERR_TRANSECT_MANNING - -_swmm5.ERR_TRANSECT_OVERBANK_swigconstant(_swmm5) ERR_TRANSECT_OVERBANK = _swmm5.ERR_TRANSECT_OVERBANK - -_swmm5.ERR_TRANSECT_NO_DEPTH_swigconstant(_swmm5) ERR_TRANSECT_NO_DEPTH = _swmm5.ERR_TRANSECT_NO_DEPTH - -_swmm5.ERR_TREATMENT_EXPR_swigconstant(_swmm5) -ERR_TREATMENT_EXPR = _swmm5.ERR_TREATMENT_EXPR - -_swmm5.ERR_FILE_NAME_swigconstant(_swmm5) +ERR_MATH_EXPR = _swmm5.ERR_MATH_EXPR +ERR_INFIL_PARAMS = _swmm5.ERR_INFIL_PARAMS ERR_FILE_NAME = _swmm5.ERR_FILE_NAME - -_swmm5.ERR_INP_FILE_swigconstant(_swmm5) ERR_INP_FILE = _swmm5.ERR_INP_FILE - -_swmm5.ERR_RPT_FILE_swigconstant(_swmm5) ERR_RPT_FILE = _swmm5.ERR_RPT_FILE - -_swmm5.ERR_OUT_FILE_swigconstant(_swmm5) ERR_OUT_FILE = _swmm5.ERR_OUT_FILE - -_swmm5.ERR_OUT_WRITE_swigconstant(_swmm5) +ERR_OUT_SIZE = _swmm5.ERR_OUT_SIZE ERR_OUT_WRITE = _swmm5.ERR_OUT_WRITE - -_swmm5.ERR_OUT_READ_swigconstant(_swmm5) ERR_OUT_READ = _swmm5.ERR_OUT_READ - -_swmm5.ERR_RAIN_FILE_SCRATCH_swigconstant(_swmm5) ERR_RAIN_FILE_SCRATCH = _swmm5.ERR_RAIN_FILE_SCRATCH - -_swmm5.ERR_RAIN_FILE_OPEN_swigconstant(_swmm5) ERR_RAIN_FILE_OPEN = _swmm5.ERR_RAIN_FILE_OPEN - -_swmm5.ERR_RAIN_FILE_DATA_swigconstant(_swmm5) ERR_RAIN_FILE_DATA = _swmm5.ERR_RAIN_FILE_DATA - -_swmm5.ERR_RAIN_FILE_SEQUENCE_swigconstant(_swmm5) ERR_RAIN_FILE_SEQUENCE = _swmm5.ERR_RAIN_FILE_SEQUENCE - -_swmm5.ERR_RAIN_FILE_FORMAT_swigconstant(_swmm5) ERR_RAIN_FILE_FORMAT = _swmm5.ERR_RAIN_FILE_FORMAT - -_swmm5.ERR_RAIN_IFACE_FORMAT_swigconstant(_swmm5) ERR_RAIN_IFACE_FORMAT = _swmm5.ERR_RAIN_IFACE_FORMAT - -_swmm5.ERR_RAIN_FILE_GAGE_swigconstant(_swmm5) ERR_RAIN_FILE_GAGE = _swmm5.ERR_RAIN_FILE_GAGE - -_swmm5.ERR_RUNOFF_FILE_OPEN_swigconstant(_swmm5) ERR_RUNOFF_FILE_OPEN = _swmm5.ERR_RUNOFF_FILE_OPEN - -_swmm5.ERR_RUNOFF_FILE_FORMAT_swigconstant(_swmm5) ERR_RUNOFF_FILE_FORMAT = _swmm5.ERR_RUNOFF_FILE_FORMAT - -_swmm5.ERR_RUNOFF_FILE_END_swigconstant(_swmm5) ERR_RUNOFF_FILE_END = _swmm5.ERR_RUNOFF_FILE_END - -_swmm5.ERR_RUNOFF_FILE_READ_swigconstant(_swmm5) ERR_RUNOFF_FILE_READ = _swmm5.ERR_RUNOFF_FILE_READ - -_swmm5.ERR_HOTSTART_FILE_NAMES_swigconstant(_swmm5) -ERR_HOTSTART_FILE_NAMES = _swmm5.ERR_HOTSTART_FILE_NAMES - -_swmm5.ERR_HOTSTART_FILE_OPEN_swigconstant(_swmm5) ERR_HOTSTART_FILE_OPEN = _swmm5.ERR_HOTSTART_FILE_OPEN - -_swmm5.ERR_HOTSTART_FILE_FORMAT_swigconstant(_swmm5) ERR_HOTSTART_FILE_FORMAT = _swmm5.ERR_HOTSTART_FILE_FORMAT - -_swmm5.ERR_HOTSTART_FILE_READ_swigconstant(_swmm5) ERR_HOTSTART_FILE_READ = _swmm5.ERR_HOTSTART_FILE_READ - -_swmm5.ERR_NO_CLIMATE_FILE_swigconstant(_swmm5) ERR_NO_CLIMATE_FILE = _swmm5.ERR_NO_CLIMATE_FILE - -_swmm5.ERR_CLIMATE_FILE_OPEN_swigconstant(_swmm5) ERR_CLIMATE_FILE_OPEN = _swmm5.ERR_CLIMATE_FILE_OPEN - -_swmm5.ERR_CLIMATE_FILE_READ_swigconstant(_swmm5) ERR_CLIMATE_FILE_READ = _swmm5.ERR_CLIMATE_FILE_READ - -_swmm5.ERR_CLIMATE_END_OF_FILE_swigconstant(_swmm5) ERR_CLIMATE_END_OF_FILE = _swmm5.ERR_CLIMATE_END_OF_FILE - -_swmm5.ERR_RDII_FILE_SCRATCH_swigconstant(_swmm5) ERR_RDII_FILE_SCRATCH = _swmm5.ERR_RDII_FILE_SCRATCH - -_swmm5.ERR_RDII_FILE_OPEN_swigconstant(_swmm5) ERR_RDII_FILE_OPEN = _swmm5.ERR_RDII_FILE_OPEN - -_swmm5.ERR_RDII_FILE_FORMAT_swigconstant(_swmm5) ERR_RDII_FILE_FORMAT = _swmm5.ERR_RDII_FILE_FORMAT - -_swmm5.ERR_ROUTING_FILE_OPEN_swigconstant(_swmm5) ERR_ROUTING_FILE_OPEN = _swmm5.ERR_ROUTING_FILE_OPEN - -_swmm5.ERR_ROUTING_FILE_FORMAT_swigconstant(_swmm5) ERR_ROUTING_FILE_FORMAT = _swmm5.ERR_ROUTING_FILE_FORMAT - -_swmm5.ERR_ROUTING_FILE_NOMATCH_swigconstant(_swmm5) ERR_ROUTING_FILE_NOMATCH = _swmm5.ERR_ROUTING_FILE_NOMATCH - -_swmm5.ERR_ROUTING_FILE_NAMES_swigconstant(_swmm5) ERR_ROUTING_FILE_NAMES = _swmm5.ERR_ROUTING_FILE_NAMES - -_swmm5.ERR_TABLE_FILE_OPEN_swigconstant(_swmm5) ERR_TABLE_FILE_OPEN = _swmm5.ERR_TABLE_FILE_OPEN - -_swmm5.ERR_TABLE_FILE_READ_swigconstant(_swmm5) ERR_TABLE_FILE_READ = _swmm5.ERR_TABLE_FILE_READ - -_swmm5.ERR_SYSTEM_swigconstant(_swmm5) ERR_SYSTEM = _swmm5.ERR_SYSTEM - -_swmm5.ERR_NOT_CLOSED_swigconstant(_swmm5) -ERR_NOT_CLOSED = _swmm5.ERR_NOT_CLOSED - -_swmm5.ERR_NOT_OPEN_swigconstant(_swmm5) -ERR_NOT_OPEN = _swmm5.ERR_NOT_OPEN - -_swmm5.ERR_FILE_SIZE_swigconstant(_swmm5) -ERR_FILE_SIZE = _swmm5.ERR_FILE_SIZE - -_swmm5.MAXERRMSG_swigconstant(_swmm5) +ERR_API_NOT_OPEN = _swmm5.ERR_API_NOT_OPEN +ERR_API_NOT_STARTED = _swmm5.ERR_API_NOT_STARTED +ERR_API_NOT_ENDED = _swmm5.ERR_API_NOT_ENDED +ERR_API_OBJECT_TYPE = _swmm5.ERR_API_OBJECT_TYPE +ERR_API_OBJECT_INDEX = _swmm5.ERR_API_OBJECT_INDEX +ERR_API_OBJECT_NAME = _swmm5.ERR_API_OBJECT_NAME +ERR_API_PROPERTY_TYPE = _swmm5.ERR_API_PROPERTY_TYPE +ERR_API_PROPERTY_VALUE = _swmm5.ERR_API_PROPERTY_VALUE +ERR_API_TIME_PERIOD = _swmm5.ERR_API_TIME_PERIOD MAXERRMSG = _swmm5.MAXERRMSG -def error_getMsg(i): - return _swmm5.error_getMsg(i) -error_getMsg = _swmm5.error_getMsg - -def error_getCode(i): - return _swmm5.error_getCode(i) -error_getCode = _swmm5.error_getCode +def error_getMsg(i, msg): + return _swmm5.error_getMsg(i, msg) def error_setInpError(errcode, s): return _swmm5.error_setInpError(errcode, s) -error_setInpError = _swmm5.error_setInpError -# This file is compatible with both classic and new-style classes. cvar = _swmm5.cvar diff --git a/swmm/swmm5/swmm5/climate.c b/swmm/swmm5/swmm5/climate.c index a44c5fa..336f0aa 100644 --- a/swmm/swmm5/swmm5/climate.c +++ b/swmm/swmm5/swmm5/climate.c @@ -2,36 +2,32 @@ // climate.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/10 (Build 5.1.001) -// 09/15/14 (Build 5.1.007) -// 03/19/15 (Build 5.1.008) -// 08/05/15 (Build 5.1.010) -// 08/01/16 (Build 5.1.011) -// 05/10/18 (Build 5.1.013) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) // Author: L. Rossman // // Climate related functions. // +// Update History +// ============== // Build 5.1.007: // - NCDC GHCN climate file format added. // - Monthly adjustments for temperature, evaporation & rainfall added. -// // Build 5.1.008: // - Monthly adjustments for hyd. conductivity added. // - Time series evaporation rates can now vary within a day. // - Evaporation rates are now properly updated when only flow routing // is being simulated. -// // Build 5.1.010: // - Hargreaves evaporation now computed using 7-day average temperatures. -// // Build 5.1.011: // - Monthly adjustment for hyd. conductivity <= 0 is ignored. -// // Build 5.1.013: // - Reads names of monthly adjustment patterns for various parameters // of a subcatchment from the [ADJUSTMENTS] section of input file. +// Build 5.2.0: +// - Reads temperature units for use with GHCND climate files. +// - Support added for relative file names. ///----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE @@ -55,8 +51,10 @@ static const int MAXDAYSPERMONTH = 32; // These variables are used when processing climate files. enum ClimateVarType {TMIN, TMAX, EVAP, WIND}; enum WindSpeedType {WDMV, AWND}; +enum TempUnitsType {DEG_C10, DEG_C, DEG_F}; static char* ClimateVarWords[] = {"TMIN", "TMAX", "EVAP", "WDMV", "AWND", NULL}; +static char* TempUnitsWords[] = {"C10", "C", "F", NULL}; //----------------------------------------------------------------------------- // Data Structures @@ -108,6 +106,7 @@ static char FileLine[MAXLINE+1]; // line from climate data file static int FileFieldPos[4]; // start of data fields for file record static int FileDateFieldPos; // start of date field for file record static int FileWindType; // wind speed type +static int FileTempUnits; // GHCND file temperature units (C10, C or F) //----------------------------------------------------------------------------- // External functions (defined in funcs.h) @@ -147,6 +146,7 @@ static void setTD3200FileValues(int param); static int isGhcndFormat(char* line); static void readGhcndFileLine(int *year, int *month); static void parseGhcndFileLine(void); +static double convertGhcndValue(int var, double v); //============================================================================= @@ -159,7 +159,7 @@ int climate_readParams(char* tok[], int ntoks) // // Format of data can be // TIMESERIES name -// FILE name +// FILE name (start) (units) // WINDSPEED MONTHLY v1 v2 ... v12 // WINDSPEED FILE // SNOWMELT v1 v2 ... v6 @@ -169,6 +169,7 @@ int climate_readParams(char* tok[], int ntoks) int i, j, k; double x[6], y; DateTime aDate; + char fname[MAXFNAME + 1]; // --- identify keyword k = findmatch(tok[0], TempKeyWords); @@ -194,7 +195,8 @@ int climate_readParams(char* tok[], int ntoks) // --- save name and usage mode of external climate file Fclimate.mode = USE_FILE; - sstrncpy(Fclimate.name, tok[1], MAXFNAME); + sstrncpy(fname, tok[1], MAXFNAME); + sstrncpy(Fclimate.name, addAbsolutePath(fname), MAXFNAME); // --- save starting date to read from file if one is provided Temp.fileStartDate = NO_DATE; @@ -207,6 +209,18 @@ int climate_readParams(char* tok[], int ntoks) Temp.fileStartDate = aDate; } } + + // --- file temperature units + FileTempUnits = DEG_F; + if (UnitSystem == SI) + FileTempUnits = DEG_C; + if (ntoks > 3) + { + i = findmatch(tok[3], TempUnitsWords); + if (i < 0) + return error_setInpError(ERR_KEYWORD, tok[3]); + FileTempUnits = i; + } break; case 2: // Wind speeds @@ -373,11 +387,11 @@ int climate_readAdjustments(char* tok[], int ntoks) // EVAPORATION v1 ... v12 // RAINFALL v1 ... v12 // CONDUCTIVITY v1 ... v12 -// N-PERV subcatchID patternID //(5.1.013 -// DSTORE subcatchID patternID // -// INFIL subcatchID patternID // +// N-PERV subcatchID patternID +// DSTORE subcatchID patternID +// INFIL subcatchID patternID { - int i, j; //(5.1.013) + int i, j; if (ntoks == 1) return 0; @@ -426,7 +440,6 @@ int climate_readAdjustments(char* tok[], int ntoks) return 0; } -//// Following code segments added to release 5.1.013. //// //(5.1.013) if ( match(tok[0], "N-PERV") ) { if ( ntoks < 3 ) return error_setInpError(ERR_ITEMS, ""); @@ -459,7 +472,6 @@ int climate_readAdjustments(char* tok[], int ntoks) Subcatch[i].infilPattern = j; return 0; } -//// return error_setInpError(ERR_KEYWORD, tok[0]); } @@ -550,14 +562,14 @@ void climate_openFile() // --- position file to begin reading climate file at either user-specified // month/year or at start of simulation period. rewind(Fclimate.file); - strcpy(FileLine, ""); + sstrncpy(FileLine, "", 0); if ( Temp.fileStartDate == NO_DATE ) datetime_decodeDate(StartDate, &FileYear, &FileMonth, &FileDay); else datetime_decodeDate(Temp.fileStartDate, &FileYear, &FileMonth, &FileDay); while ( !feof(Fclimate.file) ) { - strcpy(FileLine, ""); + sstrncpy(FileLine, "", 0); readFileLine(&y, &m); if ( y == FileYear && m == FileMonth ) break; } @@ -982,7 +994,7 @@ double getTempEvap(int day, double tave, double trng) double lamda = 2.50 - 0.002361 * ta; //latent heat of vaporization double dr = 1.0 + 0.033*cos(a*day); //relative earth-sun distance double phi = Temp.anglat*2.0*PI/360.0; //latitude angle (rad) - double del = 0.4093*sin(a*(284+day)); //solar declination angle (rad) + double del = 0.4093*sin(a*(284.+(double)day)); //solar declination angle (rad) double omega = acos(-tan(phi)*tan(del)); //sunset hour angle (rad) double ra = 37.6*dr* //extraterrestrial radiation (omega*sin(phi)*sin(del) + @@ -1097,11 +1109,9 @@ void readTD3200FileLine(int* y, int* m) char recdType[4] = ""; char year[5] = ""; char month[3] = ""; - int len; // --- check for minimum number of characters - len = strlen(FileLine); - if ( len < 30 ) + if ( strlen(FileLine) < 30 ) { report_writeErrorMsg(ERR_CLIMATE_FILE_READ, Fclimate.name); return; @@ -1134,11 +1144,9 @@ void readDLY0204FileLine(int* y, int* m) { char year[5] = ""; char month[3] = ""; - int len; // --- check for minimum number of characters - len = strlen(FileLine); - if ( len < 16 ) + if ( strlen(FileLine) < 16 ) { report_writeErrorMsg(ERR_CLIMATE_FILE_READ, Fclimate.name); return; @@ -1184,7 +1192,7 @@ void readFileValues() case DLY0204: parseDLY0204FileLine(); break; case GHCND: parseGhcndFileLine(); break; } - strcpy(FileLine, ""); + sstrncpy(FileLine, "", 0); } } @@ -1201,10 +1209,10 @@ void parseUserFileLine() int n; int y, m, d; char staID[80]; - char s0[80]; - char s1[80]; - char s2[80]; - char s3[80]; + char s0[80] = ""; + char s1[80] = ""; + char s2[80] = ""; + char s3[80] = ""; double x; // --- read day, Tmax, Tmin, Evap, & Wind from file line @@ -1275,15 +1283,13 @@ void setTD3200FileValues(int i) double x; int nValues; int j, k, d; - int lineLength; // --- parse number of days with data from cols. 27-29 of file line sstrncpy(valCount, &FileLine[27], 3); nValues = atoi(valCount); - lineLength = strlen(FileLine); // --- check for enough characters on line - if ( lineLength >= 12*nValues + 30 ) + if ( (int)strlen(FileLine) >= 12*nValues + 30 ) { // --- for each day's value for (j=0; j= 0 ) return TRUE; @@ -1463,59 +1469,98 @@ void parseGhcndFileLine() // wind speed. // { - int y, m, d, n, v; - double x; + int y, m, d, n, i; + double v; // --- parse day of month from date field n = sscanf(&FileLine[FileDateFieldPos], "%4d%2d%2d", &y, &m, &d); if ( n < 3 ) return; if ( d < 1 || d > 31 ) return; - // --- parse temperatures (in tenths of deg. C) to deg F - if ( FileFieldPos[TMAX] >= 0 ) - { - if ( sscanf(&FileLine[FileFieldPos[TMAX]], "%8d", &v) > 0 ) - { - if ( abs(v) < 9999 ) - FileData[TMAX][d] = (double)v*0.1*9.0/5.0 + 32.0; - } - } - if ( FileFieldPos[TMIN] >= 0 ) - { - if ( sscanf(&FileLine[FileFieldPos[TMIN]], "%8d", &v) > 0 ) - { - if ( abs(v) < 9999 ) - FileData[TMIN][d] = (double)v*0.1*9.0/5.0 + 32.0; - } - } - - // -- parse evaporation (in tenths of mm) to user units - if ( FileFieldPos[EVAP] >= 0 ) + // --- parse climate variables + for (i = TMIN; i <= WIND; i++) { - if ( sscanf(&FileLine[FileFieldPos[EVAP]], "%8d", &v) > 0 ) + if ( FileFieldPos[i] >= 0 ) { - if ( abs(v) < 9999 ) + if ( sscanf(&FileLine[FileFieldPos[i]], "%8lf", &v) > 0 ) { - x = (double)v * 0.1; - if ( UnitSystem == US ) x /= MMperINCH; - FileData[EVAP][d] = x; + if ( fabs(v) < 9999. ) + FileData[i][d] = convertGhcndValue(i, v); } } } +} + +//============================================================================= - // --- parse wind speed (in km/day for WDMV or tenths of m/s for AWND) - // to miles/hr - if ( FileFieldPos[WIND] >= 0 ) +double convertGhcndValue(int var, double v) +// +// Input: var = climate variable code +// v = climate variable value +// Output: climate variable value in SWMM's internal units +// Purpose: converts a climate variable value read from a NCDC GHCN Daily file +// to SWMM's internal units. +// +{ + switch (var) { - if ( sscanf(&FileLine[FileFieldPos[WIND]], "%8d", &v) > 0 ) - { - if ( abs(v) < 9999 ) + case TMIN: + case TMAX: + switch (FileTempUnits) { - if ( FileWindType == WDMV ) x = (double)v * 0.62137 / 24.; - else x = (double)v * 0.1 / 1000. * 0.62137 * 3600.; - FileData[WIND][d] = x; + case DEG_C10: // tenths deg. C ==> deg. F + return v / 10. * 9.0 / 5.0 + 32.0; + + case DEG_C: // deg. C ==> deg. F + return v * 9.0 / 5.0 + 32.0; + + default: // deg. F + return v; } - } + case EVAP: + switch (FileTempUnits) + { + case DEG_C10: // tenths mm ==> inches or mm + v /= 10.; + if (UnitSystem == US) v /= MMperINCH; + return v; + + case DEG_C: // mm ==> inches or mm + if (UnitSystem == US) v /= MMperINCH; + return v; + + default: // inches ==> inches or mm + if (UnitSystem == SI) v *= MMperINCH; + return v; + } + case WIND: + switch (FileTempUnits) + { + case DEG_C10: + // km/day ==> miles/hr + if (FileWindType == WDMV) + return v * 0.62137 / 24.; + // tenths m/s ==> miles/hr + else + return v / 10. / 1000. * 0.62137 * 3600.; + case DEG_C: + // km/day ==> miles/hr + if (FileWindType == WDMV) + return v * 0.62137 / 24.; + // m/s ==> miles/hr + else + return v / 1000. * 0.62137 * 3600.; + + default: + // miles ==> miles/hr + if (FileWindType == WDMV) + return v / 24.; + // miles/hr + else + return v; + } + default: + return v; } } @@ -1532,14 +1577,15 @@ void updateTempMoveAve(double tmin, double tmax) { double ta, // new day's average temperature (deg F) tr; // new day's temperature range (deg F) - int count = Tma.count; + int kount = Tma.count; + double count = kount; // --- find ta and tr from new day's min and max temperature ta = (tmin + tmax) / 2.0; tr = fabs(tmax - tmin); // --- if the array used to store previous days' temperatures is full - if ( count == Tma.maxCount ) + if ( kount == Tma.maxCount ) { // --- update the moving averages with the new day's value Tma.tAve = (Tma.tAve * count + ta - Tma.ta[Tma.front]) / count; diff --git a/swmm/swmm5/swmm5/consts.h b/swmm/swmm5/swmm5/consts.h index 829b089..c42235c 100644 --- a/swmm/swmm5/swmm5/consts.h +++ b/swmm/swmm5/swmm5/consts.h @@ -2,12 +2,8 @@ // consts.h // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/14 (Build 5.1.001) -// 08/01/16 (Build 5.1.011) -// 05/10/18 (Build 5.1.013) -// 03/01/20 (Build 5.1.014) -// 04/01/20 (Build 5.1.015) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) // Author: L. Rossman // // Various Constants @@ -16,12 +12,11 @@ #ifndef CONSTS_H #define CONSTS_H - //------------------ // General Constants //------------------ -#define VERSION 51015 +#define VERSION 52000 #define MAGICNUMBER 516114522 #define EOFMARK 0x1A // Use 0x04 for UNIX systems #define MAXTITLE 3 // Max. # title lines @@ -41,7 +36,9 @@ #define PI 3.141592654 // Value of pi #define GRAVITY 32.2 // accel. of gravity in US units #define SI_GRAVITY 9.81 // accel of gravity in SI units +/* DEPRECATED #define MAXFILESIZE 2147483647L // largest file size in bytes +*/ //----------------------------- // Units factor in Manning Eqn. @@ -72,7 +69,7 @@ //----------------------------------------------------- // Minimum flow depth and area for dynamic wave routing //----------------------------------------------------- -#define FUDGE 0.0001 // ft or ft2 +#define FUDGE 0.0001 // ft or ft2 //--------------------------- // Various conversion factors @@ -98,8 +95,8 @@ //--------------------------- // Token separator characters -//--------------------------- -#define SEPSTR " \t\n\r" +//--------------------------- +#define SEPSTR " \t\n\r" #endif //CONSTS_H diff --git a/swmm/swmm5/swmm5/controls.c b/swmm/swmm5/swmm5/controls.c index 33e1b21..d2af281 100644 --- a/swmm/swmm5/swmm5/controls.c +++ b/swmm/swmm5/swmm5/controls.c @@ -2,12 +2,8 @@ // controls.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/21/14 (Build 5.1.001) -// 03/19/15 (Build 5.1.008) -// 04/30/15 (Build 5.1.009) -// 08/05/15 (Build 5.1.010) -// 08/01/16 (Build 5.1.011) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) // Author: L. Rossman // // Rule-based controls functions. @@ -36,20 +32,22 @@ // E.g.: Pump abc status = OFF // Weir xyz setting = 0.5 // +// Update History +// ============== // Build 5.1.008: // - Support added for r.h.s. variables in rule premises. // - Node volume added as a premise variable. -// // Build 5.1.009: // - Fixed problem with parsing a RHS premise variable. -// // Build 5.1.010: // - Support added for link TIMEOPEN & TIMECLOSED premises. -// // Build 5.1.011: // - Support added for DAYOFYEAR attribute. // - Modulated controls no longer included in reported control actions. -// +// Build 5.2.0: +// - Additional attributes added to condition clauses. +// - Support added for named variables in condition clauses. +// - Support added for math expressions in condition clauses. //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE @@ -62,26 +60,31 @@ // Constants //----------------------------------------------------------------------------- enum RuleState {r_RULE, r_IF, r_AND, r_OR, r_THEN, r_ELSE, r_PRIORITY, - r_ERROR}; -enum RuleObject {r_NODE, r_LINK, r_CONDUIT, r_PUMP, r_ORIFICE, r_WEIR, - r_OUTLET, r_SIMULATION}; -enum RuleAttrib {r_DEPTH, r_HEAD, r_VOLUME, r_INFLOW, r_FLOW, r_STATUS, - r_SETTING, r_TIMEOPEN, r_TIMECLOSED, r_TIME, r_DATE, - r_CLOCKTIME, r_DAYOFYEAR, r_DAY, r_MONTH}; + r_VARIABLE, r_EXPRESSION, r_ERROR}; +enum RuleObject {r_GAGE, r_NODE, r_LINK, r_CONDUIT, r_PUMP, r_ORIFICE, + r_WEIR, r_OUTLET, r_SIMULATION}; +enum RuleAttrib {r_DEPTH, r_MAXDEPTH, r_HEAD, r_VOLUME, r_INFLOW, + r_FLOW, r_FULLFLOW, r_FULLDEPTH, r_STATUS, r_SETTING, + r_LENGTH, r_SLOPE, r_VELOCITY, r_TIMEOPEN, r_TIMECLOSED, + r_TIME, r_DATE, r_CLOCKTIME, r_DAYOFYEAR, r_DAY, r_MONTH}; enum RuleRelation {EQ, NE, LT, LE, GT, GE}; enum RuleSetting {r_CURVE, r_TIMESERIES, r_PID, r_NUMERIC}; +#define MAXVARNAME 32 + static char* ObjectWords[] = - {"NODE", "LINK", "CONDUIT", "PUMP", "ORIFICE", "WEIR", "OUTLET", - "SIMULATION", NULL}; + {"GAGE", "NODE", "LINK", "CONDUIT", "PUMP", "ORIFICE", "WEIR", "OUTLET", + "SIMULATION", NULL}; static char* AttribWords[] = - {"DEPTH", "HEAD", "VOLUME", "INFLOW", "FLOW", "STATUS", "SETTING", - "TIMEOPEN", "TIMECLOSED","TIME", "DATE", "CLOCKTIME", "DAYOFYEAR", - "DAY", "MONTH", NULL}; + {"DEPTH", "MAXDEPTH", "HEAD", "VOLUME", "INFLOW", + "FLOW", "FULLFLOW", "FULLDEPTH", "STATUS", "SETTING", + "LENGTH", "SLOPE", "VELOCITY", "TIMEOPEN", "TIMECLOSED", + "TIME", "DATE", "CLOCKTIME", "DAYOFYEAR", "DAY", "MONTH", NULL}; static char* RelOpWords[] = {"=", "<>", "<", "<=", ">", ">=", NULL}; static char* StatusWords[] = {"OFF", "ON", NULL}; static char* ConduitWords[] = {"CLOSED", "OPEN", NULL}; static char* SettingTypeWords[] = {"CURVE", "TIMESERIES", "PID", NULL}; +static char* IntensityWord = "INTENSITY"; //----------------------------------------------------------------------------- // Data Structures @@ -89,15 +92,30 @@ static char* SettingTypeWords[] = {"CURVE", "TIMESERIES", "PID", NULL}; // Rule Premise Variable struct TVariable { - int node; // index of a node (-1 if N/A) - int link; // index of a link (-1 if N/A) - int attribute; // type of attribute for node/link + int object; // type of object + int index; // index in object's array + int attribute; // object's attribute +}; + +// Named Variable +struct TNamedVariable +{ + struct TVariable variable; // a rule premise variable + char name[MAXVARNAME+1]; // name used in math expression +}; + +// Rule Premise Function +struct TExpression +{ + MathExpr* expression; // tokenized math expression + char name[MAXVARNAME+1]; // expression name }; // Rule Premise Clause struct TPremise { int type; // clause type (IF/AND/OR) + int exprIndex; // expression index (-1 if N/A) struct TVariable lhsVar; // left hand side variable struct TVariable rhsVar; // right hand side variable int relation; // relational operator (>, <, =, etc) @@ -149,11 +167,22 @@ double SetPoint; // value of controller setpoint DateTime CurrentDate; // current date in whole days DateTime CurrentTime; // current time of day (decimal) +int VariableCount; +int ExpressionCount; +int CurrentVariable; +int CurrentExpression; +struct TNamedVariable* NamedVariable; // array of named variables +struct TExpression* Expression; // array of math expressions + //----------------------------------------------------------------------------- // External functions (declared in funcs.h) //----------------------------------------------------------------------------- // controls_create // controls_delete +// controls_init +// controls_addToCount +// controls_addVariable +// controls_addExpression // controls_addRuleClause // controls_evaluate @@ -161,7 +190,7 @@ DateTime CurrentTime; // current time of day (decimal) // Local functions //----------------------------------------------------------------------------- int addPremise(int r, int type, char* Tok[], int nToks); -int getPremiseVariable(char* tok[], int* k, struct TVariable* v); +int getPremiseVariable(char* tok[], int nToks, int* k, struct TVariable* v); int getPremiseValue(char* token, int attrib, double* value); int addAction(int r, char* Tok[], int nToks); @@ -183,6 +212,43 @@ int setActionSetting(char* tok[], int nToks, int* curve, int* tseries, void updateActionValue(struct TAction* a, DateTime currentTime, double dt); double getPIDSetting(struct TAction* a, double dt); +int getVariableIndex(char* varName); +double getNamedVariableValue(int varIndex); +int getExpressionIndex(char* exprName); +int getGageAttrib(char* token); +double getRainValue(struct TVariable v); + +//============================================================================= + +void controls_init() +// +// Input: none +// Output: none +// Purpose: initializes the control rule system. +// +{ + Rules = NULL; + NamedVariable = NULL; + Expression = NULL; + RuleCount = 0; + VariableCount = 0; + ExpressionCount = 0; +} + +//============================================================================= + +void controls_addToCount(char* s) +// +// Input: s = either VARIABLE or EXPRESSION +// Output: none +// Purpose: updates the number of named variables or math expressions used +// by control rules. +// +{ + if (match(s, w_VARIABLE)) VariableCount++; + else if (match(s, w_EXPRESSION)) ExpressionCount++; +} + //============================================================================= int controls_create(int n) @@ -192,21 +258,38 @@ int controls_create(int n) // Purpose: creates an array of control rules. // { - int r; - ActionList = NULL; - InputState = r_PRIORITY; - RuleCount = n; - if ( n == 0 ) return 0; - Rules = (struct TRule *) calloc(RuleCount, sizeof(struct TRule)); - if (Rules == NULL) return ERR_MEMORY; - for ( r=0; r 0) + { + Rules = (struct TRule *) calloc(RuleCount, sizeof(struct TRule)); + if (Rules == NULL) return ERR_MEMORY; + for ( r=0; r 0) + { + NamedVariable = (struct TNamedVariable *) calloc(VariableCount, + sizeof(struct TNamedVariable)); + if (NamedVariable == NULL) return ERR_MEMORY; + } + if (ExpressionCount > 0) { - Rules[r].ID = NULL; - Rules[r].firstPremise = NULL; - Rules[r].lastPremise = NULL; - Rules[r].thenActions = NULL; - Rules[r].elseActions = NULL; - Rules[r].priority = 0.0; + Expression = (struct TExpression *) calloc(ExpressionCount, + sizeof(struct TExpression)); + if (Expression == NULL) return ERR_MEMORY; } return 0; } @@ -220,6 +303,16 @@ void controls_delete(void) // Purpose: deletes all control rules. // { + int i; + + for (i = 0; i < ExpressionCount; i++) + { + mathexpr_delete(Expression[i].expression); + Expression[i].expression = NULL; + } + FREE(Expression); + FREE(NamedVariable); + if ( RuleCount == 0 ) return; deleteActionList(); deleteRules(); @@ -227,6 +320,119 @@ void controls_delete(void) //============================================================================= +int controls_addVariable(char* tok[], int nToks) +// +// Input: tok = an array of string tokens +// n = the size of tok[] +// Output: returns error code +// Purpose: adds a named variable to the control rule system from a +// tokenized line of input with formats: +// VARIABLE name = Object id attribute +// VARIABLE name = SIMULATION attribute +// +{ + struct TVariable v1; + int k, err; + + CurrentVariable++; + if (nToks < 5) return ERR_ITEMS; + if (findExactMatch(tok[1], AttribWords) >= 0) + return error_setInpError(ERR_KEYWORD, tok[1]); + if (!match(tok[2], "=")) return error_setInpError(ERR_KEYWORD, tok[2]); + if (!match(tok[3], "SIMULATION") && nToks < 6) return ERR_ITEMS; + k = 3; + err = getPremiseVariable(tok, nToks, &k, &v1); + if (err > 0) return err; + k = CurrentVariable; + NamedVariable[k].variable = v1; + sstrncpy(NamedVariable[k].name, tok[1], MAXVARNAME); + return 0; +} + +//============================================================================= + +int controls_addExpression(char* tok[], int nToks) +// +// Input: tok = an array of string tokens +// n = number of tokens +// Output: returns error code +// Purpose: adds a math expression to the control rule system from a +// a tokenized line of input with format: +// EXPRESSION name = +// +{ + int i, k; + char s[MAXLINE + 1]; + MathExpr* expr; + + CurrentExpression++; + if (nToks < 4) return ERR_ITEMS; + k = CurrentExpression; + Expression[k].expression = NULL; + sstrncpy(Expression[k].name, tok[1], MAXVARNAME); + sstrncpy(s, tok[3], MAXLINE); + for (i = 4; i < nToks; i++) + { + sstrcat(s, " ", MAXLINE); + sstrcat(s, tok[i], MAXLINE); + } + + expr = mathexpr_create(s, getVariableIndex); + if (expr == NULL) + return error_setInpError(ERR_MATH_EXPR, ""); + + Expression[k].expression = expr; + return 0; +} + +//============================================================================= + +int getVariableIndex(char* varName) +// +// Input: varName = string containing a variable name +// Output: returns the index of the named variable or -1 if not found +// Purpose: finds the array index of a named variable. +// +{ + int i; + for (i = 0; i < VariableCount; i++) + { + if (match(varName, NamedVariable[i].name)) return i; + } + return -1; +} + +//============================================================================= + +double getNamedVariableValue(int varIndex) +// +// Input: varIndex = index of a named variable +// Output: returns the current value of the variable +// Purpose: finds the value of a named variable. +// +{ + return getVariableValue(NamedVariable[varIndex].variable); +} + +//============================================================================= + +int getExpressionIndex(char* exprName) +// +// Input: exprName = string containing an expression name +// Output: returns the index of the expression or -1 if not found +// Purpose: finds the array index of a math expression +// +{ + int i; + for (i = 0; i < ExpressionCount; i++) + { + if (match(exprName, Expression[i].name)) return i; + } + return -1; +} + +//============================================================================= + int controls_addRuleClause(int r, int keyword, char* tok[], int nToks) // // Input: r = rule index @@ -359,53 +565,84 @@ int addPremise(int r, int type, char* tok[], int nToks) struct TPremise* p; struct TVariable v1; struct TVariable v2; + int obj, exprIndex, varIndex = -1; - // --- check for minimum number of tokens - if ( nToks < 5 ) return ERR_ITEMS; - - // --- get LHS variable + // --- initialize LHS variable v1 + if (nToks < 4) return ERR_ITEMS; + v1.attribute = -1; + v1.object = -1; + v1.index = -1; n = 1; - err = getPremiseVariable(tok, &n, &v1); - if ( err > 0 ) return err; + + // --- check if 2nd token is a math expression + exprIndex = getExpressionIndex(tok[1]); + + // --- if not then check if it's a named variable + if (exprIndex < 0) + { + varIndex = getVariableIndex(tok[n]); + if (varIndex >= 0) + { + v1 = NamedVariable[varIndex].variable; + } + + // otherwise parse object|index|attribute tokens + else + { + err = getPremiseVariable(tok, nToks, &n, &v1); + if ( err > 0 ) return err; + } + } // --- get relational operator n++; + if ( n >= nToks ) return error_setInpError(ERR_ITEMS, ""); relation = findExactMatch(tok[n], RelOpWords); if ( relation < 0 ) return error_setInpError(ERR_KEYWORD, tok[n]); - n++; - // --- initialize RHS variable + // --- initialize RHS variable v2 v2.attribute = -1; - v2.link = -1; - v2.node = -1; + v2.object = -1; + v2.index = -1; + n++; + if (n >= nToks) return error_setInpError(ERR_ITEMS, ""); - // --- check that more tokens remain - if ( n >= nToks ) return error_setInpError(ERR_ITEMS, ""); - - // --- see if a RHS variable is supplied - if ( findmatch(tok[n], ObjectWords) >= 0 && n + 3 >= nToks ) + // --- check for named RHS variable + varIndex = getVariableIndex(tok[n]); + if (varIndex >= 0) { - err = getPremiseVariable(tok, &n, &v2); - if ( err > 0 ) return ERR_RULE; - if ( v1.attribute != v2.attribute) - report_writeWarningMsg(WARN11, Rules[r].ID); + v2 = NamedVariable[varIndex].variable; } - // --- otherwise get value to which LHS variable is compared to + // --- check for object|index|attribute variable else { - err = getPremiseValue(tok[n], v1.attribute, &value); - n++; + obj = findmatch(tok[n], ObjectWords); + if (obj >= 0) + { + err = getPremiseVariable(tok, nToks, &n, &v2); + if ( err > 0 ) return ERR_RULE; + if (exprIndex < 0 && v1.attribute != v2.attribute) + report_writeWarningMsg(WARN11, Rules[r].ID); + } + + // --- check for a single RHS value + else + { + err = getPremiseValue(tok[n], v1.attribute, &value); + if ( err > 0 ) return err; + } } - if ( err > 0 ) return err; // --- make sure another clause is not on same line + n++; if ( n < nToks && findmatch(tok[n], RuleKeyWords) >= 0 ) return ERR_RULE; // --- create the premise object p = (struct TPremise *) malloc(sizeof(struct TPremise)); if ( !p ) return ERR_MEMORY; p->type = type; + p->exprIndex = exprIndex; p->lhsVar = v1; p->rhsVar = v2; p->relation = relation; @@ -425,19 +662,19 @@ int addPremise(int r, int type, char* tok[], int nToks) //============================================================================= -int getPremiseVariable(char* tok[], int* k, struct TVariable* v) +int getPremiseVariable(char* tok[], int nToks, int* k, struct TVariable* v) // -// Input: tok = array of string tokens containing premise statement +// Input: tok = array of string tokens +// nToks = number of tokens // k = index of current token // Output: returns an error code; updates k to new current token and // places identity of specified variable in v -// Purpose: parses a variable (e.g., Node 123 Depth) specified in a -// premise clause of a control rule. +// Purpose: parses a variable (e.g., Node 123 Depth) used in a control rule. // { int n = *k; - int node = -1; - int link = -1; + int object = -1; + int index = -1; int obj, attrib; // --- get object type @@ -446,11 +683,19 @@ int getPremiseVariable(char* tok[], int* k, struct TVariable* v) // --- get object index from its name n++; + if (n >= nToks) return error_setInpError(ERR_ITEMS, ""); switch (obj) { + case r_GAGE: + index = project_findObject(GAGE, tok[n]); + if (index < 0) return error_setInpError(ERR_NAME, tok[n]); + object = r_GAGE; + break; + case r_NODE: - node = project_findObject(NODE, tok[n]); - if ( node < 0 ) return error_setInpError(ERR_NAME, tok[n]); + index = project_findObject(NODE, tok[n]); + if ( index < 0 ) return error_setInpError(ERR_NAME, tok[n]); + object = r_NODE; break; case r_LINK: @@ -459,21 +704,32 @@ int getPremiseVariable(char* tok[], int* k, struct TVariable* v) case r_ORIFICE: case r_WEIR: case r_OUTLET: - link = project_findObject(LINK, tok[n]); - if ( link < 0 ) return error_setInpError(ERR_NAME, tok[n]); + index = project_findObject(LINK, tok[n]); + if ( index < 0 ) return error_setInpError(ERR_NAME, tok[n]); + object = r_LINK; break; default: n--; } n++; + if (n >= nToks) return error_setInpError(ERR_ITEMS, ""); // --- get attribute index from its name - attrib = findmatch(tok[n], AttribWords); + if (object == r_GAGE) + attrib = getGageAttrib(tok[n]); + else + attrib = findmatch(tok[n], AttribWords); if ( attrib < 0 ) return error_setInpError(ERR_KEYWORD, tok[n]); // --- check that attribute belongs to object type - if ( obj == r_NODE ) switch (attrib) + if (obj == r_GAGE) + { + + } + + else if ( obj == r_NODE ) switch (attrib) { case r_DEPTH: + case r_MAXDEPTH: case r_HEAD: case r_VOLUME: case r_INFLOW: break; @@ -481,30 +737,36 @@ int getPremiseVariable(char* tok[], int* k, struct TVariable* v) } // --- check for link TIMEOPEN & TIMECLOSED attributes - else if ( link >= 0 && - ( (attrib == r_TIMEOPEN || - attrib == r_TIMECLOSED) + else if ( object == r_LINK && index >= 0 && + ( (attrib == r_TIMEOPEN || attrib == r_TIMECLOSED) )) { - + // nothing to do here } else if ( obj == r_LINK || obj == r_CONDUIT ) switch (attrib) { case r_STATUS: case r_DEPTH: - case r_FLOW: break; + case r_FULLFLOW: + case r_FULLDEPTH: + case r_FLOW: + case r_LENGTH: + case r_SLOPE: + case r_VELOCITY: break; default: return error_setInpError(ERR_KEYWORD, tok[n]); } else if ( obj == r_PUMP ) switch (attrib) { case r_FLOW: + case r_SETTING: case r_STATUS: break; default: return error_setInpError(ERR_KEYWORD, tok[n]); } else if ( obj == r_ORIFICE || obj == r_WEIR || obj == r_OUTLET ) switch (attrib) { + case r_FLOW: case r_SETTING: break; default: return error_setInpError(ERR_KEYWORD, tok[n]); } @@ -520,8 +782,8 @@ int getPremiseVariable(char* tok[], int* k, struct TVariable* v) } // --- populate variable structure - v->node = node; - v->link = link; + v->object = object; + v->index = index; v->attribute = attrib; *k = n; return 0; @@ -529,6 +791,33 @@ int getPremiseVariable(char* tok[], int* k, struct TVariable* v) //============================================================================= +int getGageAttrib(char* token) +// +// Input: token = a string token +// Output: returns an attribute code or -1 if an error occurred +// Purpose: determines the atrribute code for a rain gage variable. +// Note: a valid token is INTENSITY for current rainfall intensity +// (attribute code = 0) or nHR_PRECIP for total rain depth +// over past n hours (attribute code = n). +// +{ + int attrib; + + // --- check if token is currrent rainfall intensity + if (match(token, IntensityWord)) + return 0; + + // --- token is past rain depth - read number of past hours + attrib = atoi(token); + + // --- check that number of hours is in allowable range + if (attrib < 1 || attrib > MAXPASTRAIN) + return -1; + return attrib; +} + +//============================================================================= + int getPremiseValue(char* token, int attrib, double* value) // // Input: token = a string token @@ -544,7 +833,7 @@ int getPremiseValue(char* token, int attrib, double* value) { case r_STATUS: *value = findmatch(token, StatusWords); - if ( *value < 0.0 ) *value = findmatch(token, ConduitWords); + if ( *value < 0.0 ) *value = findmatch(token, ConduitWords); if ( *value < 0.0 ) return error_setInpError(ERR_KEYWORD, token); break; @@ -576,8 +865,8 @@ int getPremiseValue(char* token, int attrib, double* value) break; case r_DAYOFYEAR: - strncpy(strDate, token, 6); - strcat(strDate, "/1947"); + sstrncpy(strDate, token, 6); + sstrcat(strDate, "/1947", 25); if ( datetime_strToDate(strDate, value) ) { *value = datetime_dayOfYear(*value); @@ -626,9 +915,9 @@ int addAction(int r, char* tok[], int nToks) switch (obj) { case r_CONDUIT: - if ( Link[link].type != CONDUIT ) - return error_setInpError(ERR_NAME, tok[2]); - break; + if ( Link[link].type != CONDUIT ) + return error_setInpError(ERR_NAME, tok[2]); + break; case r_PUMP: if ( Link[link].type != PUMP ) return error_setInpError(ERR_NAME, tok[2]); @@ -830,11 +1119,11 @@ double getPIDSetting(struct TAction* a, double dt) // a->e2 = error from two time steps ago { double e0, setting; - double p, i, d, update; - double tolerance = 0.0001; + double p, i, d, update; + double tolerance = 0.0001; - // --- convert time step from days to minutes - dt *= 1440.0; + // --- convert time step from days to minutes + dt *= 1440.0; // --- determine relative error in achieving controller set point e0 = SetPoint - ControlValue; @@ -844,24 +1133,24 @@ double getPIDSetting(struct TAction* a, double dt) else e0 = e0/ControlValue; } - // --- reset previous errors to 0 if controller gets stuck - if (fabs(e0 - a->e1) < tolerance) - { - a->e2 = 0.0; - a->e1 = 0.0; - } + // --- reset previous errors to 0 if controller gets stuck + if (fabs(e0 - a->e1) < tolerance) + { + a->e2 = 0.0; + a->e1 = 0.0; + } // --- use the recursive form of the PID controller equation to // determine the new setting for the controlled link - p = (e0 - a->e1); - if ( a->ki == 0.0 ) i = 0.0; - else i = e0 * dt / a->ki; - d = a->kd * (e0 - 2.0*a->e1 + a->e2) / dt; - update = a->kp * (p + i + d); - if ( fabs(update) < tolerance ) update = 0.0; - setting = Link[a->link].targetSetting + update; - - // --- update previous errors + p = (e0 - a->e1); + if ( a->ki == 0.0 ) i = 0.0; + else i = e0 * dt / a->ki; + d = a->kd * (e0 - 2.0*a->e1 + a->e2) / dt; + update = a->kp * (p + i + d); + if ( fabs(update) < tolerance ) update = 0.0; + setting = Link[a->link].targetSetting + update; + + // --- update previous errors a->e2 = a->e1; a->e1 = e0; @@ -900,13 +1189,13 @@ void updateActionList(struct TAction* a) } // --- action not listed so add it to ActionList - if ( !listItem ) + listItem = (struct TActionList *) malloc(sizeof(struct TActionList)); + if (listItem) { - listItem = (struct TActionList *) malloc(sizeof(struct TActionList)); listItem->next = ActionList; ActionList = listItem; + listItem->action = a; } - listItem->action = a; } //============================================================================= @@ -959,10 +1248,21 @@ int evaluatePremise(struct TPremise* p, double tStep) double lhsValue, rhsValue; int result = FALSE; - lhsValue = getVariableValue(p->lhsVar); + // --- check if left hand side (lhs) of premise is an expression + if (p->exprIndex >= 0) + lhsValue = mathexpr_eval(Expression[p->exprIndex].expression, + getNamedVariableValue); + + // --- otherwise get value of the lhs variable + else + lhsValue = getVariableValue(p->lhsVar); + + // --- if right hand side (rhs) of premise is a variable then get its value if ( p->value == MISSING ) rhsValue = getVariableValue(p->rhsVar); else rhsValue = p->value; if ( lhsValue == MISSING || rhsValue == MISSING ) return FALSE; + + // --- compare the lhs of the premise to the rhs switch (p->lhsVar.attribute) { case r_TIME: @@ -982,8 +1282,13 @@ int evaluatePremise(struct TPremise* p, double tStep) double getVariableValue(struct TVariable v) { - int i = v.node; - int j = v.link; + int i = -1; // a node index + int j = -1; // a link index + + if (v.object == r_GAGE) + return getRainValue(v); + if (v.object == r_NODE) i = v.index; + if (v.object == r_LINK) j = v.index; switch ( v.attribute ) { @@ -1011,7 +1316,9 @@ double getVariableValue(struct TVariable v) else return Link[j].setting; case r_SETTING: - if ( j < 0 || (Link[j].type != ORIFICE && Link[j].type != WEIR) ) + if ( j < 0 || (Link[j].type != PUMP && + Link[j].type != ORIFICE && + Link[j].type != WEIR) ) return MISSING; else return Link[j].setting; @@ -1019,12 +1326,34 @@ double getVariableValue(struct TVariable v) if ( j < 0 ) return MISSING; else return Link[j].direction*Link[j].newFlow*UCF(FLOW); + case r_FULLFLOW: + case r_FULLDEPTH: + case r_VELOCITY: + case r_LENGTH: + case r_SLOPE: + if ( j < 0 ) return MISSING; + else if (Link[j].type != CONDUIT) return MISSING; + switch (v.attribute) + { + case r_FULLFLOW: return Link[j].qFull * UCF(FLOW); + case r_FULLDEPTH: return Link[j].xsect.yFull * UCF(LENGTH); + case r_VELOCITY: + return link_getVelocity(j, Link[j].newFlow, Link[j].newDepth) + * UCF(LENGTH); + case r_LENGTH: return Conduit[Link[j].subIndex].length * UCF(LENGTH); + case r_SLOPE: return Conduit[Link[j].subIndex].slope; + default: return MISSING; + } case r_DEPTH: if ( j >= 0 ) return Link[j].newDepth*UCF(LENGTH); else if ( i >= 0 ) return Node[i].newDepth*UCF(LENGTH); else return MISSING; + case r_MAXDEPTH: + if (i >= 0) return Node[i].fullDepth*UCF(LENGTH); + else return MISSING; + case r_HEAD: if ( i < 0 ) return MISSING; return (Node[i].newDepth + Node[i].invertElev) * UCF(LENGTH); @@ -1053,6 +1382,23 @@ double getVariableValue(struct TVariable v) //============================================================================= +double getRainValue(struct TVariable v) +// +// Input: v = a rule premise variable for a rain gage +// Output: returns current or past rainfall amount +// Purpose: retrieves either the current rainfall intensity or the past +// rainfall total for a rain gage. +// +{ + if (v.index < 0) return MISSING; + else if (Gage[v.index].isUsed == FALSE) return 0.0; + else if (v.attribute == 0) + return Gage[v.index].rainfall; + else return gage_getPastRain(v.index, v.attribute); +} + +//============================================================================= + int compareTimes(double lhsValue, int relation, double rhsValue, double halfStep) // // Input: lhsValue = date/time value on left hand side of relation diff --git a/swmm/swmm5/swmm5/culvert.c b/swmm/swmm5/swmm5/culvert.c index 37c7c8d..5f62877 100644 --- a/swmm/swmm5/swmm5/culvert.c +++ b/swmm/swmm5/swmm5/culvert.c @@ -2,9 +2,8 @@ // culvert.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/14 (Build 5.1.001) -// 05/10/18 (Build 5.1.013) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) // Author: L. Rossman // // Culvert equations for SWMM5 @@ -12,6 +11,8 @@ // Computes flow reduction in a culvert-type conduit due to // inlet control using equations from the FHWA HEC-5 circular. // +// Update History +// ============== // Build 5.1.013: // - C parameter corrected for Arch, Corrugated Metal, Mitered culvert. //----------------------------------------------------------------------------- @@ -108,7 +109,7 @@ static const double Params[58][5] = { // Arch, Corrugated Metal {1.0, 0.0083, 2.00, 0.0379, 0.69}, //90 deg headwall - {1.0, 0.0300, 1.00, 0.0473, 0.75}, //Mitered to slope //(5.1.013) + {1.0, 0.0300, 1.00, 0.0473, 0.75}, //Mitered to slope {1.0, 0.0340, 1.50, 0.0496, 0.57}, //Thin wall projecting // Circular Culvert @@ -163,10 +164,10 @@ static double getTransitionFlow(int code, double h, double h1, double h2, TCulvert* culvert); static double getForm1Flow(double h, TCulvert* culvert); static double form1Eqn(double yc, void* p); - +/* static void report_CulvertControl(int j, double q0, double q, int condition, double yRatio); //for debugging only - +*/ //============================================================================= @@ -391,7 +392,7 @@ double form1Eqn(double yc, void* p) } //============================================================================= - +/* void report_CulvertControl(int j, double q0, double q, int condition, double yRatio) // // Used for debugging only @@ -407,3 +408,4 @@ void report_CulvertControl(int j, double q0, double q, int condition, double yRa "\n %11s: %8s Culvert %s flow reduced from %.3f to %.3f cfs for %s flow (%.2f).", theDate, theTime, Link[j].ID, q0, q, conditionTxt[condition], yRatio); } +*/ diff --git a/swmm/swmm5/swmm5/datetime.c b/swmm/swmm5/swmm5/datetime.c index e139ff2..d96ef7f 100644 --- a/swmm/swmm5/swmm5/datetime.c +++ b/swmm/swmm5/swmm5/datetime.c @@ -2,17 +2,17 @@ // datetime.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/14 (Build 5.1.001) -// 08/01/16 (Build 5.1.011) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) // Author: L. Rossman // // DateTime functions. // -// Build 5.1.011 +// Update History +// ============== +// Build 5.1.011: // - decodeTime() no longer rounds up. // - New getTimeStamp function added. -// //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE @@ -125,7 +125,8 @@ DateTime datetime_encodeDate(int year, int month, int day) { for (j = 0; j < month-1; j++) day += DaysPerMonth[i][j]; i = year - 1; - return i*365 + i/4 - i/100 + i/400 + day - DateDelta; + i = i*365 + i/4 - i/100 + i/400 + day - DateDelta; + return i; } else return -DateDelta; } @@ -249,27 +250,23 @@ void datetime_dateToStr(DateTime date, char* s) { int y, m, d; - char dateStr[DATE_STR_SIZE]; datetime_decodeDate(date, &y, &m, &d); switch (DateFormat) { case Y_M_D: - sprintf(dateStr, "%4d-%3s-%02d", y, MonthTxt[m-1], d); + snprintf(s, DATE_STR_SIZE, "%4d-%3s-%02d", y, MonthTxt[m-1], d); break; case M_D_Y: //sprintf(dateStr, "%3s-%02d-%4d", MonthTxt[m-1], d, y); - sprintf(dateStr, "%02d/%02d/%04d", m, d, y); + snprintf(s, DATE_STR_SIZE, "%02d/%02d/%04d", m, d, y); break; default: - sprintf(dateStr, "%02d-%3s-%4d", d, MonthTxt[m-1], y); + snprintf(s, DATE_STR_SIZE, "%02d-%3s-%4d", d, MonthTxt[m-1], y); } - strcpy(s, dateStr); } -//============================================================================= - void datetime_timeToStr(DateTime time, char* s) // Input: time = decimal fraction of a day @@ -278,10 +275,8 @@ void datetime_timeToStr(DateTime time, char* s) { int hr, min, sec; - char timeStr[TIME_STR_SIZE]; datetime_decodeTime(time, &hr, &min, &sec); - sprintf(timeStr, "%02d:%02d:%02d", hr, min, sec); - strcpy(s, timeStr); + snprintf(s, TIME_STR_SIZE, "%02d:%02d:%02d", hr, min, sec); } //============================================================================= @@ -524,10 +519,10 @@ void datetime_getTimeStamp(int fmt, DateTime aDate, int stampSize, char* timeSta char timeStr[TIME_STR_SIZE]; int oldDateFormat = DateFormat; - if ( stampSize < DATE_STR_SIZE + TIME_STR_SIZE + 2 ) return; + if ( stampSize < TIME_STAMP_SIZE ) return; datetime_setDateFormat(fmt); datetime_dateToStr(aDate, dateStr); DateFormat = oldDateFormat; datetime_timeToStr(aDate, timeStr); - sprintf(timeStamp, "%s %s", dateStr, timeStr); + snprintf(timeStamp, stampSize, "%s %s", dateStr, timeStr); } diff --git a/swmm/swmm5/swmm5/datetime.h b/swmm/swmm5/swmm5/datetime.h index 8c84b3b..98b87b4 100644 --- a/swmm/swmm5/swmm5/datetime.h +++ b/swmm/swmm5/swmm5/datetime.h @@ -2,9 +2,8 @@ // datetime.h // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/14 (Build 5.1.001) -// 08/01/16 (Build 5.1.011) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) // Author: L. Rossman // // The DateTime type is used to store date and time values. It is @@ -14,7 +13,9 @@ // passed since 12/31/1899. The fractional part of a DateTime value is the // fraction of a 24 hour day that has elapsed. // -// Build 5.1.011 +// Update History +// ============== +// Build 5.1.011: // - New getTimeStamp function added. //----------------------------------------------------------------------------- @@ -30,6 +31,7 @@ typedef double DateTime; #define NO_DATE -693594 // 1/1/0001 #define DATE_STR_SIZE 12 #define TIME_STR_SIZE 9 +#define TIME_STAMP_SIZE 21 // Functions for encoding a date or time value to a DateTime value DateTime datetime_encodeDate(int year, int month, int day); diff --git a/swmm/swmm5/swmm5/dwflow.c b/swmm/swmm5/swmm5/dwflow.c index de096ad..14338b2 100644 --- a/swmm/swmm5/swmm5/dwflow.c +++ b/swmm/swmm5/swmm5/dwflow.c @@ -2,29 +2,24 @@ // dwflow.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/14 (Build 5.1.001) -// 03/19/15 (Build 5.1.008) -// 03/14/17 (Build 5.1.012) -// 05/10/18 (Build 5.1.013) -// 03/01/20 (Build 5.1.014) -// Author: L. Rossman (EPA) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) +// Author: L. Rossman // M. Tryby (EPA) // R. Dickinson (CDM) // // Solves the momentum equation for flow in a conduit under dynamic wave // flow routing. // +// Update History +// ============== // Build 5.1.008: // - Bug in finding if conduit was upstrm/dnstrm full was fixed. -// // Build 5.1.012: // - Modified uniform loss rate term of conduit momentum equation. -// // Build 5.1.013: // - Preissmann slot surcharge option implemented. // - Changed sign of uniform loss rate term (dq6) in flow updating equation. -// // Build 5.1.014: // - Conduit evap. and seepage loss initialized to 0 in dwflow_findConduitFlow. // - Most current flow (qLast) used instead of previous time period flow @@ -32,8 +27,8 @@ //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE -#include "headers.h" #include +#include "headers.h" static const double MAXVELOCITY = 50.; // max. allowable velocity (ft/sec) @@ -46,8 +41,8 @@ static double findLocalLosses(int link, double a1, double a2, double aMid, double q); static double getWidth(TXsect* xsect, double y); -static double getSlotWidth(TXsect* xsect, double y); //(5.1.013) -static double getArea(TXsect* xsect, double y, double wSlot); //(5.1.013) +static double getSlotWidth(TXsect* xsect, double y); +static double getArea(TXsect* xsect, double y, double wSlot); static double getHydRad(TXsect* xsect, double y); static double checkNormalFlow(int j, double q, double y1, double y2, @@ -82,7 +77,7 @@ void dwflow_findConduitFlow(int j, int steps, double omega, double dt) double rho; // upstream weighting factor double sigma; // inertial damping factor double length; // effective conduit length (ft) - double wSlot; // Preissmann slot width (ft) //(5.1.013) + double wSlot; // Preissmann slot width (ft) double dq1, dq2, dq3, dq4, dq5, // terms in momentum eqn. dq6; // term for evap and infil losses double denom; // denominator of flow update formula @@ -102,8 +97,8 @@ void dwflow_findConduitFlow(int j, int steps, double omega, double dt) barrels = Conduit[k].barrels; qOld = Link[j].oldFlow / barrels; qLast = Conduit[k].q1; - Conduit[k].evapLossRate = 0.0; //(5.1.014) - Conduit[k].seepLossRate = 0.0; //(5.1.014) + Conduit[k].evapLossRate = 0.0; + Conduit[k].seepLossRate = 0.0; // --- get most current heads at upstream and downstream ends of conduit n1 = Link[j].node1; @@ -123,7 +118,7 @@ void dwflow_findConduitFlow(int j, int steps, double omega, double dt) y2 = MAX(y2, FUDGE); // --- flow depths can't exceed full depth of conduit if slot not used - if ( SurchargeMethod != SLOT ) //(5.1.013) + if ( SurchargeMethod != SLOT ) { y1 = MIN(y1, xsect->yFull); y2 = MIN(y2, xsect->yFull); @@ -141,16 +136,16 @@ void dwflow_findConduitFlow(int j, int steps, double omega, double dt) findSurfArea(j, qLast, length, &h1, &h2, &y1, &y2); // --- compute area at each end of conduit & hyd. radius at upstream end - wSlot = getSlotWidth(xsect, y1); //(5.1.013) - a1 = getArea(xsect, y1, wSlot); //(5.1.013) + wSlot = getSlotWidth(xsect, y1); + a1 = getArea(xsect, y1, wSlot); r1 = getHydRad(xsect, y1); - wSlot = getSlotWidth(xsect, y2); //(5.1.013) - a2 = getArea(xsect, y2, wSlot); //(5.1.013) + wSlot = getSlotWidth(xsect, y2); + a2 = getArea(xsect, y2, wSlot); // --- compute area & hyd. radius at midpoint yMid = 0.5 * (y1 + y2); - wSlot = getSlotWidth(xsect, yMid); //(5.1.013) - aMid = getArea(xsect, yMid, wSlot); //(5.1.013) + wSlot = getSlotWidth(xsect, yMid); + aMid = getArea(xsect, yMid, wSlot); rMid = getHydRad(xsect, yMid); // --- alternate approach not currently used, but might produce better @@ -234,11 +229,11 @@ void dwflow_findConduitFlow(int j, int steps, double omega, double dt) } // --- 6. term for evap and seepage losses per unit length - dq6 = link_getLossRate(j, qLast) * 2.5 * dt * v / link_getLength(j); //(5.1.014) + dq6 = link_getLossRate(j, qLast) * 2.5 * dt * v / link_getLength(j); // --- combine terms to find new conduit flow denom = 1.0 + dq1 + dq5; - q = (qOld - dq2 + dq3 + dq4 + dq6) / denom; //(5.1.013) + q = (qOld - dq2 + dq3 + dq4 + dq6) / denom; // --- compute derivative of flow w.r.t. head Link[j].dqdh = 1.0 / denom * GRAVITY * dt * aWtd / length * barrels; @@ -288,7 +283,7 @@ void dwflow_findConduitFlow(int j, int steps, double omega, double dt) Conduit[k].q2 = q; Link[j].newDepth = MIN(yMid, xsect->yFull); aMid = (a1 + a2) / 2.0; -// aMid = MIN(aMid, xsect->aFull); //Slot can have aMid > aFull //(5.1.013) +// aMid = MIN(aMid, xsect->aFull); //Slot can have aMid > aFull Conduit[k].fullState = link_getFullState(a1, a2, xsect->aFull); Link[j].newVolume = aMid * link_getLength(j) * barrels; Link[j].newFlow = q * barrels; @@ -454,7 +449,6 @@ void findSurfArea(int j, double q, double length, double* h1, double* h2, normalDepth = (flowDepth1 + flowDepth2) / 2.0; criticalDepth = normalDepth; -//// Following code segment modified for release 5.1.013. //// //(5.1.013) // --- find conduit's flow classification fullDepth = xsect->yFull; if (flowDepth1 >= fullDepth && flowDepth2 >= fullDepth) @@ -463,7 +457,6 @@ void findSurfArea(int j, double q, double length, double* h1, double* h2, } else Link[j].flowClass = getFlowClass(j, q, *h1, *h2, *y1, *y2, &criticalDepth, &normalDepth, &fasnh); -/////////////////////////////////////////////////////////////// // --- add conduit's surface area to its end nodes depending on flow class switch ( Link[j].flowClass ) @@ -576,8 +569,6 @@ double findLocalLosses(int j, double a1, double a2, double aMid, double q) //============================================================================= -//// New function added to release 5.1.013. //// //(5.1.013) - double getSlotWidth(TXsect* xsect, double y) { double yNorm = y / xsect->yFull; @@ -595,8 +586,6 @@ double getSlotWidth(TXsect* xsect, double y) //============================================================================= -//// This function was re-written for release 5.1.013. //// //(5.1.013) - double getWidth(TXsect* xsect, double y) // // Input: xsect = ptr. to conduit cross section @@ -614,8 +603,6 @@ double getWidth(TXsect* xsect, double y) //============================================================================= -//// This function was re-written for release 5.1.013. //// //(5.1.013) - double getArea(TXsect* xsect, double y, double wSlot) // // Input: xsect = ptr. to conduit cross section @@ -630,8 +617,6 @@ double getArea(TXsect* xsect, double y, double wSlot) //============================================================================= -//// This function was re-written for release 5.1.013. //// //(5.1.013) - double getHydRad(TXsect* xsect, double y) // // Input: xsect = ptr. to conduit cross section diff --git a/swmm/swmm5/swmm5/dynwave.c b/swmm/swmm5/swmm5/dynwave.c index 8c90105..ac302e8 100644 --- a/swmm/swmm5/swmm5/dynwave.c +++ b/swmm/swmm5/swmm5/dynwave.c @@ -2,16 +2,9 @@ // dynwave.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/14 (5.1.001) -// 03/28/14 (5.1.002) -// 09/15/14 (5.1.007) -// 03/19/15 (5.1.008) -// 08/01/16 (5.1.011) -// 05/10/18 (5.1.013) -// 03/01/20 (5.1.014) -// 07/10/20 (5.1.015) -// Author: L. Rossman (EPA) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) +// Author: L. Rossman // M. Tryby (EPA) // R. Dickinson (CDM) // @@ -22,47 +15,41 @@ // to solve the explicit form of the continuity and momentum equations // for conduits. // +// Update History +// ============== // Build 5.1.002: // - Only non-ponded nodal surface area is saved for use in // surcharge algorithm. -// // Build 5.1.007: // - Node losses added to node outflow variable instead of treated // as a separate item when computing change in node flow volume. -// // Build 5.1.008: // - Module-specific constants moved here from project.c. // - Support added for user-specified minimum variable time step. // - Node crown elevations found here instead of in flowrout.c module. // - OpenMP use to parallelize findLinkFlows() & findNodeDepths(). // - Bug in finding complete list of capacity limited links fixed. -// // Build 5.1.011: // - Added test for failed memory allocation. // - Fixed illegal array index bug for Ideal Pumps. -// // Build 5.1.013: // - Include omp.h protected against lack of compiler support for OpenMP. // - SurchargeMethod option used to decide how node surcharging is handled. // - Storage nodes allowed to pressurize if their surcharge depth > 0. // - Minimum flow needed to compute a Courant time step modified. -// // Build 5.1.014: // - updateNodeFlows() modified to subtract conduit evap. and seepage losses // from downstream node inflow instead of upstream node outflow. -// // Build 5.1.015: // - Roll back the 5.1.014 change for conduit losses in updateNodeFlows(). -// +// Build 5.2.0: +// - Support added for reporting most frequent non-converging links. //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE -#include "headers.h" #include #include -#if defined(_OPENMP) //(5.1.013) -#include -#endif +#include "headers.h" //----------------------------------------------------------------------------- // Constants @@ -71,9 +58,9 @@ static const double MINTIMESTEP = 0.001; // min. time step (sec) static const double OMEGA = 0.5; // under-relaxation parameter static const double DEFAULT_SURFAREA = 12.566; // Min. nodal surface area (~4 ft diam.) static const double DEFAULT_HEADTOL = 0.005; // Default head tolerance (ft) -static const double EXTRAN_CROWN_CUTOFF = 0.96; // crown cutoff for EXTRAN //(5.1.013) -static const double SLOT_CROWN_CUTOFF = 0.985257; // crown cutoff for SLOT //(5.1.013) -static const int DEFAULT_MAXTRIALS = 8; // Max. trials per time step +static const double EXTRAN_CROWN_CUTOFF = 0.96; // crown cutoff for EXTRAN +static const double SLOT_CROWN_CUTOFF = 0.985257; // crown cutoff for SLOT +static const int DEFAULT_MAXTRIALS = 8; // Max. trials per time step //----------------------------------------------------------------------------- @@ -111,6 +98,7 @@ static void findNonConduitFlow(int link, double dt); static void findNonConduitSurfArea(int link); static double getModPumpFlow(int link, double q, double dt); static void updateNodeFlows(int link); +static void updateConvergenceStats(); static int findNodeDepths(double dt); static void setNodeDepth(int node, double dt); @@ -163,9 +151,9 @@ void dynwave_init() Link[i].dqdh = 0.0; } - // --- set crown cutoff for finding top width of closed conduits //(5.1.013) - if ( SurchargeMethod == SLOT ) CrownCutoff = SLOT_CROWN_CUTOFF; //(5.1.013) - else CrownCutoff = EXTRAN_CROWN_CUTOFF; //(5.1.013) + // --- set crown cutoff for finding top width of closed conduits + if ( SurchargeMethod == SLOT ) CrownCutoff = SLOT_CROWN_CUTOFF; + else CrownCutoff = EXTRAN_CROWN_CUTOFF; } //============================================================================= @@ -262,7 +250,7 @@ int dynwave_execute(double tStep) findBypassedLinks(); } } - if ( !converged ) NonConvergeCount++; + if ( !converged ) updateConvergenceStats(); // --- identify any capacity-limited conduits findLimitedLinks(); @@ -271,6 +259,16 @@ int dynwave_execute(double tStep) //============================================================================= +void updateConvergenceStats() +{ + int i; + NonConvergeCount++; + for (i = 0; i < Nobjects[NODE]; i++) + stats_updateConvergenceStats(i, Xnode[i].converged); +} + +//============================================================================= + void initRoutingStep() { int i; @@ -313,12 +311,6 @@ void initNodeStates() Xnode[i].newSurfArea = node_getSurfArea(i, Node[i].newDepth); } -/* //// Removed for release 5.1.013. /// //(5.1.013) - if ( Xnode[i].newSurfArea < MinSurfArea ) - { - Xnode[i].newSurfArea = MinSurfArea; - } -*/ // --- initialize nodal inflow & outflow Node[i].inflow = 0.0; Node[i].outflow = Node[i].losses; @@ -550,19 +542,19 @@ void updateNodeFlows(int i) k = Link[i].subIndex; uniformLossRate = Conduit[k].evapLossRate + Conduit[k].seepLossRate; barrels = Conduit[k].barrels; - uniformLossRate *= barrels; //(5.1.014) + uniformLossRate *= barrels; } // --- update total inflow & outflow at upstream/downstream nodes if ( q >= 0.0 ) { - Node[n1].outflow += q + uniformLossRate; //(5.1.015) - Node[n2].inflow += q; //(5.1.015) + Node[n1].outflow += q + uniformLossRate; + Node[n2].inflow += q; } else { - Node[n1].inflow -= q; //(5.1.015) - Node[n2].outflow -= q - uniformLossRate; //(5.1.015) + Node[n1].inflow -= q; + Node[n2].outflow -= q - uniformLossRate; } // --- add surf. area contributions to upstream/downstream nodes @@ -585,9 +577,14 @@ void updateNodeFlows(int i) //============================================================================= int findNodeDepths(double dt) +// +// Input: dt = time step (sec) +// Output: returns TRUE if depth change at all non-Outfall nodes is +// within the convergence tolerance and FALSE otherwise +// Purpose: finds new depth at all nodes and checks if convergence achieved. +// { int i; - int converged; // convergence flag double yOld; // previous node depth (ft) // --- compute outfall depths based on flow in connecting link @@ -595,7 +592,6 @@ int findNodeDepths(double dt) // --- compute new depth for all non-outfall nodes and determine if // depth change from previous iteration is below tolerance - converged = TRUE; #pragma omp parallel num_threads(NumThreads) { #pragma omp for private(yOld) @@ -607,12 +603,18 @@ int findNodeDepths(double dt) Xnode[i].converged = TRUE; if ( fabs(yOld - Node[i].newDepth) > HeadTol ) { - converged = FALSE; Xnode[i].converged = FALSE; } } } - return converged; + + // --- return FALSE if any non-Outfall node failed to converge + for (i = 0; i < Nobjects[NODE]; i++) + { + if ( Node[i].type == OUTFALL ) continue; + if (Xnode[i].converged == FALSE) return FALSE; + } + return TRUE; } //============================================================================= @@ -627,7 +629,7 @@ void setNodeDepth(int i, double dt) { int canPond; // TRUE if node can pond overflows int isPonded; // TRUE if node is currently ponded - int isSurcharged = FALSE; // TRUE if node is surcharged //(5.1.013) + int isSurcharged = FALSE; // TRUE if node is surcharged double dQ; // inflow minus outflow at node (cfs) double dV; // change in node volume (ft3) double dy; // change in node depth (ft) @@ -651,13 +653,13 @@ void setNodeDepth(int i, double dt) yLast = Node[i].newDepth; Node[i].overflow = 0.0; surfArea = Xnode[i].newSurfArea; - surfArea = MAX(surfArea, MinSurfArea); //(5.1.013) + surfArea = MAX(surfArea, MinSurfArea); // --- determine average net flow volume into node over the time step dQ = Node[i].inflow - Node[i].outflow; dV = 0.5 * (Node[i].oldNetInflow + dQ) * dt; -//// Following code segment added to release 5.1.013. //// //(5.1.013) + // --- determine if node is EXTRAN surcharged if (SurchargeMethod == EXTRAN) { @@ -674,10 +676,9 @@ void setNodeDepth(int i, double dt) // --- surcharge occurs when node depth exceeds top of its highest link else isSurcharged = (yCrown > 0.0 && yLast > yCrown); } -///////////////////////////////////////////////////////////// // --- if node not surcharged, base depth change on surface area - if (!isSurcharged) //(5.1.013) + if (!isSurcharged) { dy = dV / surfArea; yNew = yOld + dy; @@ -841,7 +842,7 @@ double getLinkStep(double tMin, int *minLink) // --- skip conduits with negligible flow, area or Fr k = Link[i].subIndex; q = fabs(Link[i].newFlow) / Conduit[k].barrels; - if ( q <= FUDGE //(5.1.013) + if ( q <= FUDGE || Conduit[k].a1 <= FUDGE || Link[i].froude <= 0.01 ) continue; diff --git a/swmm/swmm5/swmm5/enums.h b/swmm/swmm5/swmm5/enums.h index 4bc50e3..fc3cc2b 100644 --- a/swmm/swmm5/swmm5/enums.h +++ b/swmm/swmm5/swmm5/enums.h @@ -2,41 +2,35 @@ // enums.h // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/14 (Build 5.1.001) -// 04/14/14 (Build 5.1.004) -// 09/15/14 (Build 5.1.007) -// 03/19/15 (Build 5.1.008) -// 08/05/15 (Build 5.1.010) -// 08/01/16 (Build 5.1.011) -// 05/10/18 (Build 5.1.013) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) // Author: L. Rossman // -// Enumerated variables +// Enumerated constants // +// Update History +// ============== // Build 5.1.004: // - IGNORE_RDII for the ignore RDII option added. -// // Build 5.1.007: // - s_GWF for [GWF] input file section added. // - s_ADJUST for [ADJUSTMENTS] input file section added. -// // Build 5.1.008: // - Enumerations for fullness state of a conduit added. // - NUM_THREADS added for number of parallel threads option. // - Runoff flow categories added to represent mass balance components. -// // Build 5.1.010: // - New ROADWAY_WEIR type of weir added. // - Potential evapotranspiration (PET) added as a system output variable. -// // Build 5.1.011: // - s_EVENT added to InputSectionType enumeration. -// // Build 5.1.013: // - SURCHARGE_METHOD and RULE_STEP options added. -// - WEIR_CURVE added as a curve type. -// +// - WEIR_CURVE added as a curve type. +// Build 5.2.0: +// - Support added for Streets and Inlets. +// - Support added for variable speed pumps. +// - Support added for analytical storage shapes. //----------------------------------------------------------------------------- #ifndef ENUMS_H @@ -63,12 +57,14 @@ SNOWMELT, // snowmelt parameter set SHAPE, // custom conduit shape LID, // LID treatment units + STREET, // street cross section + INLET, // street inlet design MAX_OBJ_TYPES}; //------------------------------------- // Names of Node sub-types //------------------------------------- - #define MAX_NODE_TYPES 4 + #define MAX_NODE_TYPES 5 enum NodeType { JUNCTION, OUTFALL, @@ -117,18 +113,18 @@ // Cross section shape types //------------------------------------- enum XsectType { - DUMMY, // 0 + DUMMY, // 0 CIRCULAR, // 1 closed FILLED_CIRCULAR, // 2 closed RECT_CLOSED, // 3 closed - RECT_OPEN, // 4 - TRAPEZOIDAL, // 5 - TRIANGULAR, // 6 + RECT_OPEN, // 4 + TRAPEZOIDAL, // 5 + TRIANGULAR, // 6 PARABOLIC, // 7 - POWERFUNC, // 8 - RECT_TRIANG, // 9 + POWERFUNC, // 8 + RECT_TRIANG, // 9 RECT_ROUND, // 10 - MOD_BASKET, // 11 + MOD_BASKET, // 11 HORIZ_ELLIPSE, // 12 closed VERT_ELLIPSE, // 13 closed ARCH, // 14 closed @@ -141,7 +137,8 @@ SEMICIRCULAR, // 21 closed IRREGULAR, // 22 CUSTOM, // 23 closed - FORCE_MAIN}; // 24 closed + FORCE_MAIN, // 24 closed + STREET_XSECT}; // 25 //------------------------------------- // Measurement units types @@ -197,7 +194,7 @@ //------------------------------------- // Computed node quantities //------------------------------------- - #define MAX_NODE_RESULTS 7 + #define MAX_NODE_RESULTS 7 enum NodeResultType { NODE_DEPTH, // water depth above invert NODE_HEAD, // hydraulic head @@ -220,7 +217,7 @@ LINK_QUAL}; // concentration of each pollutant //------------------------------------- -// System-wide flow quantities +// System-wide quantities //------------------------------------- #define MAX_SYS_RESULTS 15 enum SysFlowType { @@ -306,7 +303,7 @@ enum WindType { DRYONLY}; // evap. allowed only in dry periods enum NormalizerType { - PER_AREA, // buildup is per unit or area + PER_AREA, // buildup is per unit of area PER_CURB}; // buildup is per unit of curb length enum BuildupType { @@ -366,7 +363,6 @@ enum CompatibilityType { PARTIAL_DAMPING, // partial damping FULL_DAMPING}; // full damping -//// Added to release 5.1.013. //// //(5.1.013) enum SurchargeMethodType { EXTRAN, // original EXTRAN method SLOT}; // Preissmann slot method @@ -396,7 +392,11 @@ enum CompatibilityType { enum StorageType { TABULAR, // area v. depth from table - FUNCTIONAL}; // area v. depth from power function + FUNCTIONAL, // area v. depth from power function + CYLINDRICAL, // area v. depth from elliptical cylinder + CONICAL, // area v. depth from elliptical cone + PARABOLOID, // area v. depth from elliptical paraboloid + PYRAMIDAL}; // area v. depth from rectangular pyramid enum ReactorType { CSTR, // completely mixed reactor @@ -414,9 +414,10 @@ enum CompatibilityType { enum PumpCurveType { TYPE1_PUMP, // flow varies stepwise with wet well volume - TYPE2_PUMP, // flow varies stepwise with inlet depth + TYPE2_PUMP, // flow varies stepwise with inlet depth TYPE3_PUMP, // flow varies with head delivered TYPE4_PUMP, // flow varies with inlet depth + TYPE5_PUMP, // variable speed version of TYPE3 pump IDEAL_PUMP}; // outflow equals inflow enum OrificeType { @@ -437,11 +438,18 @@ enum CompatibilityType { RATING_CURVE, // flow rate v. head for outlet link CONTROL_CURVE, // control setting v. controller variable SHAPE_CURVE, // width v. depth for custom x-section - WEIR_CURVE, // discharge coeff. v. head for weir //(5.1.013) + WEIR_CURVE, // discharge coeff. v. head for weir PUMP1_CURVE, // flow v. wet well volume for pump PUMP2_CURVE, // flow v. depth for pump (discrete) PUMP3_CURVE, // flow v. head for pump (continuous) - PUMP4_CURVE}; // flow v. depth for pump (continuous) + PUMP4_CURVE, // flow v. depth for pump (continuous) + PUMP5_CURVE}; // variable speed version of TYPE3 pump + + enum NodeInletType { + NO_INLET, + BYPASS, + CAPTURE + }; enum InputSectionType { s_TITLE, s_OPTION, s_FILE, s_RAINGAGE, @@ -457,14 +465,15 @@ enum CompatibilityType { s_COORDINATE, s_VERTICES, s_POLYGON, s_LABEL, s_SYMBOL, s_BACKDROP, s_TAG, s_PROFILE, s_MAP, s_LID_CONTROL, s_LID_USAGE, s_GWF, - s_ADJUST, s_EVENT}; + s_ADJUST, s_EVENT, s_STREET, s_INLET_USAGE, + s_INLET}; enum InputOptionType { FLOW_UNITS, INFIL_MODEL, ROUTE_MODEL, START_DATE, START_TIME, END_DATE, END_TIME, REPORT_START_DATE, REPORT_START_TIME, SWEEP_START, SWEEP_END, START_DRY_DAYS, - WET_STEP, DRY_STEP, ROUTE_STEP, RULE_STEP, //(5.1.013) + WET_STEP, DRY_STEP, ROUTE_STEP, RULE_STEP, REPORT_STEP, ALLOW_PONDING, INERT_DAMPING, SLOPE_WEIGHTING, VARIABLE_STEP, NORMAL_FLOW_LTD, LENGTHENING_STEP, MIN_SURFAREA, COMPATIBILITY, @@ -473,7 +482,7 @@ enum CompatibilityType { IGNORE_SNOWMELT, IGNORE_GWATER, IGNORE_ROUTING, IGNORE_QUALITY, MAX_TRIALS, HEAD_TOL, SYS_FLOW_TOL, LAT_FLOW_TOL, IGNORE_RDII, - MIN_ROUTE_STEP, NUM_THREADS, SURCHARGE_METHOD}; //(5.1.013) + MIN_ROUTE_STEP, NUM_THREADS, SURCHARGE_METHOD}; enum NoYesType { NO, diff --git a/swmm/swmm5/swmm5/error.c b/swmm/swmm5/swmm5/error.c index 17c0a0e..626950b 100644 --- a/swmm/swmm5/swmm5/error.c +++ b/swmm/swmm5/swmm5/error.c @@ -2,236 +2,45 @@ // error.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/14 (Build 5.1.001) -// 03/19/15 (Build 5.1.008) -// 08/05/15 (Build 5.1.010) -// 04/14/20 (Build 5.1.015) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) // Author: L. Rossman // // Error messages // +// Update History +// ============== // Build 5.1.008: // - Text of Error 217 for control rules modified. -// // Build 5.1.010: // - Text of Error 318 for rainfall data files modified. -// // Build 5.1.015: // - Added new Error 140 for storage nodes. +// Build 5.2.0: +// - Re-designed error message system. +// - Added new Error 235 for invalid infiltration parameters. //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE #include #include "error.h" -#define ERR101 "\n ERROR 101: memory allocation error." -#define ERR103 "\n ERROR 103: cannot solve KW equations for Link %s." -#define ERR105 "\n ERROR 105: cannot open ODE solver." -#define ERR107 "\n ERROR 107: cannot compute a valid time step." - -#define ERR108 "\n ERROR 108: ambiguous outlet ID name for Subcatchment %s." -#define ERR109 "\n ERROR 109: invalid parameter values for Aquifer %s." -#define ERR110 \ -"\n ERROR 110: ground elevation is below water table for Subcatchment %s." - -#define ERR111 "\n ERROR 111: invalid length for Conduit %s." -#define ERR112 "\n ERROR 112: elevation drop exceeds length for Conduit %s." -#define ERR113 "\n ERROR 113: invalid roughness for Conduit %s." -#define ERR114 "\n ERROR 114: invalid number of barrels for Conduit %s." -#define ERR115 "\n ERROR 115: adverse slope for Conduit %s." -#define ERR117 "\n ERROR 117: no cross section defined for Link %s." -#define ERR119 "\n ERROR 119: invalid cross section for Link %s." -#define ERR121 \ -"\n ERROR 121: missing or invalid pump curve assigned to Pump %s." -#define ERR122 \ -"\n ERROR 122: startup depth not higher than shutoff depth for Pump %s." - -#define ERR131 \ -"\n ERROR 131: the following links form cyclic loops in the drainage system:" -#define ERR133 "\n ERROR 133: Node %s has more than one outlet link." -#define ERR134 "\n ERROR 134: Node %s has illegal DUMMY link connections." - -#define ERR135 "\n ERROR 135: Divider %s does not have two outlet links." -#define ERR136 "\n ERROR 136: Divider %s has invalid diversion link." -#define ERR137 "\n ERROR 137: Weir Divider %s has invalid parameters." -#define ERR138 \ -"\n ERROR 138: Node %s has initial depth greater than maximum depth." -#define ERR139 "\n ERROR 139: Regulator %s is the outlet of a non-storage node." -#define ERR140 \ -"\n ERROR 140: Storage node %s has negative volume at full depth." //(5.1.015) -#define ERR141 \ -"\n ERROR 141: Outfall %s has more than 1 inlet link or an outlet link." -#define ERR143 "\n ERROR 143: Regulator %s has invalid cross-section shape." -#define ERR145 "\n ERROR 145: Drainage system has no acceptable outlet nodes." - -#define ERR151 "\n ERROR 151: a Unit Hydrograph in set %s has invalid time base." -#define ERR153 \ -"\n ERROR 153: a Unit Hydrograph in set %s has invalid response ratios." -#define ERR155 "\n ERROR 155: invalid sewer area for RDII at node %s." - -#define ERR156 "\n ERROR 156: ambiguous station ID for Rain Gage %s." -#define ERR157 "\n ERROR 157: inconsistent rainfall format for Rain Gage %s." -#define ERR158 \ -"\n ERROR 158: time series for Rain Gage %s is also used by another object." -#define ERR159 \ -"\n ERROR 159: recording interval greater than time series interval for Rain Gage %s." - -#define ERR161 \ -"\n ERROR 161: cyclic dependency in treatment functions at node %s." - -#define ERR171 "\n ERROR 171: Curve %s has invalid or out of sequence data." -#define ERR173 "\n ERROR 173: Time Series %s has its data out of sequence." - -#define ERR181 "\n ERROR 181: invalid Snow Melt Climatology parameters." -#define ERR182 "\n ERROR 182: invalid parameters for Snow Pack %s." - -#define ERR183 "\n ERROR 183: no type specified for LID %s." -#define ERR184 "\n ERROR 184: missing layer for LID %s." -#define ERR185 "\n ERROR 185: invalid parameter value for LID %s." -#define ERR186 "\n ERROR 186: invalid parameter value for LID placed in Subcatchment %s." -#define ERR187 "\n ERROR 187: LID area exceeds total area for Subcatchment %s." -#define ERR188 \ -"\n ERROR 188: LID capture area exceeds total impervious area for Subcatchment %s." - -#define ERR191 "\n ERROR 191: simulation start date comes after ending date." -#define ERR193 "\n ERROR 193: report start date comes after ending date." -#define ERR195 \ -"\n ERROR 195: reporting time step or duration is less than routing time step." - -#define ERR200 "\n ERROR 200: one or more errors in input file." -#define ERR201 "\n ERROR 201: too many characters in input line " -#define ERR203 "\n ERROR 203: too few items " -#define ERR205 "\n ERROR 205: invalid keyword %s " -#define ERR207 "\n ERROR 207: duplicate ID name %s " -#define ERR209 "\n ERROR 209: undefined object %s " -#define ERR211 "\n ERROR 211: invalid number %s " -#define ERR213 "\n ERROR 213: invalid date/time %s " -#define ERR217 "\n ERROR 217: control rule clause invalid or out of sequence " //(5.1.008) -#define ERR219 "\n ERROR 219: data provided for unidentified transect " -#define ERR221 "\n ERROR 221: transect station out of sequence " -#define ERR223 "\n ERROR 223: Transect %s has too few stations." -#define ERR225 "\n ERROR 225: Transect %s has too many stations." -#define ERR227 "\n ERROR 227: Transect %s has no Manning's N." -#define ERR229 "\n ERROR 229: Transect %s has invalid overbank locations." -#define ERR231 "\n ERROR 231: Transect %s has no depth." -#define ERR233 "\n ERROR 233: invalid treatment function expression " - -#define ERR301 "\n ERROR 301: files share same names." -#define ERR303 "\n ERROR 303: cannot open input file." -#define ERR305 "\n ERROR 305: cannot open report file." -#define ERR307 "\n ERROR 307: cannot open binary results file." -#define ERR309 "\n ERROR 309: error writing to binary results file." -#define ERR311 "\n ERROR 311: error reading from binary results file." - -#define ERR313 "\n ERROR 313: cannot open scratch rainfall interface file." -#define ERR315 "\n ERROR 315: cannot open rainfall interface file %s." -#define ERR317 "\n ERROR 317: cannot open rainfall data file %s." -#define ERR318 \ -"\n ERROR 318: the following line is out of sequence in rainfall data file %s." //(5.1.010) -#define ERR319 "\n ERROR 319: unknown format for rainfall data file %s." -#define ERR320 "\n ERROR 320: invalid format for rainfall interface file." -#define ERR321 "\n ERROR 321: no data in rainfall interface file for gage %s." - -#define ERR323 "\n ERROR 323: cannot open runoff interface file %s." -#define ERR325 \ -"\n ERROR 325: incompatible data found in runoff interface file." -#define ERR327 \ -"\n ERROR 327: attempting to read beyond end of runoff interface file." -#define ERR329 "\n ERROR 329: error in reading from runoff interface file." - -#define ERR330 "\n ERROR 330: hotstart interface files have same names." -#define ERR331 "\n ERROR 331: cannot open hotstart interface file %s." -#define ERR333 \ -"\n ERROR 333: incompatible data found in hotstart interface file." -#define ERR335 "\n ERROR 335: error in reading from hotstart interface file." - -#define ERR336 \ -"\n ERROR 336: no climate file specified for evaporation and/or wind speed." -#define ERR337 "\n ERROR 337: cannot open climate file %s." -#define ERR338 "\n ERROR 338: error in reading from climate file %s." -#define ERR339 \ -"\n ERROR 339: attempt to read beyond end of climate file %s." - -#define ERR341 "\n ERROR 341: cannot open scratch RDII interface file." -#define ERR343 "\n ERROR 343: cannot open RDII interface file %s." -#define ERR345 "\n ERROR 345: invalid format for RDII interface file." - -#define ERR351 "\n ERROR 351: cannot open routing interface file %s." -#define ERR353 "\n ERROR 353: invalid format for routing interface file %s." -#define ERR355 "\n ERROR 355: mis-matched names in routing interface file %s." -#define ERR357 "\n ERROR 357: inflows and outflows interface files have same name." - -#define ERR361 "\n ERROR 361: could not open external file used for Time Series %s." -#define ERR363 "\n ERROR 363: invalid data in external file used for Time Series %s." - -#define ERR401 "\n ERROR 401: general system error." -#define ERR402 \ -"\n ERROR 402: cannot open new project while current project still open." -#define ERR403 "\n ERROR 403: project not open or last run not ended." -#define ERR405 \ -"\n ERROR 405: amount of output produced will exceed maximum file size;" \ -"\n either reduce Ending Date or increase Reporting Time Step." - -// API Error Keys -#define ERR501 "\n API Key Error: Object Type Outside Bonds" -#define ERR502 "\n API Key Error: Network Not Initialized (Input file open?)" -#define ERR503 "\n API Key Error: Simulation Not Running" -#define ERR504 "\n API Key Error: Incorrect object type for parameter chosen" -#define ERR505 "\n API Key Error: Object index out of Bounds." -#define ERR506 "\n API Key Error: Invalid Pollutant Index" -#define ERR507 "\n API Key Error: Invalid Inflow Type" -#define ERR508 "\n API Key Error: Invalid Timeseries Index" -#define ERR509 "\n API Key Error: Invalid Pattern Index" - -//////////////////////////////////////////////////////////////////////////// -// NOTE: Need to update ErrorMsgs[], ErrorCodes[], and ErrorType -// (in error.h) whenever a new error message is added. -///////////////////////////////////////////////////////////////////////////// - -char* ErrorMsgs[] = - { "", ERR101, ERR103, ERR105, ERR107, ERR108, ERR109, ERR110, ERR111, - ERR112, ERR113, ERR114, ERR115, ERR117, ERR119, ERR121, ERR122, ERR131, - ERR133, ERR134, ERR135, ERR136, ERR137, ERR138, ERR139, ERR141, ERR143, - ERR145, ERR151, ERR153, ERR155, ERR156, ERR157, ERR158, ERR159, ERR161, - ERR171, ERR173, ERR181, ERR182, ERR183, ERR184, ERR185, ERR186, ERR187, - ERR188, ERR191, ERR193, ERR195, ERR200, ERR201, ERR203, ERR205, ERR207, - ERR209, ERR211, ERR213, ERR217, ERR219, ERR221, ERR223, ERR225, ERR227, - ERR229, ERR231, ERR233, ERR301, ERR303, ERR305, ERR307, ERR309, ERR311, - ERR313, ERR315, ERR317, ERR318, ERR319, ERR320, ERR321, ERR323, ERR325, - ERR327, ERR329, ERR330, ERR331, ERR333, ERR335, ERR336, ERR337, ERR338, - ERR339, ERR341, ERR343, ERR345, ERR351, ERR353, ERR355, ERR357, ERR361, - ERR363, ERR401, ERR402, ERR403, ERR405, ERR501, ERR502, ERR503, ERR504, - ERR505, ERR506, ERR507, ERR508, ERR509, ERR140}; //(5.1.015) - -int ErrorCodes[] = - { 0, 101, 103, 105, 107, 108, 109, 110, 111, - 112, 113, 114, 115, 117, 119, 121, 122, 131, - 133, 134, 135, 136, 137, 138, 139, 141, 143, - 145, 151, 153, 155, 156, 157, 158, 159, 161, - 171, 173, 181, 182, 183, 184, 185, 186, 187, - 188, 191, 193, 195, 200, 201, 203, 205, 207, - 209, 211, 213, 217, 219, 221, 223, 225, 227, - 229, 231, 233, 301, 303, 305, 307, 309, 311, - 313, 315, 317, 318, 319, 320, 321, 323, 325, - 327, 329, 330, 331, 333, 335, 336, 337, 338, - 339, 341, 343, 345, 351, 353, 355, 357, 361, - 363, 401, 402, 403, 405, 501, 502, 503, 504, - 505, 506, 507, 508, 509, 140}; //(5.1.015) - char ErrString[256]; -char* error_getMsg(int i) +char* error_getMsg(int errCode, char* msg) { - if ( i >= 0 && i < MAXERRMSG ) return ErrorMsgs[i]; - else return ErrorMsgs[0]; -}; + switch (errCode) + { -int error_getCode(int i) -{ - if ( i >= 0 && i < MAXERRMSG ) return ErrorCodes[i]; - else return 0; -} +#define ERR(code,string) case code: strcpy(msg, string); break; +#include "error.txt" +#undef ERR + + default: + strcpy(msg, ""); + } + return (msg); +}; int error_setInpError(int errcode, char* s) { diff --git a/swmm/swmm5/swmm5/error.h b/swmm/swmm5/swmm5/error.h index 2073796..fedf084 100644 --- a/swmm/swmm5/swmm5/error.h +++ b/swmm/swmm5/swmm5/error.h @@ -2,9 +2,8 @@ // error.h // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/14 (Build 5.1.001) -// 04/14/20 (Build 5.1.015) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) // Author: L. Rossman // // Error codes @@ -14,175 +13,170 @@ #ifndef ERROR_H #define ERROR_H - enum ErrorType { - //... Runtime Errors - ERR_NONE, // 0 - ERR_MEMORY, //101 1 - ERR_KINWAVE, //103 2 - ERR_ODE_SOLVER, //105 3 - ERR_TIMESTEP, //107 4 - - //... Subcatchment/Aquifer Errors - ERR_SUBCATCH_OUTLET, //108 5 - ERR_AQUIFER_PARAMS, //109 6 - ERR_GROUND_ELEV, //110 7 - - //... Conduit/Pump Errors - ERR_LENGTH, //111 8 - ERR_ELEV_DROP, //112 9 - ERR_ROUGHNESS, //113 10 - ERR_BARRELS, //114 11 - ERR_SLOPE, //115 12 - ERR_NO_XSECT, //117 13 - ERR_XSECT, //119 14 - ERR_NO_CURVE, //121 15 - ERR_PUMP_LIMITS, //122 16 - - //... Topology Errors - ERR_LOOP, //131 17 - ERR_MULTI_OUTLET, //133 18 - ERR_DUMMY_LINK, //134 19 - - //... Node Errors - ERR_DIVIDER, //135 20 - ERR_DIVIDER_LINK, //136 21 - ERR_WEIR_DIVIDER, //137 22 - ERR_NODE_DEPTH, //138 23 - ERR_REGULATOR, //139 24 - ERR_OUTFALL, //141 25 - ERR_REGULATOR_SHAPE, //143 26 - ERR_NO_OUTLETS, //145 27 - - //... RDII Errors - ERR_UNITHYD_TIMES, //151 28 - ERR_UNITHYD_RATIOS, //153 29 - ERR_RDII_AREA, //155 30 - - //... Rain Gage Errors - ERR_RAIN_FILE_CONFLICT, //156 31 - ERR_RAIN_GAGE_FORMAT, //157 32 - ERR_RAIN_GAGE_TSERIES, //158 33 - ERR_RAIN_GAGE_INTERVAL, //159 34 - - //... Treatment Function Error - ERR_CYCLIC_TREATMENT, //161 35 - - //... Curve/Time Series Errors - ERR_CURVE_SEQUENCE, //171 36 - ERR_TIMESERIES_SEQUENCE, //173 37 - - //... Snowmelt Errors - ERR_SNOWMELT_PARAMS, //181 38 - ERR_SNOWPACK_PARAMS, //182 39 - - //... LID Errors - ERR_LID_TYPE, //183 40 - ERR_LID_LAYER, //184 41 - ERR_LID_PARAMS, //185 42 - ERR_SUBCATCH_LID, //186 43 - ERR_LID_AREAS, //187 44 - ERR_LID_CAPTURE_AREA, //188 45 - - //... Simulation Date/Time Errors - ERR_START_DATE, //191 46 - ERR_REPORT_DATE, //193 47 - ERR_REPORT_STEP, //195 48 - - //... Input Parser Errors - ERR_INPUT, //200 49 - ERR_LINE_LENGTH, //201 50 - ERR_ITEMS, //203 51 - ERR_KEYWORD, //205 52 - ERR_DUP_NAME, //207 53 - ERR_NAME, //209 54 - ERR_NUMBER, //211 55 - ERR_DATETIME, //213 56 - ERR_RULE, //217 57 - ERR_TRANSECT_UNKNOWN, //219 58 - ERR_TRANSECT_SEQUENCE, //221 59 - ERR_TRANSECT_TOO_FEW, //223 60 - ERR_TRANSECT_TOO_MANY, //225 61 - ERR_TRANSECT_MANNING, //227 62 - ERR_TRANSECT_OVERBANK, //229 63 - ERR_TRANSECT_NO_DEPTH, //231 64 - ERR_TREATMENT_EXPR, //233 65 - - //... File Name/Opening Errors - ERR_FILE_NAME, //301 66 - ERR_INP_FILE, //303 67 - ERR_RPT_FILE, //305 68 - ERR_OUT_FILE, //307 69 - ERR_OUT_WRITE, //309 70 - ERR_OUT_READ, //311 71 - - //... Rain File Errors - ERR_RAIN_FILE_SCRATCH, //313 72 - ERR_RAIN_FILE_OPEN, //315 73 - ERR_RAIN_FILE_DATA, //317 74 - ERR_RAIN_FILE_SEQUENCE, //318 75 - ERR_RAIN_FILE_FORMAT, //319 76 - ERR_RAIN_IFACE_FORMAT, //320 77 - ERR_RAIN_FILE_GAGE, //321 78 - - //... Runoff File Errors - ERR_RUNOFF_FILE_OPEN , //323 79 - ERR_RUNOFF_FILE_FORMAT, //325 80 - ERR_RUNOFF_FILE_END, //327 81 - ERR_RUNOFF_FILE_READ, //329 82 - - //... Hotstart File Errors - ERR_HOTSTART_FILE_NAMES, //330 83 - ERR_HOTSTART_FILE_OPEN, //331 84 - ERR_HOTSTART_FILE_FORMAT, //333 85 - ERR_HOTSTART_FILE_READ, //335 86 - - //... Climate File Errors - ERR_NO_CLIMATE_FILE, //336 87 - ERR_CLIMATE_FILE_OPEN, //337 88 - ERR_CLIMATE_FILE_READ, //338 89 - ERR_CLIMATE_END_OF_FILE, //339 90 - - //... RDII File Errors - ERR_RDII_FILE_SCRATCH, //341 91 - ERR_RDII_FILE_OPEN, //343 92 - ERR_RDII_FILE_FORMAT, //345 93 - - //... Routing File Errors - ERR_ROUTING_FILE_OPEN, //351 94 - ERR_ROUTING_FILE_FORMAT, //353 95 - ERR_ROUTING_FILE_NOMATCH, //355 96 - ERR_ROUTING_FILE_NAMES, //357 97 - - //... Time Series File Errors - ERR_TABLE_FILE_OPEN, //361 98 - ERR_TABLE_FILE_READ, //363 99 - - //... Runtime Errors - ERR_SYSTEM, //401 100 - ERR_NOT_CLOSED, //402 101 - ERR_NOT_OPEN, //403 102 - ERR_FILE_SIZE, //405 103 - - //... API Errors - ERR_API_OUTBOUNDS, //501 104 - ERR_API_INPUTNOTOPEN, //502 105 - ERR_API_SIM_NRUNNING, //503 106 - ERR_API_WRONG_TYPE, //504 107 - ERR_API_OBJECT_INDEX, //505 108 - ERR_API_POLLUT_INDEX, //506 109 - ERR_API_INFLOWTYPE, //507 110 - ERR_API_TSERIES_INDEX, //508 111 - ERR_API_PATTERN_INDEX, //509 112 - - //... Additional Errors - ERR_STORAGE_VOLUME, //140 113 //(5.1.015) - MAXERRMSG}; - -char* error_getMsg(int i); -int error_getCode(int i); +// ... Runtime Errors + ERR_NONE = 0, + ERR_MEMORY = 101, + ERR_KINWAVE = 103, + ERR_ODE_SOLVER = 105, + ERR_TIMESTEP = 107, + +// ... Subcatchment/Aquifer Errors + ERR_SUBCATCH_OUTLET = 108, + ERR_AQUIFER_PARAMS = 109, + ERR_GROUND_ELEV = 110, + +// ... Conduit/Pump Errors + ERR_LENGTH = 111, + ERR_ELEV_DROP = 112, + ERR_ROUGHNESS = 113, + ERR_BARRELS = 114, + ERR_SLOPE = 115, + ERR_NO_XSECT = 117, + ERR_XSECT = 119, + ERR_NO_CURVE = 121, + ERR_PUMP_LIMITS = 122, + +// ... Topology Errors + ERR_LOOP = 131, + ERR_MULTI_OUTLET = 133, + ERR_DUMMY_LINK = 134, + +// ... Node Errors + ERR_DIVIDER = 135, + ERR_DIVIDER_LINK = 136, + ERR_WEIR_DIVIDER = 137, + ERR_NODE_DEPTH = 138, + ERR_REGULATOR = 139, + ERR_STORAGE_VOLUME = 140, + ERR_OUTFALL = 141, + ERR_REGULATOR_SHAPE = 143, + ERR_NO_OUTLETS = 145, + +// ... RDII Errors + ERR_UNITHYD_TIMES = 151, + ERR_UNITHYD_RATIOS = 153, + ERR_RDII_AREA = 155, + +// ... Rain Gage Errors + ERR_RAIN_FILE_CONFLICT = 156, + ERR_RAIN_GAGE_FORMAT = 157, + ERR_RAIN_GAGE_TSERIES = 158, + ERR_RAIN_GAGE_INTERVAL = 159, + +// ... Treatment Function Error + ERR_CYCLIC_TREATMENT = 161, + +// ... Curve/Time Series Errors + ERR_CURVE_SEQUENCE = 171, + ERR_TIMESERIES_SEQUENCE = 173, + +// ... Snowmelt Errors + ERR_SNOWMELT_PARAMS = 181, + ERR_SNOWPACK_PARAMS = 182, + +// ... LID Errors + ERR_LID_TYPE = 183, + ERR_LID_LAYER = 184, + ERR_LID_PARAMS = 185, + ERR_LID_AREAS = 187, + ERR_LID_CAPTURE_AREA = 188, + +// ... Simulation Date/Time Errors + ERR_START_DATE = 191, + ERR_REPORT_DATE = 193, + ERR_REPORT_STEP = 195, + +// ... Input Parser Errors + ERR_INPUT = 200, + ERR_LINE_LENGTH = 201, + ERR_ITEMS = 203, + ERR_KEYWORD = 205, + ERR_DUP_NAME = 207, + ERR_NAME = 209, + ERR_NUMBER = 211, + ERR_DATETIME = 213, + ERR_RULE = 217, + ERR_TRANSECT_UNKNOWN = 219, + ERR_TRANSECT_SEQUENCE = 221, + ERR_TRANSECT_TOO_FEW = 223, + ERR_TRANSECT_TOO_MANY = 225, + ERR_TRANSECT_MANNING = 227, + ERR_TRANSECT_OVERBANK = 229, + ERR_TRANSECT_NO_DEPTH = 231, + ERR_MATH_EXPR = 233, + ERR_INFIL_PARAMS = 235, + +// ... File Name/Opening Errors + ERR_FILE_NAME = 301, + ERR_INP_FILE = 303, + ERR_RPT_FILE = 305, + ERR_OUT_FILE = 307, + ERR_OUT_SIZE = 308, + ERR_OUT_WRITE = 309, + ERR_OUT_READ = 311, + +// ... Rain File Errors + ERR_RAIN_FILE_SCRATCH = 313, + ERR_RAIN_FILE_OPEN = 315, + ERR_RAIN_FILE_DATA = 317, + ERR_RAIN_FILE_SEQUENCE = 318, + ERR_RAIN_FILE_FORMAT = 319, + ERR_RAIN_IFACE_FORMAT = 320, + ERR_RAIN_FILE_GAGE = 321, + +// ... Runoff File Errors + ERR_RUNOFF_FILE_OPEN = 323, + ERR_RUNOFF_FILE_FORMAT = 325, + ERR_RUNOFF_FILE_END = 327, + ERR_RUNOFF_FILE_READ = 329, + +// ... Hotstart File Errors + ERR_HOTSTART_FILE_OPEN = 331, + ERR_HOTSTART_FILE_FORMAT = 333, + ERR_HOTSTART_FILE_READ = 335, + +// ... Climate File Errors + ERR_NO_CLIMATE_FILE = 336, + ERR_CLIMATE_FILE_OPEN = 337, + ERR_CLIMATE_FILE_READ = 338, + ERR_CLIMATE_END_OF_FILE = 339, + +// ... RDII File Errors + ERR_RDII_FILE_SCRATCH = 341, + ERR_RDII_FILE_OPEN = 343, + ERR_RDII_FILE_FORMAT = 345, + +// ... Routing File Errors + ERR_ROUTING_FILE_OPEN = 351, + ERR_ROUTING_FILE_FORMAT = 353, + ERR_ROUTING_FILE_NOMATCH = 355, + ERR_ROUTING_FILE_NAMES = 357, + +// ... Time Series File Errors + ERR_TABLE_FILE_OPEN = 361, + ERR_TABLE_FILE_READ = 363, + +// ... Runtime Errors + ERR_SYSTEM = 500, + +// ... API Errors + ERR_API_NOT_OPEN = 501, + ERR_API_NOT_STARTED = 502, + ERR_API_NOT_ENDED = 503, + ERR_API_OBJECT_TYPE = 504, + ERR_API_OBJECT_INDEX = 505, + ERR_API_OBJECT_NAME = 506, + ERR_API_PROPERTY_TYPE = 507, + ERR_API_PROPERTY_VALUE = 508, + ERR_API_TIME_PERIOD = 509, + +// ... Additional Errors + MAXERRMSG = 1000 +}; + +char* error_getMsg(int i, char* msg); int error_setInpError(int errcode, char* s); - #endif //ERROR_H diff --git a/swmm/swmm5/swmm5/error.txt b/swmm/swmm5/swmm5/error.txt new file mode 100644 index 0000000..30d45a7 --- /dev/null +++ b/swmm/swmm5/swmm5/error.txt @@ -0,0 +1,135 @@ +// SWMM 5.2 Error Messages + +ERR(101,"\n ERROR 101: memory allocation error.") +ERR(103,"\n ERROR 103: cannot solve KW equations for Link %s.") +ERR(105,"\n ERROR 105: cannot open ODE solver.") +ERR(107,"\n ERROR 107: cannot compute a valid time step.") + +ERR(108,"\n ERROR 108: ambiguous outlet ID name for Subcatchment %s.") +ERR(109,"\n ERROR 109: invalid parameter values for Aquifer %s.") +ERR(110,"\n ERROR 110: ground elevation is below water table for Subcatchment %s.") + +ERR(111,"\n ERROR 111: invalid length for Conduit %s.") +ERR(112,"\n ERROR 112: elevation drop exceeds length for Conduit %s.") +ERR(113,"\n ERROR 113: invalid roughness for Conduit %s.") +ERR(114,"\n ERROR 114: invalid number of barrels for Conduit %s.") +ERR(115,"\n ERROR 115: adverse slope for Conduit %s.") +ERR(117,"\n ERROR 117: no cross section defined for Link %s.") +ERR(119,"\n ERROR 119: invalid cross section for Link %s.") +ERR(121,"\n ERROR 121: missing or invalid pump curve assigned to Pump %s.") +ERR(122,"\n ERROR 122: startup depth not higher than shutoff depth for Pump %s.") + +ERR(131,"\n ERROR 131: the following links form cyclic loops in the drainage system:") +ERR(133,"\n ERROR 133: Node %s has more than one outlet link.") +ERR(134,"\n ERROR 134: Node %s has illegal DUMMY link connections.") + +ERR(135,"\n ERROR 135: Divider %s does not have two outlet links.") +ERR(136,"\n ERROR 136: Divider %s has invalid diversion link.") +ERR(137,"\n ERROR 137: Weir Divider %s has invalid parameters.") +ERR(138,"\n ERROR 138: Node %s has initial depth greater than maximum depth.") +ERR(139,"\n ERROR 139: Regulator %s is the outlet of a non-storage node.") +ERR(140,"\n ERROR 140: Storage node %s has negative volume at full depth.") +ERR(141,"\n ERROR 141: Outfall %s has more than 1 inlet link or an outlet link.") +ERR(143,"\n ERROR 143: Regulator %s has invalid cross-section shape.") +ERR(145,"\n ERROR 145: Drainage system has no acceptable outlet nodes.") + +ERR(151,"\n ERROR 151: a Unit Hydrograph in set %s has invalid time base.") +ERR(153,"\n ERROR 153: a Unit Hydrograph in set %s has invalid response ratios.") +ERR(155,"\n ERROR 155: invalid sewer area for RDII at node %s.") + +ERR(156,"\n ERROR 156: ambiguous station ID for Rain Gage %s.") +ERR(157,"\n ERROR 157: inconsistent rainfall format for Rain Gage %s.") +ERR(158,"\n ERROR 158: time series for Rain Gage %s is also used by another object.") +ERR(159,"\n ERROR 159: recording interval greater than time series interval for Rain Gage %s.") + +ERR(161,"\n ERROR 161: cyclic dependency in treatment functions at node %s.") + +ERR(171,"\n ERROR 171: Curve %s has invalid or out of sequence data.") +ERR(173,"\n ERROR 173: Time Series %s has its data out of sequence.") + +ERR(181,"\n ERROR 181: invalid Snow Melt Climatology parameters.") +ERR(182,"\n ERROR 182: invalid parameters for Snow Pack %s.") + +ERR(183,"\n ERROR 183: no type specified for LID %s.") +ERR(184,"\n ERROR 184: missing layer for LID %s.") +ERR(185,"\n ERROR 185: invalid parameter value for LID %s.") +ERR(187,"\n ERROR 187: LID area exceeds total area for Subcatchment %s.") +ERR(188,"\n ERROR 188: LID capture area exceeds total impervious area for Subcatchment %s.") + +ERR(191,"\n ERROR 191: simulation start date comes after ending date.") +ERR(193,"\n ERROR 193: report start date comes after ending date.") +ERR(195,"\n ERROR 195: reporting time step or duration is less than routing time step.") + +ERR(200,"\n ERROR 200: one or more errors in input file.") +ERR(201,"\n ERROR 201: too many characters in input line ") +ERR(203,"\n ERROR 203: too few items ") +ERR(205,"\n ERROR 205: invalid keyword %s ") +ERR(207,"\n ERROR 207: duplicate ID name %s ") +ERR(209,"\n ERROR 209: undefined object %s ") +ERR(211,"\n ERROR 211: invalid number %s ") +ERR(213,"\n ERROR 213: invalid date/time %s ") +ERR(217,"\n ERROR 217: control rule clause invalid or out of sequence ") +ERR(219,"\n ERROR 219: data provided for unidentified transect ") +ERR(221,"\n ERROR 221: transect station out of sequence ") +ERR(223,"\n ERROR 223: Transect %s has too few stations.") +ERR(225,"\n ERROR 225: Transect %s has too many stations.") +ERR(227,"\n ERROR 227: Transect %s has no Manning's N.") +ERR(229,"\n ERROR 229: Transect %s has invalid overbank locations.") +ERR(231,"\n ERROR 231: Transect %s has no depth.") +ERR(233,"\n ERROR 233: invalid math expression ") +ERR(235,"\n ERROR 235: invalid infiltration parameters ") + +ERR(301,"\n ERROR 301: files share same names.") +ERR(303,"\n ERROR 303: cannot open input file.") +ERR(305,"\n ERROR 305: cannot open report file.") +ERR(307,"\n ERROR 307: cannot open binary results file.") +ERR(308,"\n ERROR 308: amount of output produced will exceed maximum file size.") + +ERR(309,"\n ERROR 309: error writing to binary results file.") +ERR(311,"\n ERROR 311: error reading from binary results file.") + +ERR(313,"\n ERROR 313: cannot open scratch rainfall interface file.") +ERR(315,"\n ERROR 315: cannot open rainfall interface file %s.") +ERR(317,"\n ERROR 317: cannot open rainfall data file %s.") +ERR(318,"\n ERROR 318: the following line is out of sequence in rainfall data file %s.") +ERR(319,"\n ERROR 319: unknown format for rainfall data file %s.") +ERR(320,"\n ERROR 320: invalid format for rainfall interface file.") +ERR(321,"\n ERROR 321: no data in rainfall interface file for gage %s.") + +ERR(323,"\n ERROR 323: cannot open runoff interface file %s.") +ERR(325,"\n ERROR 325: incompatible data found in runoff interface file.") +ERR(327,"\n ERROR 327: attempting to read beyond end of runoff interface file.") +ERR(329,"\n ERROR 329: error in reading from runoff interface file.") + +ERR(331,"\n ERROR 331: cannot open hot start interface file %s.") +ERR(333,"\n ERROR 333: incompatible data found in hot start interface file.") +ERR(335,"\n ERROR 335: error in reading from hot start interface file.") + +ERR(336,"\n ERROR 336: no climate file specified for evaporation and/or wind speed.") +ERR(337,"\n ERROR 337: cannot open climate file %s.") +ERR(338,"\n ERROR 338: error in reading from climate file %s.") +ERR(339,"\n ERROR 339: attempt to read beyond end of climate file %s.") + +ERR(341,"\n ERROR 341: cannot open scratch RDII interface file.") +ERR(343,"\n ERROR 343: cannot open RDII interface file %s.") +ERR(345,"\n ERROR 345: invalid format for RDII interface file.") + +ERR(351,"\n ERROR 351: cannot open routing interface file %s.") +ERR(353,"\n ERROR 353: invalid format for routing interface file %s.") +ERR(355,"\n ERROR 355: mis-matched names in routing interface file %s.") +ERR(357,"\n ERROR 357: inflows and outflows interface files have same name.") + +ERR(361,"\n ERROR 361: could not open external file used for Time Series %s.") +ERR(363,"\n ERROR 363: invalid data in external file used for Time Series %s.") + +// API Error Keys +ERR(500,"\n ERROR 500: System exception thrown.") +ERR(501,"\n API Error 501: project not opened.") +ERR(502,"\n API Error 502: simulation not started.") +ERR(503,"\n API Error 503: simulation not ended.") +ERR(504,"\n API Error 504: invalid object type.") +ERR(505,"\n API Error 505: invalid object index.") +ERR(506,"\n API Error 506: invalid object name.") +ERR(507,"\n API Error 507: invalid property type.") +ERR(508,"\n API Error 508: invalid property value.") +ERR(509,"\n API Error 509: invalid time period.") diff --git a/swmm/swmm5/swmm5/exfil.c b/swmm/swmm5/swmm5/exfil.c index 58ef6f5..f1cb2ad 100644 --- a/swmm/swmm5/swmm5/exfil.c +++ b/swmm/swmm5/swmm5/exfil.c @@ -2,24 +2,22 @@ // exfil.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 09/15/14 (Build 5.1.007) -// 03/19/15 (Build 5.1.008) -// 08/05/15 (Build 5.1.010) -// 08/01/16 (Build 5.1.011) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) // Author: L. Rossman // // Storage unit exfiltration functions. // +// Update History +// ============== // Build 5.1.008: // - Monthly conductivity adjustment applied to exfiltration rate. -// // Build 5.1.010: // - New modified Green-Ampt infiltration option used. -// // Build 5.1.011: // - Fixed units conversion error for storage units with surface area curves. -// +// Build 5.2.0: +// - Support added for analytical storage shapes. //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE @@ -92,48 +90,65 @@ void exfil_initState(int k) grnampt_initState(exfil->btmExfil); grnampt_initState(exfil->bankExfil); - // --- shape given by a Storage Curve - i = Storage[k].aCurve; - if ( i >= 0 ) + switch (Storage[k].shape) { - // --- get bottom area - aCurve = &Curve[i]; - Storage[k].exfil->btmArea = table_lookupEx(aCurve, 0.0); - - // --- find min/max bank depths and max. bank area - table_getFirstEntry(aCurve, &d, &a); + // --- shape given by a Storage Curve + case TABULAR: + i = Storage[k].aCurve; + exfil->btmArea = 0.0; exfil->bankMinDepth = 0.0; exfil->bankMaxDepth = 0.0; exfil->bankMaxArea = 0.0; - alast = a; - while ( table_getNextEntry(aCurve, &d, &a) ) + if ( i >= 0 ) { - if ( a < alast ) break; - else if ( a > alast ) + // --- get bottom area + aCurve = &Curve[i]; + Storage[k].exfil->btmArea = table_lookupEx(aCurve, 0.0); + + // --- find min/max bank depths and max. bank area + table_getFirstEntry(aCurve, &d, &a); + alast = a; + while ( table_getNextEntry(aCurve, &d, &a) ) { - exfil->bankMaxArea = a; - exfil->bankMaxDepth = d; + if ( a < alast ) break; + else if ( a > alast ) + { + exfil->bankMaxArea = a; + exfil->bankMaxDepth = d; + } + else if ( exfil->bankMaxArea == 0.0 ) + exfil->bankMinDepth = d; + else break; + alast = a; } - else if ( exfil->bankMaxArea == 0.0 ) exfil->bankMinDepth = d; - else break; - alast = a; - } - - // --- convert from user units to internal units - exfil->btmArea /= UCF(LENGTH) * UCF(LENGTH); - exfil->bankMaxArea /= UCF(LENGTH) * UCF(LENGTH); - exfil->bankMinDepth /= UCF(LENGTH); - exfil->bankMaxDepth /= UCF(LENGTH); - } - // --- functional storage shape curve - else - { - exfil->btmArea = Storage[k].aConst; - if ( Storage[k].aExpon == 0.0 ) exfil->btmArea +=Storage[k].aCoeff; - exfil->bankMinDepth = 0.0; - exfil->bankMaxDepth = BIG; - exfil->bankMaxArea = BIG; + // --- convert from user units to internal units + exfil->btmArea /= UCF(LENGTH) * UCF(LENGTH); + exfil->bankMaxArea /= UCF(LENGTH) * UCF(LENGTH); + exfil->bankMinDepth /= UCF(LENGTH); + exfil->bankMaxDepth /= UCF(LENGTH); + } + break; + + // --- functional storage shape curve + case FUNCTIONAL: + exfil->btmArea = Storage[k].a0; + if ( Storage[k].a2 == 0.0 ) + exfil->btmArea +=Storage[k].a1; + exfil->bankMinDepth = 0.0; + exfil->bankMaxDepth = BIG; + exfil->bankMaxArea = BIG; + break; + + // --- cylindrical, conical & prismatic shapes + case CYLINDRICAL: + case CONICAL: + case PYRAMIDAL: + exfil->btmArea = Storage[k].a0; + exfil->bankMinDepth = 0.0; + exfil->bankMaxDepth = BIG; + exfil->bankMaxArea = BIG; + break; } } } diff --git a/swmm/swmm5/swmm5/exfil.h b/swmm/swmm5/swmm5/exfil.h index 8315c08..8da4da3 100644 --- a/swmm/swmm5/swmm5/exfil.h +++ b/swmm/swmm5/swmm5/exfil.h @@ -2,9 +2,9 @@ // exfil.h // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 09/15/14 (Build 5.1.007) -// Author: L. Rossman (US EPA) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) +// Author: L. Rossman // // Public interface for exfiltration functions. //----------------------------------------------------------------------------- @@ -12,7 +12,6 @@ #ifndef EXFIL_H #define EXFIL_H - //---------------------------- // EXFILTRATION OBJECT //---------------------------- @@ -33,5 +32,4 @@ int exfil_readStorageParams(int k, char* tok[], int ntoks, int n); void exfil_initState(int k); double exfil_getLoss(TExfil* exfil, double tStep, double depth, double area); - -#endif //EXFIL_H +#endif diff --git a/swmm/swmm5/swmm5/findroot.h b/swmm/swmm5/swmm5/findroot.h index c0ae10d..3b54c64 100644 --- a/swmm/swmm5/swmm5/findroot.h +++ b/swmm/swmm5/swmm5/findroot.h @@ -9,12 +9,10 @@ #ifndef FINDROOT_H #define FINDROOT_H - int findroot_Newton(double x1, double x2, double* rts, double xacc, void (*func) (double x, double* f, double* df, void* p), - void* p); + void* p); double findroot_Ridder(double x1, double x2, double xacc, - double (*func)(double, void* p), void* p); - + double (*func)(double, void* p), void* p); #endif //FINDROOT_H diff --git a/swmm/swmm5/swmm5/flowrout.c b/swmm/swmm5/swmm5/flowrout.c index 675e393..d38b684 100644 --- a/swmm/swmm5/swmm5/flowrout.c +++ b/swmm/swmm5/swmm5/flowrout.c @@ -2,39 +2,34 @@ // flowrout.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/19/14 (Build 5.1.001) -// 09/15/14 (Build 5.1.007) -// 03/19/15 (Build 5.1.008) -// 03/14/17 (Build 5.1.012) -// 03/01/20 (Build 5.1.014) -// Author: L. Rossman (EPA) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) +// Author: L. Rossman // M. Tryby (EPA) // // Flow routing functions. // -// +// Update History +// ============== // Build 5.1.007: // - updateStorageState() modified in response to node outflow being // initialized with current evap & seepage losses in routing_execute(). -// // Build 5.1.008: // - Determination of node crown elevations moved to dynwave.c. // - Support added for new way of recording conduit's fullness state. -// // Build 5.1.012: // - Overflow computed in updateStorageState() must be non-negative. // - Terminal storage nodes now updated corectly. -// // Build 5.1.014: // - Arguments to function link_getLossRate changed. -// +// Build 5.2.0: +// - Correction made to updating state of terminal storage nodes. //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE -#include "headers.h" #include #include +#include "headers.h" //----------------------------------------------------------------------------- // Constants @@ -135,7 +130,7 @@ double flowrout_getRoutingStep(int routingModel, double fixedStep) int flowrout_execute(int links[], int routingModel, double tStep) // -// Input: links = array of link indexes in topo-sorted order +// Input: links = array of link indexes in topo-sorted order (per routing model) // routingModel = type of routing method used // tStep = routing time step (sec) // Output: returns number of computational steps taken @@ -179,10 +174,11 @@ int flowrout_execute(int links[], int routingModel, double tStep) // --- retrieve inflow at upstream end of link qin = getLinkInflow(j, tStep); - // route flow through link + // --- route flow through link if ( routingModel == SF ) steps += steadyflow_execute(j, &qin, &qout, tStep); - else steps += kinwave_execute(j, &qin, &qout, tStep); + else + steps += kinwave_execute(j, &qin, &qout, tStep); Link[j].newFlow = qout; // adjust outflow at upstream node and inflow at downstream node @@ -641,15 +637,14 @@ void setNewNodeState(int j, double dt) // --- update terminal storage nodes if ( Node[j].type == STORAGE ) { - if ( Node[j].updated == FALSE ) - updateStorageState(j, Nobjects[LINK], NULL, dt); - return; + if ( Node[j].updated == FALSE ) + updateStorageState(j, Nobjects[LINK], NULL, dt); + return; } - // --- update stored volume using mid-point integration + // --- update stored volume newNetInflow = Node[j].inflow - Node[j].outflow - Node[j].losses; - Node[j].newVolume = Node[j].oldVolume + - 0.5 * (Node[j].oldNetInflow + newNetInflow) * dt; + Node[j].newVolume = Node[j].oldVolume + newNetInflow * dt; if ( Node[j].newVolume < FUDGE ) Node[j].newVolume = 0.0; // --- determine any overflow lost from system @@ -727,7 +722,7 @@ void updateNodeDepth(int i, double y) if ( Node[i].type == STORAGE ) return; // --- if non-outfall node is flooded, then use full depth - if ( Node[i].type != OUTFALL && + if ( Node[i].type != OUTFALL && Node[i].degree > 0 && Node[i].overflow > 0.0 ) y = Node[i].fullDepth; // --- if current new depth below y @@ -770,7 +765,7 @@ int steadyflow_execute(int j, double* qin, double* qout, double tStep) else { // --- adjust flow for evap and infil losses - q -= link_getLossRate(j, q); //(5.1.014) + q -= link_getLossRate(j, q); // --- flow can't exceed full flow if ( q > Link[j].qFull ) diff --git a/swmm/swmm5/swmm5/forcmain.c b/swmm/swmm5/swmm5/forcmain.c index b2c70fd..a6314bb 100644 --- a/swmm/swmm5/swmm5/forcmain.c +++ b/swmm/swmm5/swmm5/forcmain.c @@ -2,8 +2,8 @@ // forcemain.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/14 (Build 5.1.001) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) // Author: L. Rossman // // Special Non-Manning Force Main functions diff --git a/swmm/swmm5/swmm5/funcs.h b/swmm/swmm5/swmm5/funcs.h index b9da070..6f97827 100644 --- a/swmm/swmm5/swmm5/funcs.h +++ b/swmm/swmm5/swmm5/funcs.h @@ -2,42 +2,42 @@ // funcs.h // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/14 (Build 5.1.000) -// 09/15/14 (Build 5.1.007) -// 04/02/15 (Build 5.1.008) -// 08/05/15 (Build 5.1.010) -// 05/10/18 (Build 5.1.013) -// 03/01/20 (Build 5.1.014) -// Author: L. Rossman (EPA) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) +// Author: L. Rossman // M. Tryby (EPA) // // Global interfacing functions. // +// Update History +// ============== // Build 5.1.007: // - climate_readAdjustments() added. -// // Build 5.1.008: // - Function list was re-ordered and blank lines added for readability. // - Pollutant buildup/washoff functions for the new surfqual.c module added. // - Several other functions added, re-named or have modified arguments. -// // Build 5.1.010: // - New roadway_getInflow() function added. -// // Build 5.1.013: // - Additional arguments added to function stats_updateSubcatchStats. -// // Build 5.1.014: // - Arguments to link_getLossRate function changed. -// +// Build 5.2.0: +// - Support added for Streets and Inlets. +// - Support added for reporting most frequent non-converging links. +// - Support added for named variables & math expressions in control rules. +// - Support added for tracking a gage's prior n-hour rainfall total. +// - Refactored external inflow code. //----------------------------------------------------------------------------- #ifndef FUNCS_H #define FUNCS_H - -void project_open(char *f1, char *f2, char *f3); +//----------------------------------------------------------------------------- +// Project Methods +//----------------------------------------------------------------------------- +void project_open(const char *f1, const char *f2, const char *f3); void project_close(void); void project_readInput(void); @@ -46,7 +46,7 @@ void project_validate(void); int project_init(void); int project_addObject(int type, char* id, int n); -int project_findObject(int type, char* id); +int project_findObject(int type, const char* id); char* project_findID(int type, char* id); double** project_createMatrix(int nrows, int ncols); @@ -63,7 +63,7 @@ int input_readData(void); //----------------------------------------------------------------------------- int report_readOptions(char* tok[], int ntoks); -void report_writeLine(char* line); +void report_writeLine(const char* line); void report_writeSysTime(void); void report_writeLogo(void); void report_writeTitle(void); @@ -86,12 +86,14 @@ void report_writeQualError(TRoutingTotals* totals); void report_writeMaxStats(TMaxStats massBalErrs[], TMaxStats CourantCrit[], int nMaxStats); void report_writeMaxFlowTurns(TMaxStats flowTurns[], int nMaxStats); -void report_writeSysStats(TSysStats* sysStats); +void report_writeNonconvergedStats(TMaxStats maxNonconverged[], + int nMaxStats); +void report_writeTimeStepStats(TTimeStepStats* timeStepStats); void report_writeErrorMsg(int code, char* msg); void report_writeErrorCode(void); void report_writeInputErrorMsg(int k, int sect, char* line, long lineCount); -void report_writeWarningMsg(char* msg, char* id); +void report_writeWarningMsg(char* msg, char* id); void report_writeTseriesErrorMsg(int code, TTable *tseries); void inputrpt_writeInput(void); @@ -155,13 +157,12 @@ void routing_close(int routingModel); int output_open(void); void output_end(void); void output_close(void); -void output_checkFileSize(void); void output_saveResults(double reportTime); void output_updateAvgResults(void); -void output_readDateTime(int period, DateTime *aDate); -void output_readSubcatchResults(int period, int area); -void output_readNodeResults(int period, int node); -void output_readLinkResults(int period, int link); +void output_readDateTime(long period, DateTime *aDate); +void output_readSubcatchResults(long period, int index); +void output_readNodeResults(int long, int index); +void output_readLinkResults(int long, int index); //----------------------------------------------------------------------------- // Groundwater Methods @@ -256,6 +257,7 @@ void massbal_updateGwaterTotals(double vInfil, double vUpperEvap, double vLowerEvap, double vLowerPerc, double vGwater); void massbal_updateRoutingTotals(double tStep); + void massbal_initTimeStepTotals(void); void massbal_addInflowFlow(int type, double q); void massbal_addInflowQual(int type, int pollut, double w); @@ -278,16 +280,19 @@ void stats_close(void); void stats_report(void); void stats_updateCriticalTimeCount(int node, int link); -void stats_updateFlowStats(double tStep, DateTime aDate, int stepCount, - int steadyState); -void stats_updateSubcatchStats(int subcatch, double rainVol, +void stats_updateFlowStats(double tStep, DateTime aDate); +void stats_updateTimeStepStats(double tStep, int trialsCount, int steadyState); + +void stats_updateSubcatchStats(int subcatch, double rainVol, double runonVol, double evapVol, double infilVol, - double impervVol, double pervVol, double runoffVol, double runoff); //(5.1.013) + double impervVol, double pervVol, double runoffVol, double runoff); void stats_updateGwaterStats(int j, double infil, double evap, double latFlow, double deepFlow, double theta, double waterTable, double tStep); void stats_updateMaxRunoff(void); void stats_updateMaxNodeDepth(int node, double depth); +void stats_updateConvergenceStats(int node, int converged); + //----------------------------------------------------------------------------- // Raingage Methods @@ -299,6 +304,8 @@ void gage_setState(int gage, DateTime aDate); double gage_getPrecip(int gage, double *rainfall, double *snowfall); void gage_setReportRainfall(int gage, DateTime aDate); DateTime gage_getNextRainDate(int gage, DateTime aDate); +void gage_updatePastRain(int j, int tStep); +double gage_getPastRain(int gage, int hrs); //----------------------------------------------------------------------------- // Subcatchment Methods @@ -339,7 +346,7 @@ int node_readParams(int node, int type, int subIndex, char* tok[], int ntoks void node_validate(int node); void node_initState(int node); -void node_initInflow(int node, double tStep); +void node_initFlows(int node, double tStep); void node_setOldHydState(int node); void node_setOldQualState(int node); void node_setOutletDepth(int node, double yNorm, double yCrit, double z); @@ -361,18 +368,14 @@ void node_getResults(int node, double wt, float x[]); int inflow_readExtInflow(char* tok[], int ntoks); int inflow_readDwfInflow(char* tok[], int ntoks); int inflow_readDwfPattern(char* tok[], int ntoks); -int inflow_setExtInflow(int j, int param, int type, - int tSeries, int basePat, double cf, - double baseline, double sf); -int inflow_validate(int param, int type, int tSeries, - int basePat, double *cf); - +int inflow_setExtInflow(int j, int param, int type, int tSeries, + int basePat, double cf, double baseline, double sf); + void inflow_initDwfInflow(TDwfInflow* inflow); void inflow_initDwfPattern(int pattern); double inflow_getExtInflow(TExtInflow* inflow, DateTime aDate); double inflow_getDwfInflow(TDwfInflow* inflow, int m, int d, int h); -double inflow_getPatternFactor(int p, int month, int day, int hour); void inflow_deleteExtInflows(int node); void inflow_deleteDwfInflows(int node); @@ -419,7 +422,7 @@ double link_getYnorm(int link, double q); double link_getVelocity(int link, double q, double y); double link_getFroude(int link, double v, double y); double link_getPower(int link); -double link_getLossRate(int link, double q); //(5.1.014) +double link_getLossRate(int link, double q); char link_getFullState(double a1, double a2, double aFull); void link_getResults(int link, double wt, float x[]); @@ -431,6 +434,7 @@ int xsect_isOpen(int type); int xsect_setParams(TXsect *xsect, int type, double p[], double ucf); void xsect_setIrregXsectParams(TXsect *xsect); void xsect_setCustomXsectParams(TXsect *xsect); +void xsect_setStreetXsectParams(TXsect *xsect); double xsect_getAmax(TXsect* xsect); double xsect_getSofA(TXsect* xsect, double area); @@ -464,6 +468,15 @@ int transect_create(int n); void transect_delete(void); int transect_readParams(int* count, char* tok[], int ntoks); void transect_validate(int j); +void transect_createStreetTransect(TStreet* street); + +//----------------------------------------------------------------------------- +// Street Cross-Section Methods +//----------------------------------------------------------------------------- +int street_create(int nStreets); +void street_delete(); +int street_readParams(char* tok[], int ntoks); +double street_getExtentFilled(int link); //----------------------------------------------------------------------------- // Custom Shape Cross-Section Methods @@ -475,8 +488,12 @@ int shape_validate(TShape *shape, TTable *curve); //----------------------------------------------------------------------------- int controls_create(int n); void controls_delete(void); +void controls_init(void); +void controls_addToCount(char* s); +int controls_addVariable(char* tok[], int ntoks); +int controls_addExpression(char* tok[], int ntoks); int controls_addRuleClause(int rule, int keyword, char* Tok[], int nTokens); -int controls_evaluate(DateTime currentTime, DateTime elapsedTime, +int controls_evaluate(DateTime currentTime, DateTime elapsedTime, double tStep); //----------------------------------------------------------------------------- @@ -500,8 +517,8 @@ double table_inverseLookup(TTable* table, double y); double table_getSlope(TTable *table, double x); double table_getMaxY(TTable *table, double x); -double table_getArea(TTable* table, double x); -double table_getInverseArea(TTable* table, double a); +double table_getStorageVolume(TTable* table, double x); +double table_getStorageDepth(TTable* table, double v); void table_tseriesInit(TTable *table); double table_tseriesLookup(TTable* table, double t, char extend); @@ -516,13 +533,15 @@ int getDouble(char *s, double *y); // get double from string char* getTempFileName(char *s); // get temporary file name int findmatch(char *s, char *keyword[]); // search for matching keyword int match(char *str, char *substr); // true if substr matches part of str -int strcomp(char *s1, char *s2); // case insensitive string compare -char* sstrncpy(char *dest, const char *src, - size_t maxlen); // safe string copy -void writecon(char *s); // writes string to console +int strcomp(const char *s1, const char *s2); // case insensitive string compare +size_t sstrncpy(char *dest, const char *src, + size_t n); // safe string copy +size_t sstrcat(char* dest, const char* src, + size_t destsize); // safe string concatenation +void writecon(const char *s); // writes string to console DateTime getDateTime(double elapsedMsec); // convert elapsed time to date void getElapsedTime(DateTime aDate, // convert elapsed date int* days, int* hrs, int* mins); - +char* addAbsolutePath(char *fname); // add full path to a file name #endif //FUNCS_H diff --git a/swmm/swmm5/swmm5/gage.c b/swmm/swmm5/swmm5/gage.c index b8b0e65..031cc01 100644 --- a/swmm/swmm5/swmm5/gage.c +++ b/swmm/swmm5/swmm5/gage.c @@ -2,20 +2,22 @@ // gage.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/10 (Build 5.1.001) -// 09/15/14 (Build 5.1.007) -// 05/10/18 (Build 5.1.013) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) // Author: L. Rossman // // Rain gage functions. // +// Update History +// ============== // Build 5.1.007: // - Support for monthly rainfall adjustments added. -// // Build 5.1.013: // - Validation no longer performed on unused gages. -// +// Build 5.2.0: +// - Support added for tracking a gage's prior n-hour rainfall total. +// - Support added for relative file names. +// - Support added for setting rainfall through API call. //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE @@ -37,6 +39,9 @@ const double OneSecond = 1.1574074e-5; // gage_setState (called by runoff_execute & getRainfall in rdii.c) // gage_getPrecip (called by subcatch_getRunoff) // gage_getNextRainDate (called by runoff_getTimeStep) +// gage_updatePastRain (called by runoff_execute) +// gage_getPastRain (called by getRainValue in controls.c) +// gage_setReportRainfall (called by output_saveSubcatchResults) //----------------------------------------------------------------------------- // Local functions @@ -46,7 +51,7 @@ static int readGageFileFormat(char* tok[], int ntoks, double x[]); static int getFirstRainfall(int gage); static int getNextRainfall(int gage); static double convertRainfall(int gage, double rain); - +static void initPastRain(int gage); //============================================================================= @@ -82,16 +87,16 @@ int gage_readParams(int j, char* tok[], int ntoks) x[4] = NO_DATE; // Default is no start/end date x[5] = NO_DATE; x[6] = 0.0; // US units - strcpy(fname, ""); - strcpy(staID, ""); + fname[0] = '\0'; + staID[0] = '\0'; if ( ntoks < 5 ) return error_setInpError(ERR_ITEMS, ""); k = findmatch(tok[4], GageDataWords); - if ( k == RAIN_TSERIES ) + if ( k == RAIN_TSERIES ) { err = readGageSeriesFormat(tok, ntoks, x); } - else if ( k == RAIN_FILE ) + else if ( k == RAIN_FILE ) { if ( ntoks < 8 ) return error_setInpError(ERR_ITEMS, ""); sstrncpy(fname, tok[5], MAXFNAME); @@ -112,7 +117,7 @@ int gage_readParams(int j, char* tok[], int ntoks) else Gage[j].dataSource = RAIN_FILE; if ( Gage[j].dataSource == RAIN_FILE ) { - sstrncpy(Gage[j].fname, fname, MAXFNAME); + sstrncpy(Gage[j].fname, addAbsolutePath(fname), MAXFNAME); sstrncpy(Gage[j].staID, staID, MAXMSG); Gage[j].startFileDate = x[4]; Gage[j].endFileDate = x[5]; @@ -154,7 +159,7 @@ int readGageSeriesFormat(char* tok[], int ntoks, double x[]) ts = project_findObject(TSERIES, tok[5]); if ( ts < 0 ) return error_setInpError(ERR_NAME, tok[5]); x[0] = (double)ts; - strcpy(tok[2], ""); + sstrncpy(tok[2], "", 0); return 0; } @@ -269,8 +274,9 @@ void gage_initState(int j) // Purpose: initializes state of rain gage. // { - // --- initialize actual and reported rainfall //(5.1.013) + // --- initialize actual and reported rainfall Gage[j].rainfall = 0.0; + Gage[j].apiRainfall = MISSING; Gage[j].reportRainfall = 0.0; if ( IgnoreRainfall ) return; @@ -309,6 +315,7 @@ void gage_initState(int j) else if ( !getNextRainfall(j) ) Gage[j].nextDate = NO_DATE; } else Gage[j].startDate = NO_DATE; + initPastRain(j); } //============================================================================= @@ -339,6 +346,14 @@ void gage_setState(int j, DateTime t) return; } + // --- use rainfall supplied by API function call + // (where constant ZERO (1.e-10) is used for 0 rainfall) + if (Gage[j].apiRainfall != MISSING) + { + Gage[j].rainfall = Gage[j].apiRainfall; + return; + } + // --- otherwise march through rainfall record until date t is bracketed t += OneSecond; for (;;) @@ -388,6 +403,86 @@ void gage_setState(int j, DateTime t) //============================================================================= +void initPastRain(int j) +{ + // --- initialize past hourly rain accumulation + int i; + for (i = 0; i <= MAXPASTRAIN; i++) + Gage[j].pastRain[i] = 0.0; + Gage[j].pastInterval = 0; +} + +//============================================================================= + +void gage_updatePastRain(int j, int tStep) +// +// Input: j = rain gage index +// tStep = current runoff time step (sec) +// Output: none +// Purpose: updates past MAXPASTRAIN hourly rain totals. +// +// Note: pastRain[0] is past rain volume prior to 1 hour, +// pastRain[n] is past rain volume after n hours, +// pastInterval is time since last hour was reached. +{ + int i, t; + double r; + + // --- current rainfall intensity (in/sec or mm/sec) + r = Gage[j].rainfall / 3600.; + + // --- process each hourly interval of current time step + while (tStep > 0) + { + // --- time for most recent rainfall interval to reach 1 hr + t = 3600 - Gage[j].pastInterval; + + // --- remaining time step is greater than this time + if (tStep > t) + { + // --- add current rain to most recent interval + Gage[j].pastRain[0] += t * r; + + // --- shift all prior hourly rain amounts by 1 hour + for (i = MAXPASTRAIN; i > 0; i-- ) + Gage[j].pastRain[i] = Gage[j].pastRain[i-1]; + + // --- begin a new most recent interval + Gage[j].pastInterval = 0; + Gage[j].pastRain[0] = 0.0; + tStep -= t; + } + // --- time to reach 1 hr in most recent interval is greater + // than remaining time step so update most recent interval + else + { + Gage[j].pastRain[0] += tStep * r; + Gage[j].pastInterval += tStep; + tStep = 0; + } + } +} + +//============================================================================= + +double gage_getPastRain(int j, int n) +// +// Input: j = rain gage index +// n = number of hours prior to current date +// Output: cumulative rain volume (inches or mm) in last n hours +// Purpose: retrieves rainfall total over some previous number of hours. +// +{ + int i; + double result = 0.0; + if (n < 1 || n > MAXPASTRAIN) return 0.0; + for (i = 1; i <= n; i++) + result += Gage[j].pastRain[i]; + return result; +} + +//============================================================================= + DateTime gage_getNextRainDate(int j, DateTime aDate) // // Input: j = rain gage index @@ -443,6 +538,13 @@ void gage_setReportRainfall(int j, DateTime reportDate) return; } + // --- rainfall set by API call + if (Gage[j].apiRainfall != MISSING) + { + Gage[j].reportRainfall = Gage[j].apiRainfall; + return; + } + // --- otherwise increase reporting time by 1 second to avoid // roundoff problems reportDate += OneSecond; @@ -590,7 +692,7 @@ double convertRainfall(int j, double r) case CUMULATIVE_RAINFALL: if ( r < Gage[j].rainAccum ) - r1 = r / Gage[j].rainInterval * 3600.0; + r1 = r / Gage[j].rainInterval * 3600.0; else r1 = (r - Gage[j].rainAccum) / Gage[j].rainInterval * 3600.0; Gage[j].rainAccum = r; break; diff --git a/swmm/swmm5/swmm5/globals.h b/swmm/swmm5/swmm5/globals.h index 400f35b..70f2431 100644 --- a/swmm/swmm5/swmm5/globals.h +++ b/swmm/swmm5/swmm5/globals.h @@ -2,43 +2,34 @@ // globals.h // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/19/14 (Build 5.1.000) -// 04/14/14 (Build 5.1.004) -// 09/15/14 (Build 5.1.007) -// 03/19/15 (Build 5.1.008) -// 08/01/16 (Build 5.1.011) -// 03/14/17 (Build 5.1.012) -// 05/10/18 (Build 5.1.013) -// 04/01/20 (Build 5.1.015) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) // Author: L. Rossman // // Global Variables // +// Update History +// ============== // Build 5.1.004: // - Ignore RDII option added. -// // Build 5.1.007: // - Monthly climate variable adjustments added. -// // Build 5.1.008: // - Number of parallel threads for dynamic wave routing added. // - Minimum dynamic wave routing variable time step added. -// // Build 5.1.011: // - Changed WarningCode to Warnings (# warnings issued) -// - Added error message text as a variable. +// - Added error message text as a variable. // - Added elapsed simulation time (in decimal days) variable. // - Added variables associated with detailed routing events. -// // Build 5.1.012: // - InSteadyState variable made local to routing_execute in routing.c. -// // Build 5.1.013: // - CrownCutoff and RuleStep added as analysis option variables. -// // Build 5.1.015: // - Fixes bug in summary statistics when Report Start date > Start Date. +// Build 5.2.0: +// - Support for relative file names added. //----------------------------------------------------------------------------- #ifndef GLOBALS_H @@ -60,15 +51,16 @@ EXTERN TFile EXTERN long Nperiods, // Number of reporting periods - TotalStepCount, // Total routing steps used //(5.1.015) - ReportStepCount, // Reporting routing steps used //(5.1.015) + TotalStepCount, // Total routing steps used + ReportStepCount, // Reporting routing steps used NonConvergeCount; // Number of non-converging steps EXTERN char Msg[MAXMSG+1], // Text of output message ErrorMsg[MAXMSG+1], // Text of error message Title[MAXTITLE][MAXMSG+1],// Project title - TempDir[MAXFNAME+1]; // Temporary file directory + TempDir[MAXFNAME+1], // Temporary file directory + InpDir[MAXFNAME+1]; // Input file directory EXTERN TRptFlags RptFlags; // Reporting options @@ -83,7 +75,7 @@ EXTERN int RouteModel, // Flow routing method ForceMainEqn, // Flow equation for force mains LinkOffsets, // Link offset convention - SurchargeMethod, // EXTRAN or SLOT method //(5.1.013) + SurchargeMethod, // EXTRAN or SLOT method AllowPonding, // Allow water to pond at nodes InertDamping, // Degree of inertial damping NormalFlowLtd, // Normal flow limited @@ -101,13 +93,12 @@ EXTERN int WetStep, // Runoff wet time step (sec) DryStep, // Runoff dry time step (sec) ReportStep, // Reporting time step (sec) - RuleStep, // Rule evaluation time step (sec) //(5.1.013) + RuleStep, // Rule evaluation time step (sec) SweepStart, // Day of year when sweeping starts SweepEnd, // Day of year when sweeping ends MaxTrials, // Max. trials for DW routing NumThreads, // Number of parallel threads used NumEvents; // Number of detailed events - //InSteadyState; // System flows remain constant EXTERN double RouteStep, // Routing time step (sec) @@ -124,7 +115,7 @@ EXTERN double HeadTol, // DW routing head tolerance (ft) SysFlowTol, // Tolerance for steady system flow LatFlowTol, // Tolerance for steady nodal inflow - CrownCutoff; // Fractional pipe crown cutoff //(5.1.013) + CrownCutoff; // Fractional pipe crown cutoff EXTERN DateTime StartDate, // Starting date @@ -173,6 +164,7 @@ EXTERN TPattern* Pattern; // Array of time patterns EXTERN TTable* Curve; // Array of curve tables EXTERN TTable* Tseries; // Array of time series tables EXTERN TTransect* Transect; // Array of transect data +EXTERN TStreet* Street; // Array of defined Street cross-sections EXTERN TShape* Shape; // Array of custom conduit shapes EXTERN TEvent* Event; // Array of routing events diff --git a/swmm/swmm5/swmm5/gwater.c b/swmm/swmm5/swmm5/gwater.c index 8de1ba8..48db9de 100644 --- a/swmm/swmm5/swmm5/gwater.c +++ b/swmm/swmm5/swmm5/gwater.c @@ -2,28 +2,24 @@ // gwater.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/19/14 (Build 5.1.000) -// 09/15/14 (Build 5.1.007) -// 03/19/15 (Build 5.1.008) -// 08/05/15 (Build 5.1.010) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) // Author: L. Rossman // // Groundwater functions. // +// Update History +// ============== // Build 5.1.007: // - User-supplied function for deep GW seepage flow added. // - New variable names for use in user-supplied GW flow equations added. -// // Build 5.1.008: // - More variable names for user-supplied GW flow equations added. // - Subcatchment area made into a shared variable. // - Evaporation loss initialized to 0. // - Support for collecting GW statistics added. -// // Build 5.1.010: // - Unsaturated hydraulic conductivity added to GW flow equation variables. -// //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE @@ -291,11 +287,11 @@ int gwater_readFlowExpression(char* tok[], int ntoks) else return error_setInpError(ERR_KEYWORD, tok[1]); // --- concatenate remaining tokens into a single string - strcpy(exprStr, tok[2]); + sstrncpy(exprStr, tok[2], MAXLINE); for ( i = 3; i < ntoks; i++) { - strcat(exprStr, " "); - strcat(exprStr, tok[i]); + sstrcat(exprStr, " ", MAXLINE+1); + sstrcat(exprStr, tok[i], MAXLINE+1); } // --- delete any previous flow eqn. @@ -306,7 +302,7 @@ int gwater_readFlowExpression(char* tok[], int ntoks) // (getVariableIndex is the function that converts a GW // variable's name into an index number) expr = mathexpr_create(exprStr, getVariableIndex); - if ( expr == NULL ) return error_setInpError(ERR_TREATMENT_EXPR, ""); + if ( expr == NULL ) return error_setInpError(ERR_MATH_EXPR, ""); // --- save expression tree with the subcatchment if ( k == 1 ) Subcatch[j].gwLatFlowExpr = expr; diff --git a/swmm/swmm5/swmm5/hash.c b/swmm/swmm5/swmm5/hash.c index 4915733..4a73e6c 100644 --- a/swmm/swmm5/swmm5/hash.c +++ b/swmm/swmm5/swmm5/hash.c @@ -21,7 +21,7 @@ #define UCHAR(x) (((x) >= 'a' && (x) <= 'z') ? ((x)&~32) : (x)) /* Case-insensitive comparison of strings s1 and s2 */ -int samestr(char *s1, char *s2) +int samestr(const char *s1, const char *s2) { int i; for (i=0; UCHAR(s1[i]) == UCHAR(s2[i]); i++) @@ -30,7 +30,7 @@ int samestr(char *s1, char *s2) } /* End of samestr */ /* Use Fletcher's checksum to compute 2-byte hash of string */ -unsigned int hash(char *str) +unsigned int hash(const char *str) { unsigned int sum1= 0, check1; unsigned long sum2= 0L; @@ -70,7 +70,7 @@ int HTinsert(HTtable *ht, char *key, int data) return(1); } -int HTfind(HTtable *ht, char *key) +int HTfind(HTtable *ht, const char *key) { unsigned int i = hash(key); struct HTentry *entry; @@ -84,7 +84,7 @@ int HTfind(HTtable *ht, char *key) return(NOTFOUND); } -char *HTfindKey(HTtable *ht, char *key) +char *HTfindKey(HTtable *ht, const char *key) { unsigned int i = hash(key); struct HTentry *entry; diff --git a/swmm/swmm5/swmm5/hash.h b/swmm/swmm5/swmm5/hash.h index 90ac1ca..ba42be0 100644 --- a/swmm/swmm5/swmm5/hash.h +++ b/swmm/swmm5/swmm5/hash.h @@ -20,11 +20,11 @@ struct HTentry typedef struct HTentry *HTtable; -HTtable *HTcreate(void); -int HTinsert(HTtable *, char *, int); -int HTfind(HTtable *, char *); -char *HTfindKey(HTtable *, char *); -void HTfree(HTtable *); +HTtable* HTcreate(void); +int HTinsert(HTtable *, char *, int); +int HTfind(HTtable *, const char *); +char* HTfindKey(HTtable *, const char *); +void HTfree(HTtable *); #endif //HASH_H diff --git a/swmm/swmm5/swmm5/headers.h b/swmm/swmm5/swmm5/headers.h index eafda8a..87139b0 100644 --- a/swmm/swmm5/swmm5/headers.h +++ b/swmm/swmm5/swmm5/headers.h @@ -2,24 +2,19 @@ // headers.h // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/14 (Build 5.1.001) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) // Author: L. Rossman // // Header files included in most SWMM5 modules. // // DO NOT CHANGE THE ORDER OF THE #INCLUDE STATEMENTS //----------------------------------------------------------------------------- - -#include -#include "consts.h" #include "macros.h" -#include "enums.h" -#include "error.h" -#include "datetime.h" #include "objects.h" +#define EXTERN extern +#include "globals.h" #include "funcs.h" +#include "error.h" #include "text.h" #include "keywords.h" -#define EXTERN extern -#include "globals.h" diff --git a/swmm/swmm5/swmm5/hotstart.c b/swmm/swmm5/swmm5/hotstart.c index 33c005e..69f7abc 100644 --- a/swmm/swmm5/swmm5/hotstart.c +++ b/swmm/swmm5/swmm5/hotstart.c @@ -2,17 +2,12 @@ // hotstart.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/14 (Build 5.1.001) -// 03/28/14 (Build 5.1.002) -// 04/23/14 (Build 5.1.005) -// 03/19/15 (Build 5.1.008) -// 08/01/16 (Build 5.1.011) -// 04/01/20 (Build 5.1.015) -// Author: L. Rossman (EPA) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) +// Author: L. Rossman // // Hot Start file functions. - +// // A SWMM hot start file contains the state of a SWMM project after // a simulation has been run, allowing it to be used to initialize // a subsequent simulation that picks up where the previous run ended. @@ -29,15 +24,15 @@ // insure that these components are of the same sub-type and maintain // the same order as when the hot start file was created. // +// Update History +// ============== // Build 5.1.008: // - Storage node hydraulic residence time (HRT) was added to the file. // - Link control settings are now applied when reading a hot start file. // - Runoff read from file assigned to newRunoff property instead of oldRunoff. // - Array indexing bug when reading snowpack state from file fixed. -// // Build 5.1.011: // - Link control setting bug when reading a hot start file fixed. -// // Build 5.1.015: // - Support added for multiple infiltration methods within a project. //----------------------------------------------------------------------------- @@ -365,13 +360,10 @@ void saveRunoff(void) // Purpose: saves current state of all subcatchments to hotstart file. // { - int i, j, k, sizeX; - double* x; + int i, j, k; + double x[6]; FILE* f = Fhotstart2.file; - sizeX = MAX(6, Nobjects[POLLUT]+1); - x = (double *) calloc(sizeX, sizeof(double)); - for (i = 0; i < Nobjects[SUBCATCH]; i++) { // Ponded depths for each sub-area & total runoff (4 elements) @@ -380,8 +372,8 @@ void saveRunoff(void) fwrite(x, sizeof(double), 4, f); // Infiltration state (max. of 6 elements) - for (j=0; j 0 ) { // Runoff quality - for (j=0; j 0 ) @@ -479,22 +485,21 @@ int getIfaceFilePolluts() } // --- read pollutant names & units - if ( NumIfacePolluts > 0 ) + if ( NumIfacePolluts > 0 && Nobjects[POLLUT] > 0 ) { // --- check each pollutant name on file with project's pollutants for (i=0; i 0 ) - { - j = project_findObject(POLLUT, s1); - if ( j < 0 ) continue; - if ( !strcomp(s2, QualUnitsWords[Pollut[j].units]) ) - return ERR_ROUTING_FILE_NOMATCH; - IfacePolluts[j] = i; - } + if ( sscanf(line, "%s %s", s1, s2) < 2 ) + return ERR_ROUTING_FILE_FORMAT; + j = project_findObject(POLLUT, s1); + if ( j < 0 ) continue; + if ( !strcomp(s2, QualUnitsWords[Pollut[j].units]) ) + return ERR_ROUTING_FILE_NOMATCH; + IfacePolluts[j] = i; } } return 0; @@ -515,8 +520,8 @@ int getIfaceFileNodes() // --- read number of interface nodes fgets(line, MAXLINE, Finflows.file); - sscanf(line, "%d", &NumIfaceNodes); - if ( NumIfaceNodes <= 0 ) return ERR_ROUTING_FILE_FORMAT; + if ( !sscanf(line, "%d", &NumIfaceNodes) || NumIfaceNodes <= 0 ) + return ERR_ROUTING_FILE_FORMAT; // --- allocate memory for interface nodes index array IfaceNodes = (int *) calloc(NumIfaceNodes, sizeof(int)); @@ -525,9 +530,12 @@ int getIfaceFileNodes() // --- read names of interface nodes from file & save their indexes for ( i=0; if0 * InfilFactor; //(5.1.013) - double fmin = infil->fmin * InfilFactor; //(5.1.013) + double f0 = infil->f0 * InfilFactor; + double fmin = infil->fmin * InfilFactor; double Fmax = infil->Fmax; double tp = infil->tp; double df = f0 - fmin; @@ -515,8 +508,8 @@ double modHorton_getInfil(THorton *infil, double tstep, double irate, // --- assign local variables double f = 0.0; double fp, fa; - double f0 = infil->f0 * InfilFactor; //(5.1.013) - double fmin = infil->fmin * InfilFactor; //(5.1.013) + double f0 = infil->f0 * InfilFactor; + double fmin = infil->fmin * InfilFactor; double df = f0 - fmin; double kd = infil->decay; double kr = infil->regen * Evap.recoveryFactor; @@ -588,7 +581,7 @@ int grnampt_setParams(TGrnAmpt *infil, double p[]) { double ksat; // sat. hyd. conductivity in in/hr - if ( p[0] < 0.0 || p[1] <= 0.0 || p[2] < 0.0 ) return FALSE; + if ( p[0] < 0.0 || p[1] <= 0.0 || p[2] < 0.0 || p[2] > 1.0) return FALSE; infil->S = p[0] / UCF(RAINDEPTH); // Capillary suction head (ft) infil->Ks = p[1] / UCF(RAINFALL); // Sat. hyd. conductivity (ft/sec) infil->IMDmax = p[2]; // Max. init. moisture deficit @@ -652,7 +645,7 @@ double grnampt_getInfil(TGrnAmpt *infil, double tstep, double irate, // { // --- find saturated upper soil zone water volume - Fumax = infil->IMDmax * infil->Lu * sqrt(InfilFactor); //(5.1.013) + Fumax = infil->IMDmax * infil->Lu * sqrt(InfilFactor); // --- reduce time until next event infil->T -= tstep; @@ -680,8 +673,8 @@ double grnampt_getUnsatInfil(TGrnAmpt *infil, double tstep, double irate, // { double ia, c1, F2, dF, Fs, kr, ts; - double ks = infil->Ks * InfilFactor; //(5.1.013) - double lu = infil->Lu * sqrt(InfilFactor); //(5.1.013) + double ks = infil->Ks * InfilFactor; + double lu = infil->Lu * sqrt(InfilFactor); // --- get available infiltration rate (rainfall + ponded water) ia = irate + depth / tstep; @@ -786,8 +779,8 @@ double grnampt_getSatInfil(TGrnAmpt *infil, double tstep, double irate, // { double ia, c1, dF, F2; - double ks = infil->Ks * InfilFactor; //(5.1.013) - double lu = infil->Lu * sqrt(InfilFactor); //(5.1.013) + double ks = infil->Ks * InfilFactor; + double lu = infil->Lu * sqrt(InfilFactor); // --- get available infiltration rate (rainfall + ponded water) ia = irate + depth / tstep; diff --git a/swmm/swmm5/swmm5/infil.h b/swmm/swmm5/swmm5/infil.h index c4923e8..f5ddf31 100644 --- a/swmm/swmm5/swmm5/infil.h +++ b/swmm/swmm5/swmm5/infil.h @@ -2,22 +2,18 @@ // infil.h // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/14 (Build 5.1.001) -// 09/15/14 (Build 5.1.007) -// 08/05/15 (Build 5.1.010) -// 05/10/18 (Build 5.1.013) -// 04/01/20 (Build 5.1.015) -// Author: L. Rossman (US EPA) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) +// Author: L. Rossman // // Public interface for infiltration functions. // +// Update History +// ============== // Build 5.1.010: // - New Modified Green Ampt infiltration option added. -// // Build 5.1.013: // - New function infil_setInfilFactor() added. -// // Build 5.1.015: // - Support added for multiple infiltration methods within a project. //----------------------------------------------------------------------------- @@ -25,7 +21,6 @@ #ifndef INFIL_H #define INFIL_H - //--------------------- // Enumerated Constants //--------------------- @@ -114,5 +109,4 @@ void grnampt_initState(TGrnAmpt *infil); double grnampt_getInfil(TGrnAmpt *infil, double tstep, double irate, double depth, int modelType); - -#endif //INFIL_H +#endif diff --git a/swmm/swmm5/swmm5/inflow.c b/swmm/swmm5/swmm5/inflow.c index 42976c1..7cb5e25 100644 --- a/swmm/swmm5/swmm5/inflow.c +++ b/swmm/swmm5/swmm5/inflow.c @@ -2,12 +2,17 @@ // inflow.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/14 (Build 5.1.001) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) // Author: L. Rossman // // Manages any Direct External or Dry Weather Flow inflows // that have been assigned to nodes of the drainage system. +// +// Update History +// ============== +// Build 5.2.0: +// - Removed references to unused extIfaceInflow member of ExtInflow struct. //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE @@ -24,10 +29,14 @@ // inflow_deleteExtInflows (called by deleteObjects in project.c) // inflow_deleteDwfInflows (called by deleteObjects in project.c) // inflow_getExtInflow (called by addExternalInflows in routing.c) +// inflow_setExtInflow (called by setNodeInflow in swmm5.c) // inflow_getDwfInflow (called by addDryWeatherInflows in routing.c) -// inflow_getPatternFactor -//============================================================================= +//----------------------------------------------------------------------------- +// Local Functions +//----------------------------------------------------------------------------- +double getPatternFactor(int p, int month, int day, int hour); + int inflow_readExtInflow(char* tok[], int ntoks) // @@ -75,6 +84,7 @@ int inflow_readExtInflow(char* tok[], int ntoks) if (param == -1) { type = FLOW_INFLOW; + cf = 1.0/UCF(FLOW); } // --- do the same for a pollutant inflow @@ -116,60 +126,14 @@ int inflow_readExtInflow(char* tok[], int ntoks) if ( basePat < 0 ) return error_setInpError(ERR_NAME, tok[7]); } + // --- include LperFT3 term in conversion factor for MASS_INFLOW + if ( type == MASS_INFLOW ) cf /= LperFT3; + return(inflow_setExtInflow(j, param, type, tseries, basePat, cf, baseline, sf)); } -int inflow_validate(int param, int type, int tseries, int basePat, double *cf) -// -// Purpose: Validates Inflow -// Input: param = -1 for Flow or Index of Pollutant -// type = FLOW_INFLOW, CONCEN_INFLOW or MASS_INFLOW -// tSeries = Time Series Index -// basePat = Base Pattern Index -// Output: cf = Unit Conversion -// Return: returns Error Code -{ - int errcode = 0; - // Validate param - if (param >= Nobjects[POLLUT]) - { - errcode = ERR_API_POLLUT_INDEX; - } - // Validate Type - else if (type != FLOW_INFLOW - && type != CONCEN_INFLOW - && type != MASS_INFLOW) - { - errcode = ERR_KEYWORD; - } - // Validate Timeseries Index - else if (tseries >= Nobjects[TSERIES]) - { - errcode = ERR_API_TSERIES_INDEX; - } - // Validate Timepattern Index - else if (basePat >= Nobjects[TIMEPATTERN]) - { - errcode = ERR_API_PATTERN_INDEX; - } - else - { - // --- assign type & cf values for a FLOW inflow - if ( type == FLOW_INFLOW ) - { - *cf = 1.0/UCF(FLOW); - } - // --- include LperFT3 term in conversion factor for MASS_INFLOW - else if ( type == MASS_INFLOW ) - { - *cf /= LperFT3; - } - } - - return(errcode); -} - +//============================================================================= int inflow_setExtInflow(int j, int param, int type, int tseries, int basePat, double cf, double baseline, double sf) @@ -185,46 +149,37 @@ int inflow_setExtInflow(int j, int param, int type, int tseries, int basePat, // Return: returns Error Code { - int errcode = 0; TExtInflow* inflow; // external inflow object - // Validate Inflow - errcode = inflow_validate(param, type, tseries, basePat, &cf); - - if (errcode == 0) + // --- check if an external inflow object for this constituent already exists + inflow = Node[j].extInflow; + while ( inflow ) { + if ( inflow->param == param ) break; + inflow = inflow->next; + } - // --- check if an external inflow object for this constituent already exists - inflow = Node[j].extInflow; - while ( inflow ) - { - if ( inflow->param == param ) break; - inflow = inflow->next; - } - - // --- if it doesn't exist, then create it - if ( inflow == NULL ) + // --- if it doesn't exist, then create it + if ( inflow == NULL ) + { + inflow = (TExtInflow *) malloc(sizeof(TExtInflow)); + if ( inflow == NULL ) { - inflow = (TExtInflow *) malloc(sizeof(TExtInflow)); - if ( inflow == NULL ) - { - return error_setInpError(ERR_MEMORY, ""); - } - inflow->next = Node[j].extInflow; - Node[j].extInflow = inflow; + return error_setInpError(ERR_MEMORY, ""); } - - // Assigning Values to the inflow object - inflow->param = param; - inflow->type = type; - inflow->tSeries = tseries; - inflow->cFactor = cf; - inflow->sFactor = sf; - inflow->baseline = baseline; - inflow->basePat = basePat; - inflow->extIfaceInflow = 0.0; + inflow->next = Node[j].extInflow; + Node[j].extInflow = inflow; } - return(errcode); + + // --- assign property values to the inflow object + inflow->param = param; + inflow->type = type; + inflow->tSeries = tseries; + inflow->cFactor = cf; + inflow->sFactor = sf; + inflow->baseline = baseline; + inflow->basePat = basePat; + return 0; } //============================================================================= @@ -265,17 +220,16 @@ double inflow_getExtInflow(TExtInflow* inflow, DateTime aDate) double sf = inflow->sFactor; // scaling factor double blv = inflow->baseline; // baseline value double tsv = 0.0; // time series value - double extIfaceInflow = inflow->extIfaceInflow;// external interfacing inflow if ( p >= 0 ) { month = datetime_monthOfYear(aDate) - 1; day = datetime_dayOfWeek(aDate) - 1; hour = datetime_hourOfDay(aDate); - blv *= inflow_getPatternFactor(p, month, day, hour); + blv *= getPatternFactor(p, month, day, hour); } if ( k >= 0 ) tsv = table_tseriesLookup(&Tseries[k], aDate, FALSE) * sf; - return cf * (tsv + blv) + cf * extIfaceInflow; + return cf * (tsv + blv); } //============================================================================= @@ -418,19 +372,19 @@ double inflow_getDwfInflow(TDwfInflow* inflow, int month, int day, int hour) double f = 1.0; // pattern factor p1 = inflow->patterns[MONTHLY_PATTERN]; - if ( p1 >= 0 ) f *= inflow_getPatternFactor(p1, month, day, hour); + if ( p1 >= 0 ) f *= getPatternFactor(p1, month, day, hour); p1 = inflow->patterns[DAILY_PATTERN]; - if ( p1 >= 0 ) f *= inflow_getPatternFactor(p1, month, day, hour); + if ( p1 >= 0 ) f *= getPatternFactor(p1, month, day, hour); p1 = inflow->patterns[HOURLY_PATTERN]; p2 = inflow->patterns[WEEKEND_PATTERN]; if ( p2 >= 0 ) { if ( day == 0 || day == 6 ) - f *= inflow_getPatternFactor(p2, month, day, hour); + f *= getPatternFactor(p2, month, day, hour); else if ( p1 >= 0 ) - f *= inflow_getPatternFactor(p1, month, day, hour); + f *= getPatternFactor(p1, month, day, hour); } - else if ( p1 >= 0 ) f *= inflow_getPatternFactor(p1, month, day, hour); + else if ( p1 >= 0 ) f *= getPatternFactor(p1, month, day, hour); return f * inflow->avgValue; } @@ -499,7 +453,7 @@ int inflow_readDwfPattern(char* tok[], int ntoks) //============================================================================= -double inflow_getPatternFactor(int p, int month, int day, int hour) +double getPatternFactor(int p, int month, int day, int hour) // // Input: p = time pattern index // month = current month of year of simulation @@ -528,5 +482,3 @@ double inflow_getPatternFactor(int p, int month, int day, int hour) } return 1.0; } - -//============================================================================= \ No newline at end of file diff --git a/swmm/swmm5/swmm5/inlet.c b/swmm/swmm5/swmm5/inlet.c new file mode 100644 index 0000000..2a09642 --- /dev/null +++ b/swmm/swmm5/swmm5/inlet.c @@ -0,0 +1,1945 @@ +//----------------------------------------------------------------------------- +// inlet.c +// +// Project: EPA SWMM5 +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) +// Author: L. Rossman +// +// Street/Channel Inlet Functions +// +// Computes capture efficiency of inlets placed in Street conduits +// or Rectangular/Trapezoidal channels using FHWA HEC-22 methods (see +// Brown, S.A. et al., Urban Drainage Design Manual, Federal Highway +// Administration Hydraulic Engineering Circular No. 22, 3rd Edition, +// FHWA-NHI-10-009, August 2013). +// +//----------------------------------------------------------------------------- +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include +#include +#include "headers.h" + +// Grate inlet +typedef struct +{ + int type; // type of grate used + double length; // length (parallel to flow) (ft) + double width; // width (perpendicular to flow) (ft) + double fracOpenArea; // fraction of grate area that is open + double splashVeloc; // splash-over velocity (ft/s) +} TGrateInlet; + +// Slotted drain inlet +typedef struct +{ + double length; // length (parallel to flow) (ft) + double width; // width (perpendicular to flow) (ft) +} TSlottedInlet; + +// Curb opening inlet +typedef struct +{ + double length; // length of curb opening (ft) + double height; // height of curb opening (ft) + int throatAngle; // type of throat angle +} TCurbInlet; + +// Custom inlet +typedef struct +{ + int onGradeCurve; // flow diversion curve index + int onSagCurve; // flow rating curve index +} TCustomInlet; + +// Inlet design object +typedef struct +{ + char * ID; // name assigned to inlet design + int type; // type of inlet used (grate, curb, etc) + TGrateInlet grateInlet; // length = 0 if not used + TSlottedInlet slottedInlet; // length = 0 if not used + TCurbInlet curbInlet; // length = 0 if not used + int customCurve; // curve index = -1 if not used +} TInletDesign; + + +// Inlet performance statistics +typedef struct +{ + int flowPeriods; // # periods with approach flow + int capturePeriods; // # periods with captured flow + int backflowPeriods; // # periods with backflow + double peakFlow; // peak flow seen by inlet (cfs) + double peakFlowCapture; // capture efficiency at peak flow + double avgFlowCapture; // average capture efficiency + double bypassFreq; // frequency of bypass flow +} TInletStats; + +// Inlet list object +struct TInlet +{ + int linkIndex; // index of conduit link with the inlet + int designIndex; // index of inlet's design + int nodeIndex; // index of node receiving captured flow + int numInlets; // # inlets on each side of street or in channel + int placement; // whether inlet is on-grade or on-sag + double clogFactor; // fractional degree of inlet clogging + double flowLimit; // inlet flow restriction (cfs) + double localDepress; // local gutter depression (ft) + double localWidth; // local depression width (ft) + + double flowFactor; // flow = flowFactor * (flow spread)^2.67 + double flowCapture; // captured flow rate (cfs) + double backflow; // backflow from capture node (cfs) + double backflowRatio; // inlet backflow / capture node overflow + TInletStats stats; // inlet performance statistics + TInlet * nextInlet; // next inlet in list +}; + +// Shared inlet variables +TInletDesign * InletDesigns; // array of available inlet designs +int InletDesignCount; // number of inlet designs +int UsesInlets; // TRUE if project uses inlets + +//----------------------------------------------------------------------------- +// Enumerations +//----------------------------------------------------------------------------- + +enum InletType { + GRATE_INLET, CURB_INLET, COMBO_INLET, SLOTTED_INLET, + DROP_GRATE_INLET, DROP_CURB_INLET, CUSTOM_INLET +}; + +enum GrateType { + P50, P50x100, P30, CURVED_VANE, TILT_BAR_45, + TILT_BAR_30, RETICULINE, GENERIC +}; + +enum InletPlacementType { AUTOMATIC, ON_GRADE, ON_SAG }; + +enum ThroatAngleType { HORIZONTAL_THROAT, INCLINED_THROAT, VERTICAL_THROAT }; + +//----------------------------------------------------------------------------- +// Constants +//----------------------------------------------------------------------------- +static char* InletTypeWords[] = + {"GRATE", "CURB", "", "SLOTTED", "DROP_GRATE", "DROP_CURB", "CUSTOM", NULL}; + +static char* GrateTypeWords[] = + {"P_BAR-50", "P_BAR-50x100", "P_BAR-30", "CURVED_VANE", "TILT_BAR-45", "TILT_BAR-30", + "RETICULINE", "GENERIC", NULL}; + +static char* ThroatAngleWords[] = + {"HORIZONTAL", "INCLINED", "VERTICAL", NULL}; + +static char *PlacementTypeWords[] = + {"AUTOMATIC", "ON_GRADE", "ON_SAG"}; + +// Coefficients for cubic polynomials fitted to Splash Over Velocity v. +// Grate Length curves in Chart 5B of HEC-22 manual taken from Denver +// UDFCD manual. +static const double SplashCoeffs[][4] = { + {2.22, 4.03, 0.65, 0.06}, //P_BAR-50 + {0.74, 2.44, 0.27, 0.02}, //P_BAR-50x100 + {1.76, 3.12, 0.45, 0.03}, //P_BAR-30 + {0.30, 4.85, 1.31, 0.15}, //Curved_Vane + {0.99, 2.64, 0.36, 0.03}, //Tilt_Bar-45 + {0.51, 2.34, 0.2, 0.01}, //Tilt_Bar-30 + {0.28, 2.28, 0.18, 0.01}}; //Reticuline + +// Grate opening ratios (Chart 9B of HEC-22 manual) +static const double GrateOpeningRatios[] = { + 0.90, //P_BAR-50 + 0.80, //P_BAR-50x100 + 0.60, //P_BAR-30 + 0.35, //Curved_Vane + 0.17, //Tilt_Bar-45 (assumed) + 0.34, //Tilt_Bar-30 + 0.80, //Reticuline + 1.00}; //Generic + +//----------------------------------------------------------------------------- +// Imported Variables +//----------------------------------------------------------------------------- +extern TLinkStats* LinkStats; // defined in STATS.C +extern TNodeStats* NodeStats; // defined in STATS.C + +//----------------------------------------------------------------------------- +// Local Shared Variables +//----------------------------------------------------------------------------- +// Variables as named in the HEC-22 manual. +static double Sx; // street cross slope +static double SL; // conduit longitudinal slope +static double Sw; // gutter + cross slope +static double a; // street gutter depression (ft) +static double W; // street gutter width (ft) +static double T; // top width of flow spread (ft) +static double n; // Manning's roughness coeff. + +// Additional variables +static int Nsides; // 1- or 2-sided street +static double Tcrown; // distance from street curb to crown (ft) +static double Beta; // = 1.486 * sqrt(SL) / n +static double Qfactor; // factor f in Izzard's eqn. Q = f*T^2.67 +static TXsect* xsect; // cross-section data of inlet's conduit +static double* InletFlow; // captured inlet flow received by each node +static TInlet* FirstInlet; // head of list of deployed inlets + +//----------------------------------------------------------------------------- +// External functions (declared in inlet.h) +//----------------------------------------------------------------------------- +// inlet_create called by createObjects in project.c +// inlet_delete called by deleteObjects in project.c +// inlet_readDesignParams called by parseLine in input.c +// inlet_readUsageParams called by parseLine in input.c +// inlet_validate called by project_validate +// inlet_findCapturedFlows called by routing_execute +// inlet_adjustQualInflows called by routing_execute +// inlet_adjustQualOutflows called by routing execute +// inlet_writeStatsReport called by statsrpt_writeReport +// inlet_capturedFlow called by findLinkMassFlow in qualrout.c + +//----------------------------------------------------------------------------- +// Local functions +//----------------------------------------------------------------------------- +static int readGrateInletParams(int inletIndex, char* tok[], int ntoks); +static int readCurbInletParams(int inletIndex, char* tok[], int ntoks); +static int readSlottedInletParams(int inletIndex, char* tok[], int ntoks); +static int readCustomInletParams(int inletIndex, char* tok[], int ntoks); + +static void initInletStats(TInlet* inlet); +static void updateInletStats(TInlet* inlet, double q); +static void writeStreetStatsHeader(); +static void writeStreetStats(int link); + +static void getBackflowRatios(); +static double getInletArea(TInlet* inlet); + +static int getInletPlacement(TInlet* inlet, int node); +static void getConduitGeometry(TInlet* inlet); +static double getFlowSpread(double flow); +static double getEo(double slopeRatio, double spread, double gutterWidth); + +static double getCustomCapturedFlow(TInlet* inlet, double flow, double depth); +static double getOnGradeCapturedFlow(TInlet* inlet, double flow, double depth); +static double getOnGradeInletCapture(int inletIndex, double flow, double depth); +static double getGrateInletCapture(int inletIndex, double flow); +static double getCurbInletCapture(double flow, double length); + +static double getGutterFlowRatio(double gutterWidth); +static double getGutterAreaRatio(double grateWidth, double area); +static double getSplashOverVelocity(int grateType, double grateLength); + +static double getOnSagCapturedFlow(TInlet* inlet, double flow, double depth); +static double getOnSagInletCapture(int inletIndex, double depth); +static void findOnSagGrateFlows(int inletIndex, double depth, + double *weirFlow, double *orificeFlow); +static void findOnSagCurbFlows(int inletIndex, double depth, + double openingLength, double *weirFlow, + double *orificeFlow); +static double getCurbOrificeFlow(double flowDepth, double openingHeight, + double openingLength, int throatAngle); +static double getOnSagSlottedFlow(int inletIndex, double depth); + +//============================================================================= + +int inlet_create(int numInlets) +// +// Input: numInlets = number of inlet designs to create +// Output: none +// Purpose: creats a collection of inlet designs. +// +{ + int i; + + InletDesigns = NULL; + InletFlow = NULL; + InletDesignCount = 0; + UsesInlets = FALSE; + FirstInlet = NULL; + InletDesigns = (TInletDesign *)calloc(numInlets, sizeof(TInletDesign)); + if (InletDesigns == NULL) return ERR_MEMORY; + InletDesignCount = numInlets; + + InletFlow = (double *)calloc(Nobjects[NODE], sizeof(double)); + if (InletFlow == NULL) return ERR_MEMORY; + + for (i = 0; i < InletDesignCount; i++) + { + InletDesigns[i].customCurve = -1; + InletDesigns[i].curbInlet.length = 0.0; + InletDesigns[i].grateInlet.length = 0.0; + InletDesigns[i].slottedInlet.length = 0.0; + InletDesigns[i].type = CUSTOM_INLET; + } + return 0; +} + +//============================================================================= + +void inlet_delete() +// +// Input: none +// Output: none +// Purpose: frees all memory allocated for inlet analysis. +// +{ + TInlet* inlet = FirstInlet; + TInlet* nextInlet; + while (inlet) + { + nextInlet = inlet->nextInlet; + free(inlet); + inlet = nextInlet; + } + FirstInlet = NULL; + FREE(InletFlow); + FREE(InletDesigns); +} + +//============================================================================= + +int inlet_readDesignParams(char* tok[], int ntoks) +// +// Input: tok[] = array of string tokens +// ntoks = number of tokens +// Output: returns an error code +// Purpose: extracts a set of inlet design parameters from a tokenized line +// of the [INLETS] section of a SWMM input file. +// +// Format of input line is: +// ID GRATE Length Width GrateType (OpenArea) (SplashVeloc) +// ID CURB Length Height (ThroatType) +// ID SLOTTED Length Width +// ID DROP_GRATE Length Width GrateType (OpenArea) (SplashVeloc) +// ID DROP_CURB Length Height +// ID CUSTOM CurveID +// +{ + int i; + + // --- check for minimum number of tokens + if ( ntoks < 3 ) return error_setInpError(ERR_ITEMS, ""); + + // --- check that design ID already registered in project + i = project_findObject(INLET, tok[0]); + if ( i < 0 ) return error_setInpError(ERR_NAME, tok[0]); + InletDesigns[i].ID = project_findID(INLET, tok[0]); + + // --- retrieve type of inlet design + InletDesigns[i].type = findmatch(tok[1], InletTypeWords); + + // --- read inlet's design parameters + switch (InletDesigns[i].type) + { + case GRATE_INLET: + case DROP_GRATE_INLET: + return readGrateInletParams(i, tok, ntoks); + case CURB_INLET: + case DROP_CURB_INLET: + return readCurbInletParams(i, tok, ntoks); + case SLOTTED_INLET: + return readSlottedInletParams(i, tok, ntoks); + case CUSTOM_INLET: + return readCustomInletParams(i, tok, ntoks); + default: return error_setInpError(ERR_KEYWORD, tok[1]); + } + return 0; +} +//============================================================================= + +int inlet_readUsageParams(char* tok[], int ntoks) +// +// Input: tok[] = array of string tokens +// ntoks = number of tokens +// Output: returns an error code +// Purpose: extracts inlet usage parameters from a tokenized line +// of the [INLET_USAGE] section of a SWMM input file. +// +// Format of input line is: +// linkID inletID nodeID (#Inlets %Clog Qmax aLocal wLocal placement) +// where +// linkID = ID name of link containing the inlet +// inletID = ID name of inlet design being used +// nodeID = ID name of node receiving captured flow +// #Inlets = number of identical inlets used (default = 1) +// %Clog = percent that inlet is clogged +// Qmax = maximum flow that inlet can capture (default = 0 (no limit)) +// aLocal = local gutter depression (ft or m) (default = 0) +// wLocal = width of local gutter depression (ft or m) (default = 0) +// placement = ON_GRADE, ON_SAG, or AUTO (the default) +// +{ + int linkIndex, designIndex, nodeIndex, numInlets = 1; + int placement = AUTOMATIC; + double flowLimit = 0.0, pctClogged = 0.0; + double aLocal = 0.0, wLocal = 0.0; + TInlet* inlet; + + // --- check that inlet's link exists + if (ntoks < 3) return error_setInpError(ERR_ITEMS, ""); + linkIndex = project_findObject(LINK, tok[0]); + if (linkIndex < 0) return error_setInpError(ERR_NAME, tok[0]); + + // --- check that inlet design type exists + designIndex = project_findObject(INLET, tok[1]); + if (designIndex < 0) return error_setInpError(ERR_NAME, tok[1]); + + // --- check that receiving node exists + nodeIndex = project_findObject(NODE, tok[2]); + if (nodeIndex < 0) return error_setInpError(ERR_NAME, tok[2]); + + // --- get number of inlets + if (ntoks > 3) + if (!getInt(tok[3], &numInlets) || numInlets < 1) + return error_setInpError(ERR_NUMBER, tok[3]); + + // --- get flow limit & percent clogged + if (ntoks > 4) + { + if (!getDouble(tok[4], &pctClogged) || pctClogged < 0.0 + || pctClogged > 99.) + return error_setInpError(ERR_NUMBER, tok[4]); + } + if (ntoks > 5) + if (!getDouble(tok[5], &flowLimit) || flowLimit < 0.0) + return error_setInpError(ERR_NUMBER, tok[5]); + + // --- get local depression parameters + if (ntoks > 6) + if (!getDouble(tok[6], &aLocal) || aLocal < 0.0) + return error_setInpError(ERR_NUMBER, tok[6]); + if (ntoks > 7) + if (!getDouble(tok[7], &wLocal) || wLocal < 0.0) + return error_setInpError(ERR_NUMBER, tok[7]); + + // --- get inlet placement + if (ntoks > 8) + { + placement = findmatch(tok[8], PlacementTypeWords); + if (placement < 0) return error_setInpError(ERR_KEYWORD, tok[8]); + } + + // --- create an inlet usage object for the link + inlet = Link[linkIndex].inlet; + if (inlet == NULL) + { + inlet = (TInlet *)malloc(sizeof(TInlet)); + if (!inlet) return error_setInpError(ERR_MEMORY, ""); + Link[linkIndex].inlet = inlet; + inlet->nextInlet = FirstInlet; + FirstInlet = inlet; + } + + // --- save inlet usage parameters + inlet->linkIndex = linkIndex; + inlet->designIndex = designIndex; + inlet->nodeIndex = nodeIndex; + inlet->numInlets = numInlets; + inlet->placement = placement; + inlet->clogFactor = 1.0 - (pctClogged / 100.); + inlet->flowLimit = flowLimit / UCF(FLOW); + inlet->localDepress = aLocal / UCF(LENGTH); + inlet->localWidth = wLocal / UCF(LENGTH); + inlet->flowFactor = 0.0; + inlet->backflowRatio = 0.0; + initInletStats(inlet); + UsesInlets = TRUE; + return 0; +} + +//============================================================================= + +void inlet_validate() +// +// Input: none +// Output: none +// Purpose: checks that inlets have been assigned to conduits with proper +// cross section shapes and counts the number of inlets that each +// node receives either bypased or captured flow from. +// +{ + int i, j, inletType, inletValid; + TInlet* inlet; + TInlet* prevInlet; + + // --- traverse the list of inlets placed in conduits + if (!UsesInlets) return; + prevInlet = FirstInlet; + inlet = FirstInlet; + while (inlet) + { + // --- check that inlet's conduit can accept the inlet's type + inletValid = FALSE; + i = inlet->linkIndex; + xsect = &Link[i].xsect; + inletType = InletDesigns[inlet->designIndex].type; + if (inletType == CUSTOM_INLET) + { + j = InletDesigns[inlet->designIndex].customCurve; + if (j >= 0) + { + if (Curve[j].curveType == DIVERSION_CURVE || + Curve[j].curveType == RATING_CURVE) + inletValid = TRUE; + } + } + else if ((xsect->type == TRAPEZOIDAL || xsect->type == RECT_OPEN) && + (inletType == DROP_GRATE_INLET || + inletType == DROP_CURB_INLET)) + inletValid = TRUE; + else if (xsect->type == STREET_XSECT && + inletType != DROP_GRATE_INLET && + inletType != DROP_CURB_INLET) + inletValid = TRUE; + + // --- if inlet placement is valid then + if (inletValid) + { + // --- record that receptor node has inlets + Node[Link[i].node2].inlet = BYPASS; + Node[inlet->nodeIndex].inlet = CAPTURE; + + // --- initialize inlet's backflow + inlet->backflow = 0.0; + + // --- compute street inlet's flow factor for Izzard's eqn. + // (used in Q = flowFactor * Spread^2.67 equation) + getConduitGeometry(inlet); + inlet->flowFactor = (0.56/n) * pow(SL,0.5) * pow(Sx,1.67); + + // --- save reference to current inlet & continue to next inlet + prevInlet = inlet; + inlet = inlet->nextInlet; + } + + // --- if inlet placement is not valid then issue a warning message + // and remove the inlet from the conduit + else + { + report_writeWarningMsg(WARN12, Link[i].ID); + if (inlet == FirstInlet) + { + FirstInlet = inlet->nextInlet; + prevInlet = FirstInlet; + free(inlet); + inlet = FirstInlet; + } + else + { + prevInlet->nextInlet = inlet->nextInlet; + free(inlet); + inlet = prevInlet->nextInlet; + } + Link[i].inlet = NULL; + } + } + + // --- determine how capture node's overflow is split between its inlets + getBackflowRatios(); +} + +//============================================================================= + +void inlet_findCapturedFlows(double tStep) +// +// Input: tStep = current flow routing time step (sec) +// Output: none +// Purpose: computes flow captured by each inlet and adjusts the +// lateral flows of the inlet's bypass and capture nodes accordingly. +// +// This function is called after regular lateral flows to all nodes have been +// set but before a flow routing step has been taken. +{ + int i, j, m, placement; + double q; + TInlet *inlet; + + // --- For non-DW routing find conduit flow into each node + // (used to limit max. amount of on-sag capture) + if (!UsesInlets) return; + memset(InletFlow, 0, Nobjects[NODE]*sizeof(double)); + if (RouteModel != DW) + { + for (j = 0; j < Nobjects[NODE]; j++) + Node[j].inflow = MAX(0., Node[j].newLatFlow); + for (i = 0; i < Nobjects[LINK]; i++) + Node[Link[i].node2].inflow += MAX(0.0, Link[i].newFlow); + } + + // --- loop through each inlet + for (inlet = FirstInlet; inlet != NULL; inlet = inlet->nextInlet) + { + // --- identify indexes of inlet's bypass (j) and capture (m) nodes + i = inlet->linkIndex; + j = Link[i].node2; + m = inlet->nodeIndex; + + // --- get inlet's placement (ON_GRADE or ON_SAG) + placement = getInletPlacement(inlet, j); + + // --- find flow captured by a Custom inlet + if (InletDesigns[inlet->designIndex].type == CUSTOM_INLET) + { + q = fabs(Link[i].newFlow); + inlet->flowCapture = getCustomCapturedFlow(inlet, q, Node[j].newDepth); + } + + // --- find flow captured by on-grade inlet + else if (placement == ON_GRADE) + { + q = fabs(Link[i].newFlow); + inlet->flowCapture = getOnGradeCapturedFlow(inlet, q, Node[j].newDepth); + } + + // --- find flow captured by on-sag inlet + else + { + q = Node[j].inflow; + inlet->flowCapture = getOnSagCapturedFlow(inlet, q, Node[j].newDepth); + } + if (fabs(inlet->flowCapture) < FUDGE) inlet->flowCapture = 0.0; + + // --- add to total flow captured by inlet's node + InletFlow[j] += inlet->flowCapture; + + // --- capture node's overflow becomes inlet's backflow + inlet->backflow = Node[m].overflow * inlet->backflowRatio; + if (fabs(inlet->backflow) < FUDGE) inlet->backflow = 0.0; + } + + // --- make second pass through each inlet + for (inlet = FirstInlet; inlet != NULL; inlet = inlet->nextInlet) + { + // --- identify indexes of inlet's bypass (j) and capture (m) nodes + i = inlet->linkIndex; + j = Link[i].node2; + m = inlet->nodeIndex; + + // --- for on-sag placement under non-DW routing, captured flow + // is limited to inlet's share of bypass node's inflow plus + // any stored volume + if (RouteModel != DW && getInletPlacement(inlet, j) == ON_SAG) + { + q = Node[j].newVolume / tStep; + q += MAX(Node[j].inflow, 0.0); + if (InletFlow[j] > q) + inlet->flowCapture *= q / InletFlow[j]; + } + + // --- adjust lateral flows at bypass and capture nodes + // (subtract captured flow from bypass node, add it to capture + // node, and add any backflow to bypass node) + Node[j].newLatFlow -= (inlet->flowCapture - inlet->backflow); + Node[m].newLatFlow += inlet->flowCapture; + + // --- update inlet's performance if reporting has begun + if (getDateTime(NewRoutingTime) > ReportStart) + updateInletStats(inlet, fabs(Link[i].newFlow)); + } +} + +//============================================================================= + +void inlet_adjustQualInflows() +// +// Input: none +// Output: none +// Purpose: adjusts accumulated flow rates and pollutant mass inflows at each +// inlet's bypass and capture nodes after a flow routing step has +// been taken prior to a quality routing step. +// +{ + int i, j, m, p; + double qNet; + TInlet* inlet; + + if (!UsesInlets) return; + if (IgnoreQuality || Nobjects[POLLUT] == 0) return; + for (inlet = FirstInlet; inlet != NULL; inlet = inlet->nextInlet) + { + // --- identify indexes of inlet's bypass (j) and capture (m) nodes + i = inlet->linkIndex; + j = Link[i].node2; + m = inlet->nodeIndex; + + // --- there's a net flow from the bypass to the capture node + qNet = inlet->flowCapture - inlet->backflow; + if (qNet > 0.0) + { + // --- add net capture flow to capture node's accumulated flow + // inflow for quality routing + Node[m].qualInflow += qNet; + + // --- and do the same for pollutant mass flows + // (Node[m].newQual is the mass inflow accumulator for node m) + for (p = 0; p < Nobjects[POLLUT]; p++) + Node[m].newQual[p] += qNet * Node[j].oldQual[p]; + } + + // --- there's a net backflow from the capture to the bypass node + else + { + // --- add the backflow flow rate and pollutant mass flow to the + // bypass node's accumulated flow and pollutant mass inflow + qNet = -qNet; + Node[j].qualInflow += qNet; + for (p = 0; p < Nobjects[POLLUT]; p++) + Node[j].newQual[p] += qNet * Node[m].oldQual[p]; + } + } +} + +//============================================================================= + +void inlet_adjustQualOutflows() +// +// Input: none +// Output: none +// Purpose: adjusts mass balance totals after a complete routing step has been +// taken so as not to treat inlet transfer flows as system outflows. +// +{ + int j, p; + double q, w; + TInlet* inlet; + + // --- these variables, declared in massbal.c, accumulate system-wide flow and + // pollutant mass fluxes over a time step to use in mass balances + extern TRoutingTotals StepFlowTotals; + extern TRoutingTotals* StepQualTotals; + + // --- examine each node + for (j = 0; j < Nobjects[NODE]; j++) + { + // --- node receives captured flow from an inlet + if (Node[j].inlet == CAPTURE) + { + // --- node also has an overflow (e.g., it's a surcharged sewer node) + q = Node[j].overflow; + if (q > 0.0) + { + // --- remove overflow from system flooding total since it does + // not leave the system (it is sent to inlet's bypass node) + StepFlowTotals.flooding -= q; + + // --- also remove pollutant overflow mass from system totals + if (!IgnoreQuality) + for (p = 0; p < Nobjects[POLLUT]; p++) + { + w = q * Node[j].newQual[p]; + StepQualTotals[p].flooding -= w; + } + } + } + } + + // --- for WQ analysis, examine each inlet's bypass node + if (!IgnoreQuality && Nobjects[POLLUT] > 0) + { + for (inlet = FirstInlet; inlet != NULL; inlet = inlet->nextInlet) + { + j = Link[inlet->linkIndex].node2; + + // --- inlet has net positive flow capture leading to + // node having a net negative lateral inflow + q = inlet->flowCapture - inlet->backflow; + if (q > 0.0 && Node[j].newLatFlow < 0.0) + + // --- remove the pollutant mass in the captured flow from + // the system totals since it does not leave the system + // (it is sent to the inlet's capture node) + for (p = 0; p < Nobjects[POLLUT]; p++) + { + w = q * Node[j].newQual[p]; + StepQualTotals[p].outflow -= w; + } + } + } +} + +//============================================================================= + +void inlet_writeStatsReport() +// +// Input: none +// Output: none +// Purpose: writes table of street & inlet flow statistics to SWMM's report file. +// +{ + int j, header = FALSE; + + if (Nobjects[STREET] == 0) return; + for (j = 0; j < Nobjects[LINK]; j++) + { + if (Link[j].xsect.type == STREET_XSECT) + { + if (!header) + { + writeStreetStatsHeader(); + header = TRUE; + } + writeStreetStats(j); + } + } + report_writeLine(""); +} + +//============================================================================= + +double inlet_capturedFlow(int i) +// +// Input: i = a link index +// Output: returns captured flow rate (cfs) +// Purpose: gets the current flow captured by an inlet. +// +{ + if (Link[i].inlet) return Link[i].inlet->flowCapture; + return 0.0; +} + +//============================================================================= + +int readGrateInletParams(int i, char* tok[], int ntoks) +{ +// +// Input: i = inlet index +// tok[] = array of string tokens +// ntoks = number of tokens +// Output: returns an error code +// Purpose: extracts a grate's inlet parameters from a set of string tokens. +// + int grateType; + double width, length, areaRatio = 0.0, vSplash = 0.0; + + // --- check for enough tokens + if (ntoks < 5) return error_setInpError(ERR_ITEMS, ""); + + // --- retrieve length & width + if (!getDouble(tok[2], &length) || length <= 0.0) + return error_setInpError(ERR_NUMBER, tok[2]); + if (!getDouble(tok[3], &width) || width <= 0.0) + return error_setInpError(ERR_NUMBER, tok[3]); + + // --- retrieve grate type + grateType = findmatch(tok[4], GrateTypeWords); + if (grateType < 0) return error_setInpError(ERR_KEYWORD, tok[4]); + + // --- only read open area & splash velocity for GENERIC type grate + if (grateType == GENERIC) + { + if (ntoks < 6) return error_setInpError(ERR_ITEMS, ""); + if (!getDouble(tok[5], &areaRatio) || areaRatio <= 0.0 + || areaRatio > 1.0) return error_setInpError(ERR_NUMBER, tok[5]); + if (ntoks > 6) + { + if (!getDouble(tok[6], &vSplash) || vSplash < 0.0) + return error_setInpError(ERR_NUMBER, tok[6]); + } + } + + // --- save grate inlet parameters + InletDesigns[i].grateInlet.length = length / UCF(LENGTH); + InletDesigns[i].grateInlet.width = width / UCF(LENGTH); + InletDesigns[i].grateInlet.type = grateType; + InletDesigns[i].grateInlet.fracOpenArea = areaRatio; + InletDesigns[i].grateInlet.splashVeloc = vSplash / UCF(LENGTH); + + // --- check if grate is part of a combo inlet + if (InletDesigns[i].type == GRATE_INLET && + InletDesigns[i].curbInlet.length > 0.0) + InletDesigns[i].type = COMBO_INLET; + return 0; +} + +//============================================================================= + +int readCurbInletParams(int i, char* tok[], int ntoks) +// +// Input: i = inlet index +// tok[] = array of string tokens +// ntoks = number of tokens +// Output: returns an error code +// Purpose: extracts curb opening inlet parameters from a set of string tokens. +// +{ + int throatAngle; + double height, length; + + // --- check for enough tokens + if (ntoks < 4) return error_setInpError(ERR_ITEMS, ""); + + // --- retrieve length & width of opening + if (!getDouble(tok[2], &length) || length <= 0.0) + return error_setInpError(ERR_NUMBER, tok[2]); + if (!getDouble(tok[3], &height) || height <= 0.0) + return error_setInpError(ERR_NUMBER, tok[3]); + + // --- retrieve type of throat angle for curb inlet + throatAngle = VERTICAL_THROAT; + if (InletDesigns[i].type == CURB_INLET && ntoks > 4) + { + throatAngle = findmatch(tok[4], ThroatAngleWords); + if (throatAngle < 0) return error_setInpError(ERR_KEYWORD, tok[4]); + } + + // ---- save curb opening inlet parameters + InletDesigns[i].curbInlet.length = length / UCF(LENGTH); + InletDesigns[i].curbInlet.height = height / UCF(LENGTH); + InletDesigns[i].curbInlet.throatAngle = throatAngle; + + // --- check if curb inlet is part of a combo inlet + if (InletDesigns[i].type == CURB_INLET && + InletDesigns[i].grateInlet.length > 0.0) + InletDesigns[i].type = COMBO_INLET; + return 0; +} + +//============================================================================= + +int readSlottedInletParams(int i, char* tok[], int ntoks) +// +// Input: i = inlet index +// tok[] = array of string tokens +// ntoks = number of tokens +// Output: returns an error code +// Purpose: extracts slotted drain inlet parameters from a set of string tokens. +// +{ + double width, length; + + // --- check for enough tokens + if (ntoks < 4) return error_setInpError(ERR_ITEMS, ""); + + // --- retrieve length and width + if (!getDouble(tok[2], &length) || length <= 0.0) + return error_setInpError(ERR_NUMBER, tok[2]); + if (!getDouble(tok[3], &width) || width <= 0.0) + return error_setInpError(ERR_NUMBER, tok[3]); + + // --- save slotted inlet parameters + InletDesigns[i].slottedInlet.length = length / UCF(LENGTH); + InletDesigns[i].slottedInlet.width = width / UCF(LENGTH); + return 0; +} + +//============================================================================= + +int readCustomInletParams(int i, char* tok[], int ntoks) +// +// Input: i = inlet index +// tok[] = array of string tokens +// ntoks = number of tokens +// Output: returns an error code +// Purpose: extracts custom inlet parameters from a set of string tokens. +// +{ + int c; // capture curve index + + if (ntoks < 3) return error_setInpError(ERR_ITEMS, ""); + else + { + c = project_findObject(CURVE, tok[2]); + if (c < 0) return error_setInpError(ERR_NAME, tok[2]); + } + InletDesigns[i].customCurve = c; + return 0; +} + +//============================================================================= + +void initInletStats(TInlet* inlet) +// +// Input: inlet = an inlet object placed in a conduit link +// Output: none +// Purpose: initializes the performance statistics of an inlet. +// +{ + if (inlet) + { + inlet->flowCapture = 0.0; + inlet->backflow = 0.0; + inlet->stats.flowPeriods = 0; + inlet->stats.capturePeriods = 0; + inlet->stats.backflowPeriods = 0; + inlet->stats.peakFlow = 0.0; + inlet->stats.peakFlowCapture = 0; + inlet->stats.avgFlowCapture = 0; + inlet->stats.bypassFreq = 0; + } +} + +//============================================================================= + +void updateInletStats(TInlet* inlet, double q) +// +// Input: inlet = an inlet object placed in a conduit link +// q = inlet's approach flow (cfs) +// Output: none +// Purpose: updates the performance statistics of an inlet. +// +{ + double qCapture = inlet->flowCapture, + qBackflow = inlet->backflow, + qNet = qCapture - qBackflow, + qBypass = q - qNet, + fCapture = 0.0; + + // --- check for no flow condition + if (q < MIN_RUNOFF_FLOW && qBackflow <= 0.0) return; + inlet->stats.flowPeriods++; + + // --- there is positive net flow from inlet to capture node + if (qNet > 0.0) + { + inlet->stats.capturePeriods++; + fCapture = qNet / q; + fCapture = MIN(fCapture, 1.0); + inlet->stats.avgFlowCapture += fCapture; + if (qBypass > MIN_RUNOFF_FLOW) inlet->stats.bypassFreq++; + } + + // --- otherwise inlet receives backflow from capture node + else inlet->stats.backflowPeriods++; + + // --- update peak flow stats + if (q > inlet->stats.peakFlow) + { + inlet->stats.peakFlow = q; + inlet->stats.peakFlowCapture = fCapture * 100.0; + } +} + +//============================================================================= + +void writeStreetStatsHeader() +// +// Input: none +// Output: none +// Purpose: writes column headers for Street Flow Summary table to SWMM's report file. +// +{ + report_writeLine(""); + report_writeLine("*******************"); + report_writeLine("Street Flow Summary"); + report_writeLine("*******************"); + report_writeLine(""); + fprintf(Frpt.file, +"\n ----------------------------------------------------------------------------------------------------------------------" +"\n Peak Maximum Maximum Peak Flow Average Bypass BackFlow" +"\n Flow Spread Depth Inlet Inlet Capture Capture Frequency Frequency"); + if (UnitSystem == US) fprintf(Frpt.file, +"\n Street Conduit %3s ft ft Design Location %% %% %% %%", + FlowUnitWords[FlowUnits]); + else fprintf(Frpt.file, +"\n Street Conduit %3s m m Design Location %% %% %% %%", + FlowUnitWords[FlowUnits]); + fprintf(Frpt.file, +"\n ----------------------------------------------------------------------------------------------------------------------"); +} + +//============================================================================= + +void writeStreetStats(int link) +// +// Input: link = index of a conduit link containing an inlet +// Output: none +// Purpose: writes flow statistics for a Street conduit and its inlet to +// SWMM's report file. +// +{ + int k, t, placement; + double maxSpread, maxDepth, maxFlow; + double fp, cp, afc = 0.0, bpf = 0.0; + TInlet* inlet; + + // --- retrieve street parameters + k = Link[link].subIndex; + t = Link[link].xsect.transect; + inlet = Link[link].inlet; + + // --- get recorded max flow and depth + maxFlow = LinkStats[link].maxFlow; + maxDepth = LinkStats[link].maxDepth; + + // --- SWMM's spread (flow width) at max depth + maxSpread = xsect_getWofY(&Link[link].xsect, maxDepth) / Street[t].sides; + maxSpread = MIN(maxSpread, Street[t].width); +/* + // HEC-22's spread based on max flow (doesn't account for backwater) + Sx = Street[t].slope; + a = Street[t].gutterDepression; + W = Street[t].gutterWidth; + n = Street[t].roughness; + Qfactor = (0.56 / n) * sqrt(Conduit[k].slope) * pow(Sx, 1.67); + maxSpread = getFlowSpread(maxFlow / Street[t].sides); + maxSpread = MIN(maxSpread, Street[t].width); +*/ + // --- write street stats + fprintf(Frpt.file, "\n %-16s", Link[link].ID); + fprintf(Frpt.file, " %9.3f", maxFlow * UCF(FLOW)); + fprintf(Frpt.file, " %9.3f", maxSpread * UCF(LENGTH)); + fprintf(Frpt.file, " %9.3f", maxDepth * UCF(LENGTH)); + + // --- write inlet stats + if (inlet) + { + fprintf(Frpt.file, " %-16s", InletDesigns[inlet->designIndex].ID); + placement = getInletPlacement(inlet, Link[inlet->linkIndex].node2); + if (placement == ON_GRADE) + fprintf(Frpt.file, " ON-GRADE"); + else + fprintf(Frpt.file, " ON-SAG "); + fp = inlet->stats.flowPeriods / 100.0; + if (fp > 0.0) + { + cp = inlet->stats.capturePeriods / 100.0; + fprintf(Frpt.file, " %9.2f", inlet->stats.peakFlowCapture); + if (cp > 0.0) + { + afc = inlet->stats.avgFlowCapture / cp; + bpf = inlet->stats.bypassFreq / cp; + } + fprintf(Frpt.file, " %9.2f", afc); + fprintf(Frpt.file, " %9.2f", bpf); + fprintf(Frpt.file, " %9.2f", inlet->stats.backflowPeriods / fp); + } + } +} + +//============================================================================= + +int getInletPlacement(TInlet* inlet, int j) +// +// Input: inlet = an inlet object placed in a conduit link +// j = index of inlet's bypass node +// Output: returns type of inlet placement +// Purpose: determines actual placement for an inlet with AUTOMATIC placement. +// +{ + if (inlet->placement == AUTOMATIC) + { + if (Node[j].degree > 0) return ON_GRADE; + else return ON_SAG; + } + else return inlet->placement; +} + +//============================================================================= + +void getConduitGeometry(TInlet* inlet) +// +// Input: inlet = an inlet object placed in a conduit link +// Output: none +// Purpose: assigns properties of an inlet's conduit to +// module-level shared variables used by other functions. +// +{ + int linkIndex = inlet->linkIndex; + int t, k = Link[linkIndex].subIndex; + + SL = Conduit[k].slope; // longitudinal slope + Beta = Conduit[k].beta; // 1.486 * sqrt(SL) / n + xsect = &Link[linkIndex].xsect; + + // --- if conduit has a Street cross section + if (xsect->type == STREET_XSECT) + { + t = xsect->transect; + Sx = Street[t].slope; // street cross slope + a = Street[t].gutterDepression; // gutter depression + W = Street[t].gutterWidth; // gutter width + n = Street[t].roughness; // street roughness + Nsides = Street[t].sides; // 1 or 2 sided street + Tcrown = Street[t].width; // distance from curb to crown + Qfactor = inlet->flowFactor; // factor used in Izzard's eqn. + + // --- add inlet's local depression to street's continuous depression + if (inlet && inlet->localDepress * inlet->localWidth > 0) + { + a += inlet->localDepress; // inlet depression + W = inlet->localWidth; // inlet depressed width + } + + // --- slope of depressed gutter section + if (W * a > 0.0) Sw = Sx + a / W; + else Sw = Sx; + } + + // --- conduit has rectangular or trapezoidal cross section + else + { + a = 0.0; + W = 0.0; + n = Conduit[k].roughness; + Nsides = 1; + Sx = 0.01; + Sw = Sx; + } +} + +//============================================================================= + +double getFlowSpread(double Q) +// +// Input: Q = conduit flow rate (cfs) +// Output: returns width of flow spread (ft) +// Purpose: computes width of flow spread across a Street cross section using +// HEC-22 equations derived from Izzard's form of the Manning eqn. +// +{ + int iter; + double f, f1, Sr, Ts1, Ts2, Tw, Qs, Eo; + + f = Qfactor; // = (0.56/n) * SL^0.5 * Sx^1.67 + + // --- no depressed curb + if (a == 0.0) + { + Ts1 = pow(Q / f, 0.375); //HEC-22 Eq(4-2) + } + else + { + // --- check if spread is within curb width + f1 = f * pow((a / W) / Sx, 1.67); + Tw = pow(Q / f1, 0.375); //HEC-22 Eq(4-2) + if (Tw <= W) Ts1 = Tw; + else + { + // --- spread extends beyond curb width + Sr = (Sx + a / W) / Sx; + iter = 1; + Ts1 = pow(Q / f, 0.375) - W; + if (Ts1 <= 0) Ts1 = Tw - W; + while (iter < 11) + { + Eo = getEo(Sr, Ts1, W); + Qs = (1.0 - Eo) * Q; //HEC-22 Eq(4-6) + Ts2 = pow(Qs / f, 0.375); //HEC-22 Eq(4-2) + if (fabs(Ts2 - Ts1) < 0.01) break; + Ts1 = Ts2; + iter++; + } + Ts1 = Ts2 + W; + } + } + return MIN(Ts1, Tcrown); +} + +//============================================================================= + +double getEo(double Sr, double Ts, double w) +// +// Input: Sr = ratio of gutter slope to street cross slope +// Ts = amount of flow spread outside of gutter width (ft) +// w = gutter width (ft) +// Output: returns ratio of gutter flow to total flow in street cross section +// Purpose: solves HEC-22 Eq. (4-4) for Eo with Ts/w substituted for +// (T/w) - 1 where Ts = T - w. +// +{ + double x; + x = Sr / (Ts / w); + x = pow((1.0 + x), 2.67) - 1.0; + x = 1.0 + Sr / x; + return 1.0 / x; +} + +//============================================================================= + +double getOnGradeCapturedFlow(TInlet* inlet, double q, double d) +// +// Input: inlet = an inlet object placed in a conduit link +// q = flow in link prior to any inlet capture (cfs) +// d = flow depth seen by inlet (ft) +// Output: returns flow captured by the inlet (cfs) +// Purpose: computes flow captured by an inlet placed on-grade. +// +// An inlet object placed in a conduit can have multiple inlets of +// the same type distributed along the conduit's length that all +// send their captured flow to the same sewer node. This function +// finds the total captured flow as each individual inlet is analyzed +// sequentially, where its approach flow has been reduced by the +// amount of flow captured by prior inlets. +{ + int i, + linkIndex; // index of link containing inlets + double qApproach, // single inlet's approach flow (cfs) + qc, // single inlet's captured flow (cfs) + qCaptured, // total flow captured by link's inlets (cfs) + qBypassed, // total flow bypassed by link's inlets (cfs) + qMax; // max. flow that a single inlet can capture (cfs) + + if (inlet->numInlets == 0) return 0.0; + linkIndex = inlet->linkIndex; + + // --- check that link has flow + qApproach = q; + if (qApproach < MIN_RUNOFF_FLOW) return 0.0; + + // --- store conduit geometry in shared variables + getConduitGeometry(inlet); + + // --- adjust flow for 2-sided street + qApproach /= Nsides; + qBypassed = qApproach; + qCaptured = 0.0; + + // --- set limit on max. flow captured per inlet + qMax = BIG; + if (inlet->flowLimit > 0.0) qMax = inlet->flowLimit; + + // --- evaluate each inlet + for (i = 1; i <= inlet->numInlets; i++) + { + qc = getOnGradeInletCapture(inlet->designIndex, qBypassed, d) * + inlet->clogFactor; + qc = MIN(qc, qMax); + qc = MIN(qc, qBypassed); + qCaptured += qc; + qBypassed -= qc; + if (qBypassed < MIN_RUNOFF_FLOW) break; + } + return qCaptured *= Nsides; +} + +//============================================================================= + +double getOnGradeInletCapture(int i, double Q, double d) +// +// Input: i = an InletDesigns index +// Q = flow rate seen by inlet (cfs) +// d = flow depth seen by inlet (ft) +// Output: returns captured flow rate (cfs) +// Purpose: finds the flow captured by a single on-grade inlet. +// +{ + double Q1 = Q, Qc = 0.0, Lsweep = 0.0, Lcurb = 0.0, Lgrate = 0.0; + + // --- drop curb inlet (in non-Street conduit) only operates in on sag mode + if (InletDesigns[i].type == DROP_CURB_INLET) + { + Qc = getOnSagInletCapture(i, d); + return MIN(Qc, Q); + } + + // --- drop grate inlet (in non-Street conduit) + if (InletDesigns[i].type == DROP_GRATE_INLET) + { + Qc = getGrateInletCapture(i, Q); + return MIN(Qc, Q); + } + + // --- Remaining inlet types apply to Street conduits + + // --- find flow spread + T = getFlowSpread(Q); + + // --- slotted inlet (behaves as a curb opening inlet per HEC-22) + if (InletDesigns[i].type == SLOTTED_INLET) + { + Qc = getCurbInletCapture(Q, InletDesigns[i].slottedInlet.length); + return MIN(Qc, Q); + } + + Lcurb = InletDesigns[i].curbInlet.length; + Lgrate = InletDesigns[i].grateInlet.length; + + // --- curb opening inlet + if (Lcurb > 0.0) + { + Lsweep = Lcurb - Lgrate; + if (Lsweep > 0.0) + { + Qc = getCurbInletCapture(Q1, Lsweep); + Q1 -= Qc; + } + } + + // --- grate inlet + if (Lgrate > 0.0 && Q1 > 0.0) + { + if (Q1 != Q) T = getFlowSpread(Q1); + Qc += getGrateInletCapture(i, Q1); + } + return Qc; +} + +//============================================================================= + +double getGrateInletCapture(int i, double Q) +// +// Input: i = inlet type index +// Q = flow rate seen by inlet (cfs) +// Output: returns captured flow rate (cfs) +// Purpose: finds the flow captured by an on-grade grate inlet. +// +{ + int grateType; + double Lg, // grate length (ft) + Wg, // grate width (ft) + A, // total cross section flow area (ft2) + Y, // flow depth (ft) + Eo, // ratio of gutter to total flow + V, // flow velocity (ft/s) + Vo, // splash-over velocity (ft/s) + Qo = Q, // flow over street area (cfs) + Rf = 1.0, // ratio of intercepted to total frontal flow + Rs = 0.0; // ratio of intercepted to total side flow + +// xsect, a, W, & Sx were from getConduitGeometry(). T was from getFlowSpread(). + + Lg = InletDesigns[i].grateInlet.length; + Wg = InletDesigns[i].grateInlet.width; + + // --- flow ratio for drop inlet + if (xsect->type == TRAPEZOIDAL || xsect->type == RECT_OPEN) + { + A = xsect_getAofS(xsect, Q / Beta); + Y = xsect_getYofA(xsect, A); + T = xsect_getWofY(xsect, Y); + Eo = Beta * pow(Y*Wg, 1.67) / pow(Wg + 2*Y, 0.67) / Q; + if (Wg > 0.99*xsect->yBot && xsect->type == TRAPEZOIDAL && xsect->sBot > 0.0) + { + Wg = xsect->yBot; + Sx = 1.0 / xsect->sBot; + } + } + + // --- flow ratio & area for conventional street gutter + else if (a == 0.0) + { + A = T * T * Sx / 2.0; + Eo = getGutterFlowRatio(Wg); // flow ratio based on grate width + if (T >= Tcrown) Qo = Qfactor * pow(Tcrown, 2.67); + } + + // --- flow ratio & area for composite street gutter + else + { + // --- spread confined to gutter + if (T <= W) A = T * T * Sw / 2.0; + + // --- spread beyond gutter width + else A = (T * T * Sx + a * W) / 2.0; + + // flow ratio based on gutter width corrected for grate width + Eo = getGutterFlowRatio(W); + if (Eo < 1.0) + { + if (T >= Tcrown) + Qo = Qfactor * pow(Tcrown, 2.67) / (1.0 - Eo); + Eo = Eo * getGutterAreaRatio(Wg, A); //HEC-22 Eq(4-20a) + } + } + + // --- flow and splash-over velocities + V = Qo / A; + grateType = InletDesigns[i].grateInlet.type; + if (grateType < 0 || grateType == GENERIC) + Vo = InletDesigns[i].grateInlet.splashVeloc; + else + Vo = getSplashOverVelocity(grateType, Lg); + + // --- frontal flow capture efficiency + if (V > Vo) Rf = 1.0 - 0.09 * (V - Vo); //HEC-22 Eq(4-18) + + // --- side flow capture efficiency + if (Eo < 1.0) + { + Rs = 1.0 / (1.0 + (0.15 * pow(V, 1.8) / + Sx / pow(Lg, 2.3))); //HEC-22 Eq(4-19) + } + + // --- return total flow captured + return Q * (Rf * Eo + Rs * (1.0 - Eo)); //HEC-22 Eq(4-21) +} + +//============================================================================= + +double getCurbInletCapture(double Q, double L) +// +// Input: Q = flow rate seen by inlet (cfs) +// L = length of inlet opening (ft) +// Output: returns captured flow rate (cfs) +// Purpose: finds the flow captured by an on-sag inlet. +// +{ + double Se = Sx, // equivalent gutter slope + Lt, // length for full capture + Sr, // ratio of gutter slope to cross slope + Eo = 0.0, // ratio of gutter to total flow + E = 1.0; // capture efficiency + +// a, W, Sx, Sw, SL, & n were from getConduitGeometry(). T was from getFlowSpread(). + + // --- for depressed gutter section + if (a > 0.0) + { + Sr = Sw / Sx; + Eo = getEo(Sr, T-W, W); + Se = Sx + Sw * Eo; //HEC-22 Eq(4-24) + } + + // --- opening length for full capture + Lt = 0.6 * pow(Q, 0.42) * pow(SL, 0.3) * + pow(1.0/(n*Se), 0.6); //HEC-22 Eq(4-22a) + + // --- capture efficiency for actual opening length + if (L < Lt) + { + E = 1.0 - (L/Lt); + E = 1 - pow(E, 1.8); //HEC-22 Eq(4-23) + } + E = MIN(E, 1.0); + E = MAX(E, 0.0); + return E * Q; +} + +//============================================================================= + +double getGutterFlowRatio(double w) +// +// Input: w = gutter width (ft) +// Output: returns a flow ratio +// Purpose: computes the ratio of flow over a width of gutter to the total +// flow in a street cross section. +// +{ + if (T <= w) return 1.0; + else if (a > 0.0) + return getEo(Sw / Sx, T - w, w); + else + return 1.0 - pow((1.0 - w / T), 2.67); //HEC-22 Eq(4-16) +} + +//============================================================================= + +double getGutterAreaRatio(double Wg, double A) +// +// Input: Wg = width of grate inlet (ft) +// A = total flow area (ft2) +// Output: returns an area ratio +// Purpose: computes the ratio of the flow area above a grate to the flow +// area above depressed gutter in a street cross section. +// +{ + double As, // flow area beyond gutter width (ft2) + Ag; // flow area over grate width (ft2) + + if (Wg >= W) return 1.0; + if (T <= Wg) return 1.0; + if (T <= W) return Wg / T; + As = 0.5 * SQR((T - W)) * Sx; + Ag = Wg * ( (T * Sx) + a - (Wg * Sw / 2.) ); + return Ag / (A - As); +} + +//============================================================================= + +double getSplashOverVelocity(int grateType, double L) +// +// Input: grateType = grate inlet type code +// L = length of grate inlet (ft) +// Output: returns a splash over velocity +// Purpose: computes the splash over velocity for a standard type of grate +// inlet as a function of its length. +// +{ + return SplashCoeffs[grateType][0] + + SplashCoeffs[grateType][1] * L - + SplashCoeffs[grateType][2] * L * L + + SplashCoeffs[grateType][3] * L * L * L; +} + +//============================================================================= + +double getOnSagCapturedFlow(TInlet* inlet, double q, double d) +// +// Input: inlet = an inlet object placed in a conduit link +// q = flow in link prior to any inlet capture (cfs) +// d = flow depth seen by inlet (ft) +// Output: returns flow captured by the inlet (cfs) +// Purpose: computes flow captured by an inlet placed on-sag. +// +{ + int linkIndex, designIndex, totalInlets; + double qCaptured = 0.0, qMax = HUGE; + + if (inlet->numInlets == 0) return 0.0; + totalInlets = Nsides * inlet->numInlets; + linkIndex = inlet->linkIndex; + designIndex = inlet->designIndex; + + // --- store conduit geometry in shared variables + getConduitGeometry(inlet); + + // --- set flow limit per inlet + if (inlet->flowLimit > 0.0) + qMax = inlet->flowLimit; + + // --- find nominal flow captured by inlet + qCaptured = getOnSagInletCapture(designIndex, fabs(d)); + + // --- find actual flow captured by the inlet + qCaptured *= inlet->clogFactor; + qCaptured = MIN(qCaptured, qMax); + qCaptured *= (double)totalInlets; + return qCaptured; +} + +//============================================================================= + +double getOnSagInletCapture(int i, double d) +// +// Input: i = inlet type index +// d = water level seen by inlet (ft) +// Output: returns captured flow rate (cfs) +// Purpose: finds the flow captured by an on-sag inlet. +// +{ + double Lsweep = 0.0, Lcurb = 0.0, Lgrate = 0.0; + double Qsw = 0.0, //Sweeper curb opening weir flow + Qso = 0.0, //Sweeper curb opening orifice flow + Qgw = 0.0, //Grate weir flow + Qgo = 0.0, //Grate orifice flow + Qcw = 0.0, //Curb opening weir flow + Qco = 0.0; //Curb opening orifice flow + + if (InletDesigns[i].slottedInlet.length > 0.0) + return getOnSagSlottedFlow(i, d); + + Lgrate = InletDesigns[i].grateInlet.length; + if (Lgrate > 0.0) findOnSagGrateFlows(i, d, &Qgw, &Qgo); + + Lcurb = InletDesigns[i].curbInlet.length; + if (Lcurb > 0.0) + { + Lsweep = Lcurb - Lgrate; + if (Lsweep > 0.0) findOnSagCurbFlows(i, d, Lsweep, &Qsw, &Qso); + if (Qgo > 0.0) findOnSagCurbFlows(i, d, Lgrate, &Qcw, &Qco); + } + return Qgw + Qgo + Qsw + Qso + Qco; +} + +//============================================================================= + +void findOnSagGrateFlows(int i, double d, double *Qw, double *Qo) +// +// Input: i = inlet type index +// d = water level seen by inlet (ft) +// Output: Qw = flow captured in weir mode (cfs) +// Qo = flow captured in orifice mode (cfs) +// Purpose: finds the flow captured by an on-sag grate inlet. +// +{ + int grateType = InletDesigns[i].grateInlet.type; + double Lg = InletDesigns[i].grateInlet.length; + double Wg = InletDesigns[i].grateInlet.width; + double P, // grate perimeter (ft) + Ao, // grate opening area (ft2) + di; // average flow depth across grate (ft) + + // --- for drop grate inlets + if (InletDesigns[i].type == DROP_GRATE_INLET) + { + di = d; + P = 2.0 * (Lg + Wg); + } + + // --- for gutter grate inlets: + else + { + // --- check for spread within grate width + if (d <= Wg * Sw) + Wg = d / Sw; + + // --- avergage depth over grate + di = d - (Wg / 2.0) * Sw; + + // --- effective grate perimeter + P = Lg + 2.0 * Wg; + } + + if (grateType == GENERIC) + Ao = Lg * Wg * InletDesigns[i].grateInlet.fracOpenArea; + else + Ao = Lg * Wg * GrateOpeningRatios[grateType]; + + // --- weir flow applies (based on depth where result of + // weir eqn. equals result of orifice eqn.) + + if (d <= 1.79 * Ao / P) + { + *Qw = 3.0 * P * pow(di, 1.5); //HEC-22 Eq(4-26) + } + + // --- orifice flow applies + else + { + *Qo = 0.67 * Ao * sqrt(2.0 * GRAVITY * di); //HEC-22 Eq(4-27) + } +} + +//============================================================================= + +void findOnSagCurbFlows(int i, double d, double L, double *Qw, double *Qo) +// +// Input: i = inlet type index +// d = water level seen by inlet (ft) +// L = length of curb opening (ft) +// Output: Qw = flow captured in weir mode (cfs) +// Qo = flow captured in orifice mode (cfs) +// Purpose: finds the flow captured by an on-sag curb opening inlet. +// +{ + int throatAngle = InletDesigns[i].curbInlet.throatAngle; + double h = InletDesigns[i].curbInlet.height; + double Qweir, Qorif, P; + double dweir, dorif, r; + + // --- check for orifice flow + if (L <= 0.0) return; + if (InletDesigns[i].type == DROP_CURB_INLET) L = L * 4.0; + dorif = 1.4 * h; + if (d > dorif) + { + *Qo = getCurbOrificeFlow(d, h, L, throatAngle); + return; + } + + // --- for uniform cross slope or very long opening + if (a == 0.0 || L > 12.0) + { + // --- check for weir flow + dweir = h; + if (d < dweir) + { + *Qw = 3.0 * L * pow(d, 1.5); //HEC-22 Eq(4-30) + return; + } + else Qweir = 3.0 * L * pow(dweir, 1.5); + } + + // --- for depressed gutter + else + { + // --- check for weir flow + P = L + 1.8 * W; + dweir = h + a; + if (d < dweir) + { + *Qw = 2.3 * P * pow(d, 1.5); //HEC-22 Eq(4-28) + return; + } + else Qweir = 2.3 * P * pow(dweir, 1.5); + } + + // --- interpolate between Qweir at depth dweir and Qorif at depth dorif + Qorif = getCurbOrificeFlow(dorif, h, L, throatAngle); + r = (d - dweir) / (dorif - dweir); + *Qw = (1.0 -r) * Qweir; + *Qo = r * Qorif; +} + +//============================================================================= + +double getCurbOrificeFlow(double di, double h, double L, int throatAngle) +// +// Input: di = water level at lip of inlet opening (ft) +// h = height of curb opening (ft) +// L = length of curb opening (ft) +// throatAngle = type of throat angle in curb opening +// Output: return flow captured by inlet (cfs) +// Purpose: finds the flow captured by an on-sag curb opening inlet under +// orifice flow conditions. +// +{ + double d = di; + if (throatAngle == HORIZONTAL_THROAT) + d = di - h / 2.0; + else if (throatAngle == INCLINED_THROAT) + d = di + (h / 2.0) * 0.7071; + return 0.67 * h * L * sqrt(2.0 * GRAVITY * d); //HEC-22 Eq(4-31a) +} + +//============================================================================= + +double getOnSagSlottedFlow(int i, double d) +// +// Input: i = inlet type index +// d = water level seen by inlet (ft) +// Output: returns captured flow rate (cfs) +// Purpose: finds the flow captured by an on-sag slotted inlet. +// +// Note: weir flow = orifice flow at d = 2.587 * inlet width +{ + double L = InletDesigns[i].slottedInlet.length; + double w = InletDesigns[i].slottedInlet.width; + + if (d <= 2.587 * w) + return 2.48 * L * pow(d, 1.5); //HEC-22 Eq(4-32) + else + return 0.8 * L * w * sqrt(64.32 * d); //HEC-22 Eq(4-33) +} + +//============================================================================= + +void getBackflowRatios() +// +// Input: none +// Output: overflow ratio for each inlet +// Purpose: finds the fraction of the overflow produced by an inlet's capture +// node that becomes backflow into the inlet. +// +// Note: when a capture node receives flow from two or more inlets +// its backflow is divided among the inlets based on: +// i) the fraction of total open area for standard inlets +// ii) the fraction of total number of inlets for custom inlets +{ + TInlet* inlet; + double area; + double f; + int n; + + // --- info for each node receiving flow from an inlet + typedef struct + { + int numInletLinks; // total # inlet links + int numStdInletLinks; // total # standard inlet links + int numCustomInlets; // # custom inlets + double totalInletArea; // open area of standard inlets + } TInletNode; + TInletNode* inletNodes = (TInletNode *) calloc(Nobjects[NODE], sizeof(TInletNode)); + if (inletNodes == NULL) return; + + // --- Finds each inlet's contribution to its capture node + for (inlet = FirstInlet; inlet != NULL; inlet = inlet->nextInlet) + { + n = inlet->nodeIndex; + inletNodes[n].numInletLinks++; + area = getInletArea(inlet); + if (area > 0.0) + { + inletNodes[n].numStdInletLinks++; + inletNodes[n].totalInletArea += area; + } + else + inletNodes[n].numCustomInlets += inlet->numInlets; + } + + // --- find fraction of capture node's overflow that becomes inlet backflow + for (inlet = FirstInlet; inlet != NULL; inlet = inlet->nextInlet) + { + // --- f is ratio of links with standard inlets to all inlet links + // connected to receptor node n + n = inlet->nodeIndex; + f = (double) inletNodes[n].numStdInletLinks / + (double) inletNodes[n].numInletLinks; + + // --- backflow ratio depends if inlet is standard or custom (area = 0) + area = getInletArea(inlet); + if (area == 0.0) + inlet->backflowRatio = (double)inlet->numInlets / + (double)inletNodes[n].numCustomInlets * (1. - f); + else + inlet->backflowRatio = area / inletNodes[n].totalInletArea * f; + } + free(inletNodes); +} + +//============================================================================= + +double getInletArea(TInlet* inlet) +// +// Input: inlet = an inlet object placed in a conduit link +// Output: returns the unclogged open area of the inlet (ft2) +// Purpose: finds the total open flow area inlets placed in a conduit. +// +{ + double area = 0.0; + double curbLength; + int i = inlet->designIndex; + int grateType = InletDesigns[i].grateInlet.type; + + if (InletDesigns[i].grateInlet.length > 0.0) + { + area = InletDesigns[i].grateInlet.length * InletDesigns[i].grateInlet.width; + if (grateType == GENERIC) + area *= InletDesigns[i].grateInlet.fracOpenArea; + else + area *= GrateOpeningRatios[grateType]; + } + + curbLength = InletDesigns[i].curbInlet.length - InletDesigns[i].grateInlet.length; + if (curbLength > 0.0) + area += curbLength * InletDesigns[i].curbInlet.height; + + if (InletDesigns[i].slottedInlet.length > 0.0) + area = InletDesigns[i].slottedInlet.length * InletDesigns[i].slottedInlet.width; + return area * inlet->numInlets * inlet->clogFactor; +} + +//============================================================================= + +double getCustomCapturedFlow(TInlet* inlet, double q, double d) +{ + int i = inlet->designIndex; // inlet's position in InletDesigns array + int j; // counter for replicate inlets + int sides = 1; // number of sides for inlet's street (1 or 2) + int c; // an index into the Curve array + double qApproach, // inlet's approach flow (cfs) + qBypassed, // inlet's bypassed flow (cfs) + qCaptured, // inlet's captured flow (cfs) + qIncrement, // increment to captured flow (cfs) + qMax = HUGE; // user-supplied flow capture limit (cfs) + + if (inlet->numInlets == 0) return 0.0; + + // --- set limit on max. flow captured per inlet + qMax = BIG; + if (inlet->flowLimit > 0.0) qMax = inlet->flowLimit; + + // --- get number of sides to a street xsection + xsect = &Link[inlet->linkIndex].xsect; + if (xsect->type == STREET_XSECT) + sides = Street[xsect->transect].sides; + + // --- adjust flow for 2-sided street + qApproach = q / sides; + qBypassed = qApproach; + qCaptured = 0.0; + + // --- get index of inlet's capture curve + c = InletDesigns[i].customCurve; + if (c >= 0) + { + // --- curve is captured flow v. approach flow + if (Curve[c].curveType == DIVERSION_CURVE) + { + // --- add up incrmental capture of each replicate inlet + for (j = 1; j <= inlet->numInlets; j++) + { + qIncrement = inlet->clogFactor * + table_lookupEx(&Curve[c], qBypassed * UCF(FLOW)) / UCF(FLOW); + qIncrement = MIN(qIncrement, qMax); + qIncrement = MIN(qIncrement, qBypassed); + qCaptured += qIncrement; + qBypassed -= qIncrement; + if (qBypassed < MIN_RUNOFF_FLOW) break; + } + } + + // --- curve is captured flow v. downstream node depth + else if (Curve[c].curveType == RATING_CURVE) + { + qCaptured = inlet->numInlets * inlet->clogFactor * + table_lookupEx(&Curve[c], d * UCF(LENGTH)) / UCF(FLOW); + } + qCaptured *= sides; + } + return qCaptured; +} diff --git a/swmm/swmm5/swmm5/inlet.h b/swmm/swmm5/swmm5/inlet.h new file mode 100644 index 0000000..ca78307 --- /dev/null +++ b/swmm/swmm5/swmm5/inlet.h @@ -0,0 +1,30 @@ +//----------------------------------------------------------------------------- +// inlet.h +// +// Project: EPA SWMM5 +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) +// Author: L. Rossman +// +// Street/Channel Inlet Functions +// +//----------------------------------------------------------------------------- +#ifndef INLET_H +#define INLET_H + +typedef struct TInlet TInlet; + +int inlet_create(int nInlets); +void inlet_delete(); +int inlet_readDesignParams(char* tok[], int ntoks); +int inlet_readUsageParams(char* tok[], int ntoks); +void inlet_validate(); + +void inlet_findCapturedFlows(double tStep); +void inlet_adjustQualInflows(); +void inlet_adjustQualOutflows(); + +void inlet_writeStatsReport(); +double inlet_capturedFlow(int link); + +#endif diff --git a/swmm/swmm5/swmm5/input.c b/swmm/swmm5/swmm5/input.c index 390b6f0..3613297 100644 --- a/swmm/swmm5/swmm5/input.c +++ b/swmm/swmm5/swmm5/input.c @@ -2,29 +2,28 @@ // input.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/14 (Build 5.1.001) -// 09/15/14 (Build 5.1.007) -// 08/01/16 (Build 5.1.011) -// 04/01/20 (Build 5.1.015) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) // Author: L. Rossman // // Input data processing functions. // +// Update History +// ============== // Build 5.1.007: // - Support added for climate adjustment input data. -// // Build 5.1.011: // - Support added for reading hydraulic event dates. -// // Build 5.1.015: // - Support added for multiple infiltration methods within a project. +// Build 5.2.0: +// - Support added for Streets and Inlets. +// - Support added for named variables & math expressions in control rules. //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE #include #include -#include #include #include "headers.h" #include "lid.h" @@ -87,14 +86,15 @@ int input_countObjects() for (i = 0; i < MAX_OBJ_TYPES; i++) Nobjects[i] = 0; for (i = 0; i < MAX_NODE_TYPES; i++) Nnodes[i] = 0; for (i = 0; i < MAX_LINK_TYPES; i++) Nlinks[i] = 0; + controls_init(); // --- make pass through data file counting number of each object while ( fgets(line, MAXLINE, Finp.file) != NULL ) { // --- skip blank lines & those beginning with a comment lineCount++; - strcpy(wLine, line); // make working copy of line - tok = strtok(wLine, SEPSTR); // get first text token on line + sstrncpy(wLine, line, MAXLINE); // make working copy of line + tok = strtok(wLine, SEPSTR); // get first text token on line if ( tok == NULL ) continue; if ( *tok == ';' ) continue; @@ -176,7 +176,7 @@ int input_readData() { // --- make copy of line and scan for tokens lineCount++; - strcpy(wLine, line); + sstrncpy(wLine, line, MAXLINE); Ntokens = getTokens(wLine); // --- skip blank lines and comments @@ -184,12 +184,12 @@ int input_readData() if ( *Tok[0] == ';' ) continue; // --- check if max. line length exceeded - lineLength = strlen(line); + lineLength = (int)strlen(line); if ( lineLength >= MAXLINE ) { // --- don't count comment if present comment = strchr(line, ';'); - if ( comment ) lineLength = comment - line; // Pointer math here + if ( comment ) lineLength = (int)(comment - line); // Pointer math here if ( lineLength >= MAXLINE ) { inperr = ERR_LINE_LENGTH; @@ -407,6 +407,7 @@ int addObject(int objType, char* id) case s_CONTROL: if ( match(id, w_RULE) ) Nobjects[CONTROL]++; + else controls_addToCount(id); break; case s_TRANSECT: @@ -436,6 +437,22 @@ int addObject(int objType, char* id) break; case s_EVENT: NumEvents++; break; + + case s_STREET: + if ( !project_addObject(STREET, id, Nobjects[STREET]) ) + errcode = error_setInpError(ERR_DUP_NAME, id); + Nobjects[STREET]++; + break; + + case s_INLET: + // --- an INLET object can span several lines + if (project_findObject(INLET, id) < 0) + { + if ( !project_addObject(INLET, id, Nobjects[INLET]) ) + errcode = error_setInpError(ERR_DUP_NAME, id); + Nobjects[INLET]++; + } + break; } return errcode; } @@ -481,7 +498,7 @@ int parseLine(int sect, char *line) return subcatch_readSubareaParams(Tok, Ntokens); case s_INFIL: - return infil_readParams(InfilModel, Tok, Ntokens); //(5.1.015) + return infil_readParams(InfilModel, Tok, Ntokens); case s_AQUIFER: j = Mobjects[AQUIFER]; @@ -600,6 +617,15 @@ int parseLine(int sect, char *line) case s_EVENT: return readEvent(Tok, Ntokens); + case s_STREET: + return street_readParams(Tok, Ntokens); + + case s_INLET: + return inlet_readDesignParams(Tok, Ntokens); + + case s_INLET_USAGE: + return inlet_readUsageParams(Tok, Ntokens); + default: return 0; } } @@ -620,6 +646,11 @@ int readControl(char* tok[], int ntoks) // --- check for minimum number of tokens if ( ntoks < 2 ) return error_setInpError(ERR_ITEMS, ""); + if (match(tok[0], w_VARIABLE)) + return controls_addVariable(tok, ntoks); + if (match(tok[0], w_EXPRESSION)) + return controls_addExpression(tok, ntoks); + // --- get index of control rule keyword keyword = findmatch(tok[0], RuleKeyWords); if ( keyword < 0 ) return error_setInpError(ERR_KEYWORD, tok[0]); @@ -672,7 +703,7 @@ int readTitle(char* line) if ( strlen(Title[i]) == 0 ) { // --- strip line feed character from input line - n = strlen(line); + n = (int)strlen(line); if (line[n-1] == 10) line[n-1] = ' '; // --- copy input line into Title entry @@ -771,19 +802,19 @@ int match(char *str, char *substr) // (not case sensitive). // { - int i,j; + int i,j,k; // --- fail if substring is empty if (!substr[0]) return(0); // --- skip leading blanks of str - for (i = 0; str[i]; i++) + for (k = 0; str[k]; k++) { - if (str[i] != ' ') break; + if (str[k] != ' ') break; } // --- check if substr matches remainder of str - for (i = i,j = 0; substr[j]; i++,j++) + for (i = k,j = 0; substr[j]; i++,j++) { if (!str[i] || UCHAR(str[i]) != UCHAR(substr[j])) return(0); } @@ -858,7 +889,8 @@ int getTokens(char *s) // in CONSTS.H. Text between quotes is treated as a single token. // { - int len, m, n; + int n; + size_t len, m; char *c; // --- begin with no tokens @@ -868,7 +900,7 @@ int getTokens(char *s) // --- truncate s at start of comment c = strchr(s,';'); if (c) *c = '\0'; - len = strlen(s); + len = (int)strlen(s); // --- scan s for tokens until nothing left while (len > 0 && n < MAXTOKS) @@ -890,7 +922,7 @@ int getTokens(char *s) } len -= m+1; // update length of s } - return(n); + return n; } //============================================================================= diff --git a/swmm/swmm5/swmm5/inputrpt.c b/swmm/swmm5/swmm5/inputrpt.c index 31798c9..46a6f95 100644 --- a/swmm/swmm5/swmm5/inputrpt.c +++ b/swmm/swmm5/swmm5/inputrpt.c @@ -2,12 +2,16 @@ // inputrpt.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/14 (Build 5.1.001) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) // Author: L. Rossman // // Report writing functions for input data summary. // +// Update History +// ============== +// Build 5.2.0: +// - Support added for reporting Street geometry tables. //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE @@ -237,6 +241,9 @@ void inputrpt_writeInput() else if ( Link[i].xsect.type == IRREGULAR ) fprintf(Frpt.file, "%-16s ", Transect[Link[i].xsect.transect].ID); + else if ( Link[i].xsect.type == STREET_XSECT ) + fprintf(Frpt.file, "%-16s ", + Street[Link[i].xsect.transect].ID); else fprintf(Frpt.file, "%-16s ", XsectTypeWords[Link[i].xsect.type]); fprintf(Frpt.file, "%8.2f %8.2f %8.2f %8.2f %3d %8.2f", @@ -281,7 +288,6 @@ void inputrpt_writeInput() } } } - WRITE(""); if (Nobjects[TRANSECT] > 0) { @@ -313,5 +319,36 @@ void inputrpt_writeInput() } } } + + if (Nobjects[STREET] > 0) + { + WRITE(""); + WRITE(""); + WRITE("**************"); + WRITE("Street Summary"); + WRITE("**************"); + for (i = 0; i < Nobjects[STREET]; i++) + { + fprintf(Frpt.file, "\n\n Street %s", Street[i].ID); + fprintf(Frpt.file, "\n Area: "); + for (m = 1; m < Street[i].transect.nTbl; m++) + { + if (m % 5 == 1) fprintf(Frpt.file, "\n "); + fprintf(Frpt.file, "%10.4f ", Street[i].transect.areaTbl[m]); + } + fprintf(Frpt.file, "\n Hrad: "); + for (m = 1; m < Street[i].transect.nTbl; m++) + { + if (m % 5 == 1) fprintf(Frpt.file, "\n "); + fprintf(Frpt.file, "%10.4f ", Street[i].transect.hradTbl[m]); + } + fprintf(Frpt.file, "\n Width: "); + for (m = 1; m < Street[i].transect.nTbl; m++) + { + if (m % 5 == 1) fprintf(Frpt.file, "\n "); + fprintf(Frpt.file, "%10.4f ", Street[i].transect.widthTbl[m]); + } + } + } WRITE(""); } diff --git a/swmm/swmm5/swmm5/keywords.c b/swmm/swmm5/swmm5/keywords.c index 7ae5b2d..17973d1 100644 --- a/swmm/swmm5/swmm5/keywords.c +++ b/swmm/swmm5/swmm5/keywords.c @@ -2,14 +2,8 @@ // keywords.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/14 (Build 5.1.000) -// 04/14/14 (Build 5.1.004) -// 09/15/14 (Build 5.1.007) -// 03/19/15 (Build 5.1.008) -// 08/05/15 (Build 5.1.010) -// 08/01/16 (Build 5.1.011) -// 05/10/18 (Build 5.1.013) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) // Author: L. Rossman // // Exportable keyword dictionary @@ -19,24 +13,27 @@ // must be terminated by NULL. The actual text of each keyword // is defined in text.h. // +// Update History +// ============== // Build 5.1.007: // - Keywords for Ignore RDII option and groundwater flow equation // and climate adjustment input sections added. -// // Build 5.1.008: // - Keyword arrays placed in alphabetical order for better readability. // - Keywords added for Minimum Routing Step and Number of Threads options. -// // Build 5.1.010: // - New Modified Green Ampt keyword added to InfilModelWords. // - New Roadway weir keyword added to WeirTypeWords. -// // Build 5.1.011: // - New section keyword for [EVENTS] added. -// // Build 5.1.013: // - New option keywords w_SURCHARGE_METHOD, w_RULE_STEP, w_AVERAGES // and w_WEIR added. +// Build 5.2.0: +// - Support added for Streets and Inlets. +// - Support added for variable speed pumps. +// - Support added for analytical storage shapes. +// - Support added for RptFlags.disabled option. //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE @@ -45,8 +42,9 @@ char* BuildupTypeWords[] = { w_NONE, w_POW, w_EXP, w_SAT, w_EXT, NULL}; char* CurveTypeWords[] = { w_STORAGE, w_DIVERSION, w_TIDAL, w_RATING, - w_CONTROLS, w_SHAPE, w_WEIR, //(5.1.013) - w_PUMP1, w_PUMP2, w_PUMP3, w_PUMP4, NULL}; + w_CONTROLS, w_SHAPE, w_WEIR, + w_PUMP1, w_PUMP2, w_PUMP3, w_PUMP4, + w_PUMP5, NULL}; char* DividerTypeWords[] = { w_CUTOFF, w_TABULAR, w_WEIR, w_OVERFLOW, NULL}; char* EvapTypeWords[] = { w_CONSTANT, w_MONTHLY, w_TIMESERIES, w_TEMPERATURE, w_FILE, w_RECOVERY, @@ -79,7 +77,7 @@ char* OptionWords[] = { w_FLOW_UNITS, w_INFIL_MODEL, w_REPORT_START_TIME, w_SWEEP_START, w_SWEEP_END, w_START_DRY_DAYS, w_WET_STEP, w_DRY_STEP, - w_ROUTE_STEP, w_RULE_STEP, //(5.1.013) + w_ROUTE_STEP, w_RULE_STEP, w_REPORT_STEP, w_ALLOW_PONDING, w_INERT_DAMPING, w_SLOPE_WEIGHTING, w_VARIABLE_STEP, @@ -93,7 +91,7 @@ char* OptionWords[] = { w_FLOW_UNITS, w_INFIL_MODEL, w_MAX_TRIALS, w_HEAD_TOL, w_SYS_FLOW_TOL, w_LAT_FLOW_TOL, w_IGNORE_RDII, w_MIN_ROUTE_STEP, - w_NUM_THREADS, w_SURCHARGE_METHOD, //(5.1.013) + w_NUM_THREADS, w_SURCHARGE_METHOD, NULL }; char* OrificeTypeWords[] = { w_SIDE, w_BOTTOM, NULL}; char* OutfallTypeWords[] = { w_FREE, w_NORMAL, w_FIXED, w_TIDAL, @@ -101,14 +99,16 @@ char* OutfallTypeWords[] = { w_FREE, w_NORMAL, w_FIXED, w_TIDAL, char* PatternTypeWords[] = { w_MONTHLY, w_DAILY, w_HOURLY, w_WEEKEND, NULL}; char* PondingUnitsWords[] = { w_PONDED_FEET, w_PONDED_METERS }; char* ProcessVarWords[] = { w_HRT, w_DT, w_FLOW, w_DEPTH, w_AREA, NULL}; -char* PumpTypeWords[] = { w_TYPE1, w_TYPE2, w_TYPE3, w_TYPE4, w_IDEAL }; +char* PumpTypeWords[] = { w_TYPE1, w_TYPE2, w_TYPE3, w_TYPE4, w_TYPE5, w_IDEAL }; char* QualUnitsWords[] = { w_MGperL, w_UGperL, w_COUNTperL, NULL}; char* RainTypeWords[] = { w_INTENSITY, w_VOLUME, w_CUMULATIVE, NULL}; char* RainUnitsWords[] = { w_INCHES, w_MMETER, NULL}; -char* RelationWords[] = { w_TABULAR, w_FUNCTIONAL, NULL}; -char* ReportWords[] = { w_INPUT, w_CONTINUITY, w_FLOWSTATS, - w_CONTROLS, w_SUBCATCH, w_NODE, w_LINK, - w_NODESTATS, w_AVERAGES, NULL}; //(5.1.013) +char* RelationWords[] = { w_TABULAR, w_FUNCTIONAL, + w_CYLINDRICAL, w_CONICAL, w_PARABOLIC, + w_PYRAMIDAL, NULL}; +char* ReportWords[] = { w_DISABLED, w_INPUT, w_SUBCATCH, w_NODE, w_LINK, + w_CONTINUITY, w_FLOWSTATS,w_CONTROLS, + w_AVERAGES, w_NODESTATS, NULL}; char* RouteModelWords[] = { w_NONE, w_STEADY, w_KINWAVE, w_XKINWAVE, w_DYNWAVE, NULL}; char* RuleKeyWords[] = { w_RULE, w_IF, w_AND, w_OR, w_THEN, w_ELSE, @@ -140,9 +140,10 @@ char* SectWords[] = { ws_TITLE, ws_OPTION, ws_MAP, ws_LID_CONTROL, ws_LID_USAGE, ws_GWF, ws_ADJUST, ws_EVENT, - NULL}; + ws_STREET, ws_INLET_USAGE, + ws_INLET, NULL}; char* SnowmeltWords[] = { w_PLOWABLE, w_IMPERV, w_PERV, w_REMOVAL, NULL}; -char* SurchargeWords[] = { w_EXTRAN, w_SLOT, NULL}; //(5.1.013) +char* SurchargeWords[] = { w_EXTRAN, w_SLOT, NULL}; char* TempKeyWords[] = { w_TIMESERIES, w_FILE, w_WINDSPEED, w_SNOWMELT, w_ADC, NULL}; char* TransectKeyWords[] = { w_NC, w_X1, w_GR, NULL}; @@ -165,4 +166,5 @@ char* XsectTypeWords[] = { w_DUMMY, w_CIRCULAR, w_CATENARY, w_SEMIELLIPTICAL, w_BASKETHANDLE, w_SEMICIRCULAR, w_IRREGULAR, w_CUSTOM, - w_FORCE_MAIN, NULL}; + w_FORCE_MAIN, w_STREET, + NULL}; diff --git a/swmm/swmm5/swmm5/keywords.h b/swmm/swmm5/swmm5/keywords.h index af89e0d..e330c59 100644 --- a/swmm/swmm5/swmm5/keywords.h +++ b/swmm/swmm5/swmm5/keywords.h @@ -2,17 +2,16 @@ // keywords.h // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/19/14 (Build 5.1.000) -// 03/19/15 (Build 5.1.008) -// 05/10/18 (Build 5.1.013) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) // Author: L. Rossman // // Exportable keyword dictionary // +// Update History +// ============== // Build 5.1.008: // - Keyword arrays listed in alphabetical order. -// // Build 5.1.013: // - New keyword array defined for surcharge method. //----------------------------------------------------------------------------- @@ -59,7 +58,7 @@ extern char* RouteModelWords[]; extern char* RuleKeyWords[]; extern char* SectWords[]; extern char* SnowmeltWords[]; -extern char* SurchargeWords[]; //(5.1.013) +extern char* SurchargeWords[]; extern char* TempKeyWords[]; extern char* TransectKeyWords[]; extern char* TreatTypeWords[]; diff --git a/swmm/swmm5/swmm5/kinwave.c b/swmm/swmm5/swmm5/kinwave.c index c0fdf6f..b137e4c 100644 --- a/swmm/swmm5/swmm5/kinwave.c +++ b/swmm/swmm5/swmm5/kinwave.c @@ -2,21 +2,19 @@ // kinwave.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/14 (Build 5.1.001) -// 03/19/15 (Build 5.1.008) -// 03/01/20 (Build 5.1.014) -// Author: L. Rossman (EPA) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) +// Author: L. Rossman // M. Tryby (EPA) // // Kinematic wave flow routing functions. // +// Update History +// ============== // Build 5.1.008: // - Conduit inflow passed to function that computes conduit losses. -// // Build 5.1.014: // - Arguments to function link_getLossRate changed. -// //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE @@ -102,7 +100,7 @@ int kinwave_execute(int j, double* qinflow, double* qoutflow, double tStep) qin = (*qinflow) / Conduit[k].barrels / Qfull; // --- compute evaporation and infiltration loss rate - q3 = link_getLossRate(j, qin*Qfull) / Qfull; //(5.1.014) + q3 = link_getLossRate(j, qin*Qfull) / Qfull; // --- normalize previous areas a1 = Conduit[k].a1 / Afull; diff --git a/swmm/swmm5/swmm5/landuse.c b/swmm/swmm5/swmm5/landuse.c index 380605f..4c84027 100644 --- a/swmm/swmm5/swmm5/landuse.c +++ b/swmm/swmm5/swmm5/landuse.c @@ -2,19 +2,19 @@ // landuse.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/14 (Build 5.1.001) -// 03/19/15 (Build 5.1.008) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) // Author: L. Rossman // // Pollutant buildup and washoff functions. // +// Update History +// ============== // Build 5.1.008: // - landuse_getWashoffMass() re-named to landuse_getWashoffQual() and // modified to return concentration instead of mass load. // - landuse_getRunoffLoad() re-named to landuse_getWashoffLoad() and // modified to work with landuse_getWashoffQual(). -// //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE @@ -41,8 +41,6 @@ //----------------------------------------------------------------------------- static double landuse_getBuildupDays(int landuse, int pollut, double buildup); static double landuse_getBuildupMass(int landuse, int pollut, double days); -static double landuse_getRunoffLoad(int landuse, int pollut, double area, - TLandFactor landFactor[], double runoff, double tStep); static double landuse_getWashoffQual(int landuse, int pollut, double buildup, double runoff, double area); static double landuse_getExternalBuildup(int i, int p, double buildup, @@ -201,7 +199,7 @@ int landuse_readBuildupParams(char* tok[], int ntoks) // { int i, j, k, n, p; - double c[3], tmax; + double c[3] = {0, 0, 0}, tmax; if ( ntoks < 3 ) return 0; j = project_findObject(LANDUSE, tok[0]); diff --git a/swmm/swmm5/swmm5/lid.c b/swmm/swmm5/swmm5/lid.c index 57dd9c6..7bb69dc 100644 --- a/swmm/swmm5/swmm5/lid.c +++ b/swmm/swmm5/swmm5/lid.c @@ -2,19 +2,9 @@ // lid.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/14 (Build 5.1.001) -// 05/19/14 (Build 5.1.006) -// 09/15/14 (Build 5.1.007) -// 03/19/15 (Build 5.1.008) -// 04/30/15 (Build 5.1.009) -// 08/05/15 (Build 5.1.010) -// 08/01/16 (Build 5.1.011) -// 03/14/17 (Build 5.1.012) -// 05/10/18 (Build 5.1.013) -// 03/01/20 (Build 5.1.014) -// 04/01/20 (Build 5.1.015) -// Author: L. Rossman (US EPA) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) +// Author: L. Rossman // // This module handles all data processing involving LID (Low Impact // Development) practices used to treat runoff for individual subcatchments @@ -43,44 +33,41 @@ // levels for a specific LID unit to be written to a text file named by the // user for viewing outside of the SWMM program. // +// Update History +// ============== // Build 5.1.008: // - More input error reporting added. // - Rooftop Disconnection added to the types of LIDs. // - LID drain flows are now tracked separately. // - LID drain flows can now be routed to separate outlets. // - Check added to insure LID flows not returned to nonexistent pervious area. -// // Build 5.1.009: // - Fixed bug where LID's could return outflow to non-LID area when LIDs // make up entire subcatchment. -// // Build 5.1.010: // - Support for new Modified Green Ampt infiltration model added. // - Imported variable HasWetLids now properly initialized. // - Initial state of reporting (lidUnit->rptFile->wasDry) changed to // prevent duplicate printing of first line of detailed report file. -// // Build 5.1.011: // - The top of the storage layer is no longer used as a limit for an // underdrain offset thus allowing upturned drains to be modeled. // - Column headings for the detailed LID report file were modified. -// // Build 5.1.012: // - Redefined initialization of wasDry for LID reporting. -// // Build 5.1.013: // - Support added for LID units treating pervious area runoff. // - Support added for open/closed head levels and multiplier v. head // control curve for underdrain flow. // - Support added for unclogging permeable pavement at fixed intervals. // - Support added for pollutant removal in underdrain flow. -// // Build 5.1.014: // - Fixed bug in creating LidProcs when there are no subcatchments. // - Fixed bug in adding underdrain pollutant loads to mass balances. -// // Build 5.1.015: // - Support added for mutiple infiltration methods within a project. +// Build 5.2.0: +// - Covered property added to RAIN_BARREL parameters //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE @@ -94,7 +81,7 @@ #define ERR_SWALE_SURF " - check swale surface parameters" #define ERR_GREEN_AMPT " - check subcatchment Green-Ampt parameters" #define ERR_DRAIN_OFFSET " - drain offset exceeds storage height" -#define ERR_DRAIN_HEADS " - invalid drain open/closed heads" //(5.1.013) +#define ERR_DRAIN_HEADS " - invalid drain open/closed heads" #define ERR_SWALE_WIDTH " - invalid swale width" //----------------------------------------------------------------------------- @@ -107,14 +94,14 @@ enum LidLayerTypes { PAVE, // pavement layer DRAINMAT, // drainage mat layer DRAIN, // underdrain system - REMOVALS}; // pollutant removals //(5.1.013) + REMOVALS}; // pollutant removals //// Note: DRAINMAT must be placed before DRAIN so the two keywords can /// be distinguished from one another when parsing a line of input. char* LidLayerWords[] = {"SURFACE", "SOIL", "STORAGE", "PAVEMENT", "DRAINMAT", "DRAIN", - "REMOVALS", NULL}; //(5.1.013) + "REMOVALS", NULL}; char* LidTypeWords[] = {"BC", //bio-retention cell @@ -219,7 +206,7 @@ static int readSoilData(int j, char* tok[], int ntoks); static int readStorageData(int j, char* tok[], int ntoks); static int readDrainData(int j, char* tok[], int ntoks); static int readDrainMatData(int j, char* toks[], int ntoks); -static int readRemovalsData(int j, char* toks[], int ntoks); //(5.1.013) +static int readRemovalsData(int j, char* toks[], int ntoks); static int addLidUnit(int j, int k, int n, double x[], char* fname, int drainSubcatch, int drainNode); @@ -231,10 +218,12 @@ static void validateLidGroup(int j); static int isLidPervious(int k); static double getImpervAreaRunoff(int j); -static double getPervAreaRunoff(int j); //(5.1.013) +static double getPervAreaRunoff(int j); static double getSurfaceDepth(int subcatch); +static double getRainInflow(int j, TLidUnit* lidUnit); static void findNativeInfil(int j, double tStep); + static void evalLidUnit(int j, TLidUnit* lidUnit, double lidArea, double lidInflow, double tStep, double *qRunoff, double *qDrain, double *qReturn); @@ -295,14 +284,14 @@ void lid_create(int lidCount, int subcatchCount) LidProcs[j].drain.offset = 0.0; LidProcs[j].drainMat.thickness = 0.0; LidProcs[j].drainMat.roughness = 0.0; - LidProcs[j].drainRmvl = NULL; //(5.1.013) - LidProcs[j].drainRmvl = (double *) // - calloc(Nobjects[POLLUT], sizeof(double)); // - if (LidProcs[j].drainRmvl == NULL) // - { // - ErrorCode = ERR_MEMORY; // - return; // - } // + LidProcs[j].drainRmvl = NULL; + LidProcs[j].drainRmvl = (double *) + calloc(Nobjects[POLLUT], sizeof(double)); + if (LidProcs[j].drainRmvl == NULL) + { + ErrorCode = ERR_MEMORY; + return; + } } } @@ -318,7 +307,7 @@ void lid_delete() int j; for (j = 0; j < GroupCount; j++) freeLidGroup(j); FREE(LidGroups); - for (j = 0; j < LidCount; j++) FREE(LidProcs[j].drainRmvl); //(5.1.013) + for (j = 0; j < LidCount; j++) FREE(LidProcs[j].drainRmvl); FREE(LidProcs); GroupCount = 0; LidCount = 0; @@ -376,7 +365,7 @@ int lid_readProcParams(char* toks[], int ntoks) // LID_ID STORAGE // LID_ID DRAIN // LID_ID DRAINMAT -// LID_ID REMOVALS //(5.1.013) +// LID_ID REMOVALS // { int j, m; @@ -412,7 +401,7 @@ int lid_readProcParams(char* toks[], int ntoks) case PAVE: return readPavementData(j, toks, ntoks); case DRAIN: return readDrainData(j, toks, ntoks); case DRAINMAT: return readDrainMatData(j, toks, ntoks); - case REMOVALS: return readRemovalsData(j, toks, ntoks); //(5.1.013) + case REMOVALS: return readRemovalsData(j, toks, ntoks); } return error_setInpError(ERR_KEYWORD, toks[1]); } @@ -428,7 +417,7 @@ int lid_readGroupParams(char* toks[], int ntoks) // // Format of input data line is: // Subcatch_ID LID_ID Number Area Width InitSat FromImp ToPerv -// (RptFile DrainTo FromPerv) //(5.1.013) +// (RptFile DrainTo FromPerv) // where: // Subcatch_ID = name of subcatchment // LID_ID = name of LID process @@ -440,11 +429,11 @@ int lid_readGroupParams(char* toks[], int ntoks) // ToPerv (x[4]) = 1 if outflow goes to pervious sub-area; 0 if not // RptFile = name of detailed results file (optional) // DrainTo = name of subcatch/node for drain flow (optional) -// FromPerv (x[5]) = % of pervious runoff sent to LID //(5.1.013) +// FromPerv (x[5]) = % of pervious runoff sent to LID // { int i, j, k, n; - double x[6]; //(5.1.013) + double x[6]; char* fname = NULL; int drainSubcatch = -1, drainNode = -1; @@ -489,13 +478,13 @@ int lid_readGroupParams(char* toks[], int ntoks) } } - //... read percent of pervious area treated by LID unit //(5.1.013) - x[5] = 0.0; // - if (ntoks >= 11) // - { // - if (!getDouble(toks[10], &x[5]) || x[5] < 0.0 || x[5] > 100.0) // - return error_setInpError(ERR_NUMBER, toks[10]); // - } // + //... read percent of pervious area treated by LID unit + x[5] = 0.0; + if (ntoks >= 11) + { + if (!getDouble(toks[10], &x[5]) || x[5] < 0.0 || x[5] > 100.0) + return error_setInpError(ERR_NUMBER, toks[10]); + } //... create a new LID unit and add it to the subcatchment's LID group return addLidUnit(j, k, n, x, fname, drainSubcatch, drainNode); @@ -556,7 +545,7 @@ int addLidUnit(int j, int k, int n, double x[], char* fname, lidUnit->initSat = x[2] / 100.0; lidUnit->fromImperv = x[3] / 100.0; lidUnit->toPerv = (x[4] > 0.0); - lidUnit->fromPerv = x[5] / 100.0; //(5.1.013) + lidUnit->fromPerv = x[5] / 100.0; lidUnit->drainSubcatch = drainSubcatch; lidUnit->drainNode = drainNode; @@ -595,7 +584,7 @@ int readSurfaceData(int j, char* toks[], int ntoks) // Output: returns error code // // Format of data is: -// LID_ID SURFACE StorageHt VegVolFrac Roughness SurfSlope SideSlope DamHt +// LID_ID SURFACE StorageHt VegVolFrac Roughness SurfSlope SideSlope // { int i; @@ -631,11 +620,11 @@ int readPavementData(int j, char* toks[], int ntoks) // // Format of data is: // LID_ID PAVEMENT Thickness VoidRatio FracImperv Permeability ClogFactor -// (RegenDays RegenDegree) //(5.1.013) +// (RegenDays RegenDegree) // { int i; - double x[7]; //(5.1.013) + double x[7]; if ( ntoks < 7 ) return error_setInpError(ERR_ITEMS, ""); for (i = 2; i < 7; i++) @@ -644,19 +633,19 @@ int readPavementData(int j, char* toks[], int ntoks) return error_setInpError(ERR_NUMBER, toks[i]); } - // ... read optional clogging regeneration properties //(5.1.013) - x[5] = 0.0; // - if (ntoks > 7) // - { // - if (!getDouble(toks[7], &x[5]) || x[5] < 0.0) // - return error_setInpError(ERR_NUMBER, toks[7]); // - } // - x[6] = 0.0; // - if (ntoks > 8) // - { // - if (!getDouble(toks[8], &x[6]) || x[6] < 0.0 || x[6] > 1.0) // - return error_setInpError(ERR_NUMBER, toks[8]); // - } // + // ... read optional clogging regeneration properties + x[5] = 0.0; + if (ntoks > 7) + { + if (!getDouble(toks[7], &x[5]) || x[5] < 0.0) + return error_setInpError(ERR_NUMBER, toks[7]); + } + x[6] = 0.0; + if (ntoks > 8) + { + if (!getDouble(toks[8], &x[6]) || x[6] < 0.0 || x[6] > 1.0) + return error_setInpError(ERR_NUMBER, toks[8]); + } //... convert void ratio to void fraction x[1] = x[1]/(x[1] + 1.0); @@ -666,8 +655,8 @@ int readPavementData(int j, char* toks[], int ntoks) LidProcs[j].pavement.impervFrac = x[2]; LidProcs[j].pavement.kSat = x[3] / UCF(RAINFALL); LidProcs[j].pavement.clogFactor = x[4]; - LidProcs[j].pavement.regenDays = x[5]; //(5.1.013) - LidProcs[j].pavement.regenDegree = x[6]; // + LidProcs[j].pavement.regenDays = x[5]; + LidProcs[j].pavement.regenDegree = x[6]; return 0; } @@ -717,10 +706,11 @@ int readStorageData(int j, char* toks[], int ntoks) // Output: returns error code // // Format of data is: -// LID_ID STORAGE Thickness VoidRatio Ksat ClogFactor +// LID_ID STORAGE Thickness VoidRatio Ksat ClogFactor (YES/NO) // { int i; + int covered = FALSE; double x[6]; //... read numerical parameters @@ -731,6 +721,13 @@ int readStorageData(int j, char* toks[], int ntoks) return error_setInpError(ERR_NUMBER, toks[i]); } + //... check if rain barrel is covered + if (ntoks > 6) + { + if (match(toks[6], w_YES)) + covered = TRUE; + } + //... convert void ratio to void fraction x[1] = x[1]/(x[1] + 1.0); @@ -739,6 +736,7 @@ int readStorageData(int j, char* toks[], int ntoks) LidProcs[j].storage.voidFrac = x[1]; LidProcs[j].storage.kSat = x[2] / UCF(RAINFALL); LidProcs[j].storage.clogFactor = x[3]; + LidProcs[j].storage.covered = covered; return 0; } @@ -754,36 +752,36 @@ int readDrainData(int j, char* toks[], int ntoks) // Output: returns error code // // Format of data is: -// LID_ID DRAIN coeff expon offset delay hOpen hClose curve //(5.1.013) +// LID_ID DRAIN coeff expon offset delay hOpen hClose curve // { int i; - double x[6]; //(5.1.013) + double x[6]; //... read numerical parameters if ( ntoks < 6 ) return error_setInpError(ERR_ITEMS, ""); - for (i = 0; i < 6; i++) x[i] = 0.0; //(5.1.013) - for (i = 2; i < 8; i++) // + for (i = 0; i < 6; i++) x[i] = 0.0; + for (i = 2; i < 8; i++) { - if ( ntoks > i && ! getDouble(toks[i], &x[i-2]) || x[i-2] < 0.0 ) //(5.1.013) + if ( (ntoks > i) && (! getDouble(toks[i], &x[i-2]) || x[i-2]) < 0.0 ) return error_setInpError(ERR_NUMBER, toks[i]); } - i = -1; //(5.1.013) - if ( ntoks >= 9 ) // - { // - i = project_findObject(CURVE, toks[8]); // - if (i < 0) return error_setInpError(ERR_NAME, toks[8]); // - } // + i = -1; + if ( ntoks >= 9 ) + { + i = project_findObject(CURVE, toks[8]); + if (i < 0) return error_setInpError(ERR_NAME, toks[8]); + } //... save parameters to LID drain layer structure LidProcs[j].drain.coeff = x[0]; LidProcs[j].drain.expon = x[1]; LidProcs[j].drain.offset = x[2] / UCF(RAINDEPTH); LidProcs[j].drain.delay = x[3] * 3600.0; - LidProcs[j].drain.hOpen = x[4] / UCF(RAINDEPTH); //(5.1.013) - LidProcs[j].drain.hClose = x[5] / UCF(RAINDEPTH); // - LidProcs[j].drain.qCurve = i; // + LidProcs[j].drain.hOpen = x[4] / UCF(RAINDEPTH); + LidProcs[j].drain.hClose = x[5] / UCF(RAINDEPTH); + LidProcs[j].drain.qCurve = i; return 0; } @@ -823,8 +821,6 @@ int readDrainMatData(int j, char* toks[], int ntoks) //============================================================================= -//// This function was added to release 5.1.013. //// //(5.1.013) - int readRemovalsData(int j, char* toks[], int ntoks) // // Purpose: reads pollutant removal data for a LID process from line of input @@ -904,10 +900,10 @@ void lid_writeSummary() k = lidUnit->lidIndex; pctArea = lidUnit->area * lidUnit->number / Subcatch[j].area * 100.0; fprintf(Frpt.file, "\n %-16s %-16s", Subcatch[j].ID, LidProcs[k].ID); - fprintf(Frpt.file, "%6d %10.2f %10.2f %10.2f %10.2f %10.2f", //(5.1.013) + fprintf(Frpt.file, "%6d %10.2f %10.2f %10.2f %10.2f %10.2f", lidUnit->number, lidUnit->area * SQR(UCF(LENGTH)), lidUnit->fullWidth * UCF(LENGTH), pctArea, - lidUnit->fromImperv*100.0, lidUnit->fromPerv*100.0); //(5.1.013) + lidUnit->fromImperv*100.0, lidUnit->fromPerv*100.0); lidList = lidList->nextLidUnit; } } @@ -979,8 +975,8 @@ void validateLidProc(int j) || LidProcs[j].pavement.impervFrac > 1.0 ) { - strcpy(Msg, LidProcs[j].ID); - strcat(Msg, ERR_PAVE_LAYER); + sstrncpy(Msg, LidProcs[j].ID, MAXMSG); + sstrcat(Msg, ERR_PAVE_LAYER, MAXMSG); report_writeErrorMsg(ERR_LID_PARAMS, Msg); } } @@ -994,8 +990,8 @@ void validateLidProc(int j) || LidProcs[j].soil.kSat <= 0.0 || LidProcs[j].soil.kSlope < 0.0 ) { - strcpy(Msg, LidProcs[j].ID); - strcat(Msg, ERR_SOIL_LAYER); + sstrncpy(Msg, LidProcs[j].ID, MAXMSG); + sstrcat(Msg, ERR_SOIL_LAYER, MAXMSG); report_writeErrorMsg(ERR_LID_PARAMS, Msg); } } @@ -1006,8 +1002,8 @@ void validateLidProc(int j) if ( LidProcs[j].storage.voidFrac <= 0.0 || LidProcs[j].storage.voidFrac > 1.0 ) { - strcpy(Msg, LidProcs[j].ID); - strcat(Msg, ERR_STOR_LAYER); + sstrncpy(Msg, LidProcs[j].ID, MAXMSG); + sstrcat(Msg, ERR_STOR_LAYER, MAXMSG); report_writeErrorMsg(ERR_LID_PARAMS, Msg); } } @@ -1019,14 +1015,14 @@ void validateLidProc(int j) LidProcs[j].drain.offset = 0.0; } - //... check for invalid drain open/closed heads //(5.1.013) - if (LidProcs[j].drain.hOpen > 0.0 && // - LidProcs[j].drain.hOpen <= LidProcs[j].drain.hClose) // - { // - strcpy(Msg, LidProcs[j].ID); // - strcat(Msg, ERR_DRAIN_HEADS); // - report_writeErrorMsg(ERR_LID_PARAMS, Msg); // - } // + //... check for invalid drain open/closed heads + if (LidProcs[j].drain.hOpen > 0.0 && + LidProcs[j].drain.hOpen <= LidProcs[j].drain.hClose) + { + sstrncpy(Msg, LidProcs[j].ID, MAXMSG); + sstrcat(Msg, ERR_DRAIN_HEADS, MAXMSG); + report_writeErrorMsg(ERR_LID_PARAMS, Msg); + } //... compute the surface layer's overland flow constant (alpha) if ( LidProcs[j].lidType == VEG_SWALE ) @@ -1036,8 +1032,8 @@ void validateLidProc(int j) LidProcs[j].surface.thickness == 0.0 ) { - strcpy(Msg, LidProcs[j].ID); - strcat(Msg, ERR_SWALE_SURF); + sstrncpy(Msg, LidProcs[j].ID, MAXMSG); + sstrcat(Msg, ERR_SWALE_SURF, MAXMSG); report_writeErrorMsg(ERR_LID_PARAMS, Msg); } else LidProcs[j].surface.alpha = @@ -1122,7 +1118,7 @@ void validateLidGroup(int j) double totalArea = Subcatch[j].area; double totalLidArea = 0.0; double fromImperv = 0.0; - double fromPerv = 0.0; //(5.1.013) + double fromPerv = 0.0; TLidUnit* lidUnit; TLidList* lidList; TLidGroup lidGroup; @@ -1138,7 +1134,7 @@ void validateLidGroup(int j) //... update contributing fractions totalLidArea += (lidUnit->area * lidUnit->number); fromImperv += lidUnit->fromImperv; - fromPerv += lidUnit->fromPerv; //(5.1.013) + fromPerv += lidUnit->fromPerv; //... assign biocell soil layer infiltration parameters lidUnit->soilInfil.Ks = 0.0; @@ -1150,8 +1146,8 @@ void validateLidGroup(int j) (1.0 - lidUnit->initSat); if ( grnampt_setParams(&(lidUnit->soilInfil), p) == FALSE ) { - strcpy(Msg, LidProcs[k].ID); - strcat(Msg, ERR_SOIL_LAYER); + sstrncpy(Msg, LidProcs[k].ID, MAXMSG); + sstrcat(Msg, ERR_SOIL_LAYER, MAXMSG); report_writeErrorMsg(ERR_LID_PARAMS, Msg); } } @@ -1159,21 +1155,21 @@ void validateLidGroup(int j) //... assign vegetative swale infiltration parameters if ( LidProcs[k].lidType == VEG_SWALE ) { - if ( Subcatch[j].infilModel == GREEN_AMPT || //(5.1.015) - Subcatch[j].infilModel == MOD_GREEN_AMPT ) //(5.1.015) + if ( Subcatch[j].infilModel == GREEN_AMPT || + Subcatch[j].infilModel == MOD_GREEN_AMPT ) { - grnampt_getParams(j, p); //(5.1.015) + grnampt_getParams(j, p); if ( grnampt_setParams(&(lidUnit->soilInfil), p) == FALSE ) { - strcpy(Msg, LidProcs[k].ID); - strcat(Msg, ERR_GREEN_AMPT); + sstrncpy(Msg, LidProcs[k].ID, MAXMSG); + sstrcat(Msg, ERR_GREEN_AMPT, MAXMSG); report_writeErrorMsg(ERR_LID_PARAMS, Msg); } } if ( lidUnit->fullWidth <= 0.0 ) { - strcpy(Msg, LidProcs[k].ID); - strcat(Msg, ERR_SWALE_WIDTH); + sstrncpy(Msg, LidProcs[k].ID, MAXMSG); + sstrcat(Msg, ERR_SWALE_WIDTH, MAXMSG); report_writeErrorMsg(ERR_LID_PARAMS, Msg); } } @@ -1196,7 +1192,7 @@ void validateLidGroup(int j) { report_writeErrorMsg(ERR_LID_AREAS, Subcatch[j].ID); } - if ( fromImperv > 1.001 || fromPerv > 1.001 ) //(5.1.013) + if ( fromImperv > 1.001 || fromPerv > 1.001 ) { report_writeErrorMsg(ERR_LID_CAPTURE_AREA, Subcatch[j].ID); } @@ -1247,8 +1243,8 @@ void lid_initState() lidUnit->soilMoisture = 0.0; lidUnit->paveDepth = 0.0; lidUnit->dryTime = initDryTime; - lidUnit->volTreated = 0.0; //(5.1.013) - lidUnit->nextRegenDay = LidProcs[k].pavement.regenDays; // + lidUnit->volTreated = 0.0; + lidUnit->nextRegenDay = LidProcs[k].pavement.regenDays; initVol = 0.0; if ( LidProcs[k].soil.thickness > 0.0 ) { @@ -1445,8 +1441,6 @@ double lid_getDrainFlow(int j, int timePeriod) //============================================================================= -//// This function was modified for relelase 5.1.013. //// //(5.1.013) - void lid_addDrainLoads(int j, double c[], double tStep) // // Purpose: adds pollutant loads routed from drains to system @@ -1510,11 +1504,11 @@ void lid_addDrainRunon(int j) // Output: none. // { - int i; // index of an LID unit's LID process //(5.1.013) + int i; // index of an LID unit's LID process int k; // index of subcatchment receiving LID drain flow int p; // pollutant index double q; // drain flow rate (cfs) - double w; // mass of polllutant from drain flow //(5.1.013) + double w; // mass of polllutant from drain flow TLidUnit* lidUnit; TLidList* lidList; TLidGroup lidGroup; @@ -1529,7 +1523,7 @@ void lid_addDrainRunon(int j) { //... see if LID's drain discharges to another subcatchment lidUnit = lidList->lidUnit; - i = lidUnit->lidIndex; //(5.1.013) + i = lidUnit->lidIndex; k = lidUnit->drainSubcatch; if ( k >= 0 && k != j ) { @@ -1542,9 +1536,9 @@ void lid_addDrainRunon(int j) // point which is converted later on to a concentration) for (p = 0; p < Nobjects[POLLUT]; p++) { - w = q * Subcatch[j].oldQual[p] * LperFT3; //(5.1.013) - w = w * (1.0 - LidProcs[i].drainRmvl[p]); // - Subcatch[k].newQual[p] += w; // + w = q * Subcatch[j].oldQual[p] * LperFT3; + w = w * (1.0 - LidProcs[i].drainRmvl[p]); + Subcatch[k].newQual[p] += w; } } lidList = lidList->nextLidUnit; @@ -1565,7 +1559,7 @@ void lid_addDrainInflow(int j, double f) // and pollutant mass (Node[].newQual[]) inflow seen by nodes that // receive drain flow from the LID units in subcatchment j. { - int i, // LID process index //(5.1.013) + int i, // LID process index k, // node index p; // pollutant index double q, // drain flow (cfs) @@ -1584,7 +1578,7 @@ void lid_addDrainInflow(int j, double f) { //... see if LID's drain discharges to conveyance system node lidUnit = lidList->lidUnit; - i = lidUnit->lidIndex; //(5.1.013) + i = lidUnit->lidIndex; k = lidUnit->drainNode; if ( k >= 0 ) { @@ -1602,7 +1596,7 @@ void lid_addDrainInflow(int j, double f) //... add interpolated load to node's wet weather loading w = (1.0 - f) * w1 + f * w2; - w = w * (1.0 - LidProcs[i].drainRmvl[p]); //(5.1.013) + w = w * (1.0 - LidProcs[i].drainRmvl[p]); Node[k].newQual[p] += w; massbal_addInflowQual(WET_WEATHER_INFLOW, p, w); } @@ -1628,7 +1622,7 @@ void lid_getRunoff(int j, double tStep) TLidUnit* lidUnit; // a member of the list of LID units double lidArea; // area of an LID unit double qImperv = 0.0; // runoff from impervious areas (cfs) - double qPerv = 0.0; // runoff from pervious areas (cfs) //(5.1.013) + double qPerv = 0.0; // runoff from pervious areas (cfs) double lidInflow = 0.0; // inflow to an LID unit (ft/s) double qRunoff = 0.0; // surface runoff from all LID units (cfs) double qDrain = 0.0; // drain flow from all LID units (cfs) @@ -1652,7 +1646,7 @@ void lid_getRunoff(int j, double tStep) if ( Subcatch[j].area > Subcatch[j].lidArea ) { qImperv = getImpervAreaRunoff(j); - qPerv = getPervAreaRunoff(j); //(5.1.013) + qPerv = getPervAreaRunoff(j); } //... evaluate performance of each LID unit placed in the subcatchment @@ -1666,14 +1660,14 @@ void lid_getRunoff(int j, double tStep) if ( lidArea > 0.0 ) { //... find runoff from non-LID area treated by LID area (ft/sec) - lidInflow = (qImperv * lidUnit->fromImperv + //(5.1.013) - qPerv * lidUnit->fromPerv) / lidArea; // + lidInflow = (qImperv * lidUnit->fromImperv + + qPerv * lidUnit->fromPerv) / lidArea; //... update total runoff volume treated VlidIn += lidInflow * lidArea * tStep; //... add rainfall onto LID inflow (ft/s) - lidInflow = lidInflow + Subcatch[j].rainfall; + lidInflow = lidInflow + getRainInflow(j, lidUnit); // ... add upstream runon only if LID occupies full subcatchment if ( Subcatch[j].area == Subcatch[j].lidArea ) @@ -1726,7 +1720,7 @@ void findNativeInfil(int j, double tStep) NativeInfil = infil_getInfil(j, tStep, Subcatch[j].rainfall, Subcatch[j].runon, - getSurfaceDepth(j)); //(5.1.015) + getSurfaceDepth(j)); } //... see if there is any groundwater-imposed limit on infil. @@ -1739,6 +1733,23 @@ void findNativeInfil(int j, double tStep) //============================================================================= +double getRainInflow(int j, TLidUnit* lidUnit) +// +// Purpose: gets rainfall inflow to an LID unit. +// Input: j = subcatchment index +// lidUnit = ptr. to an LID unit +// Output: returns rainfall rate over the LID unit (ft/sec) +// +{ + TLidProc* lidProc = &LidProcs[lidUnit->lidIndex]; + + if (lidProc->lidType == RAIN_BARREL && + lidProc->storage.covered == TRUE) return 0.0; + return Subcatch[j].rainfall; +} + +//============================================================================= + double getImpervAreaRunoff(int j) // // Purpose: computes runoff from impervious area of a subcatchment that @@ -1769,8 +1780,6 @@ double getImpervAreaRunoff(int j) //============================================================================= -//// This function was added for release 5.1.013. //// //(5.1.013) - double getPervAreaRunoff(int j) // // Purpose: computes runoff from pervious area of a subcatchment that @@ -2018,5 +2027,5 @@ void initLidRptFile(char* title, char* lidID, char* subcatchID, TLidUnit* lidUni //... initialize LID dryness state lidUnit->rptFile->wasDry = 1; - strcpy(lidUnit->rptFile->results, ""); + sstrncpy(lidUnit->rptFile->results, "", 0); } diff --git a/swmm/swmm5/swmm5/lid.h b/swmm/swmm5/swmm5/lid.h index 146b58e..b250fcf 100644 --- a/swmm/swmm5/swmm5/lid.h +++ b/swmm/swmm5/swmm5/lid.h @@ -2,28 +2,23 @@ // lid.h // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/14 (Build 5.1.001) -// 03/19/15 (Build 5.1.008) -// 08/01/16 (Build 5.1.011) -// 03/14/17 (Build 5.1.012) -// 05/10/18 (Build 5.1.013) -// Author: L. Rossman (US EPA) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) +// Author: L. Rossman // // Public interface for LID functions. // +// Update History +// ============== // Build 5.1.008: // - Support added for Roof Disconnection LID. // - Support added for separate routing of LID drain flows. // - Detailed LID reporting modified. -// // Build 5.1.011: -// - Water depth replaces moisture content for LID's pavement layer. +// - Water depth replaces moisture content for LID's pavement layer. // - Arguments for lidproc_saveResults() modified. -// // Build 5.1.012: // - Redefined meaning of wasDry in TLidRptFile structure. -// // Build 5.1.013: // - New member fromPerv added to TLidUnit structure to allow LID // units to also treat pervious area runoff. @@ -35,13 +30,13 @@ // pollutant removal values. // - New members added to TPavementLayer and TLidUnit to support // unclogging permeable pavement at fixed intervals. -// +// Build 5.2.0: +// - Covered property added to RAIN_BARREL parameters //----------------------------------------------------------------------------- #ifndef LID_H #define LID_H - #include #include #include @@ -52,8 +47,8 @@ //----------------------------------------------------------------------------- enum LidTypes { BIO_CELL, // bio-retention cell - RAIN_GARDEN, // rain garden - GREEN_ROOF, // green roof + RAIN_GARDEN, // rain garden + GREEN_ROOF, // green roof INFIL_TRENCH, // infiltration trench POROUS_PAVEMENT, // porous pavement RAIN_BARREL, // rain barrel @@ -74,7 +69,7 @@ typedef struct { double thickness; // depression storage or berm ht. (ft) double voidFrac; // available fraction of storage volume - double roughness; // surface Mannings n + double roughness; // surface Mannings n double surfSlope; // land surface slope (fraction) double sideSlope; // swale side slope (run/rise) double alpha; // slope/roughness term in Manning eqn. @@ -89,8 +84,8 @@ typedef struct double impervFrac; // impervious area fraction double kSat; // permeability (ft/sec) double clogFactor; // clogging factor - double regenDays; // clogging regeneration interval (days) //(5.1.013) - double regenDegree; // degree of clogging regeneration // + double regenDays; // clogging regeneration interval (days) + double regenDegree; // degree of clogging regeneration } TPavementLayer; // LID Soil Layer @@ -112,6 +107,7 @@ typedef struct double voidFrac; // void volume / total volume double kSat; // saturated hydraulic conductivity (ft/sec) double clogFactor; // clogging factor + int covered; // TRUE if rain barrel is covered } TStorageLayer; // Underdrain System (part of Storage Layer) @@ -121,9 +117,9 @@ typedef struct double expon; // underdrain head exponent (for in or mm) double offset; // offset height of underdrain (ft) double delay; // rain barrel drain delay time (sec) - double hOpen; // head when drain opens (ft) //(5.1.013) - double hClose; // head when drain closes (ft) // - int qCurve; // curve controlling flow rate (optional) // + double hOpen; // head when drain opens (ft) + double hClose; // head when drain closes (ft) + int qCurve; // curve controlling flow rate (optional) } TDrainLayer; // Drainage Mat Layer (for green roofs) @@ -146,7 +142,7 @@ typedef struct TStorageLayer storage; // storage layer parameters TDrainLayer drain; // underdrain system parameters TDrainMatLayer drainMat; // drainage mat layer - double* drainRmvl; // underdrain pollutant removals //(5.1.013) + double* drainRmvl; // underdrain pollutant removals } TLidProc; // Water Balance Statistics @@ -179,13 +175,13 @@ typedef struct double botWidth; // bottom width of single unit (ft) double initSat; // initial saturation of soil & storage layers double fromImperv; // fraction of impervious area runoff treated - double fromPerv; // fraction of pervious area runoff treated //(5.1.013) + double fromPerv; // fraction of pervious area runoff treated int toPerv; // 1 if outflow sent to pervious area; 0 if not int drainSubcatch; // subcatchment receiving drain flow int drainNode; // node receiving drain flow TLidRptFile* rptFile; // pointer to detailed report file - TGrnAmpt soilInfil; // infil. object for biocell soil layer + TGrnAmpt soilInfil; // infil. object for biocell soil layer double surfaceDepth; // depth of ponded water on surface layer (ft) double paveDepth; // depth of water in porous pavement layer double soilMoisture; // moisture content of biocell soil layer @@ -193,12 +189,12 @@ typedef struct // net inflow - outflow from previous time step for each LID layer (ft/s) double oldFluxRates[MAX_LAYERS]; - + double dryTime; // time since last rainfall (sec) double oldDrainFlow; // previous drain flow (cfs) double newDrainFlow; // current drain flow (cfs) - double volTreated; // total volume treated (ft) //(5.1.013) - double nextRegenDay; // next day when unit regenerated // + double volTreated; // total volume treated (ft) + double nextRegenDay; // next day when unit regenerated TWaterBalance waterBalance; // water balance quantites } TLidUnit; @@ -237,5 +233,4 @@ double lidproc_getOutflow(TLidUnit* lidUnit, TLidProc* lidProc, void lidproc_saveResults(TLidUnit* lidUnit, double ucfRainfall, double ucfRainDepth); - -#endif //LID_H +#endif diff --git a/swmm/swmm5/swmm5/lidproc.c b/swmm/swmm5/swmm5/lidproc.c index 54b3f9c..aae0ac9 100644 --- a/swmm/swmm5/swmm5/lidproc.c +++ b/swmm/swmm5/swmm5/lidproc.c @@ -2,28 +2,20 @@ // lidproc.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/12 (Build 5.1.001) -// 05/19/14 (Build 5.1.006) -// 09/15/14 (Build 5.1.007) -// 03/19/15 (Build 5.1.008) -// 04/30/15 (Build 5.1.009) -// 08/05/15 (Build 5.1.010) -// 08/01/16 (Build 5.1.011) -// 03/14/17 (Build 5.1.012) -// 05/10/18 (Build 5.1.013) -// 03/01/20 (Build 5.1.014) -// Author: L. Rossman (US EPA) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) +// Author: L. Rossman // // This module computes the hydrologic performance of an LID (Low Impact // Development) unit at a given point in time. // +// Update History +// ============== // Build 5.1.007: // - Euler integration now applied to all LID types except Vegetative // Swale which continues to use successive approximation. // - LID layer flux routines were re-written to more accurately model // flooded conditions. -// // Build 5.1.008: // - MAX_STATE_VARS replaced with MAX_LAYERS. // - Optional soil layer added to Porous Pavement LID. @@ -33,13 +25,10 @@ // - Detailed reporting procedure fixed. // - Possibile negative head on Bioretention Cell drain avoided. // - Bug in computing flow through Green Roof drainage mat fixed. -// // Build 5.1.009: // - Fixed typo in net flux rate for vegetative swale LID. -// // Build 5.1.010: // - New modified version of Green-Ampt used for surface layer infiltration. -// // Build 5.1.011: // - Re-named STOR_INFIL to STOR_EXFIL and StorageInfil to StorageExfil to // better reflect their meaning. @@ -48,22 +37,22 @@ // - Flux rate routines for LIDs with underdrains modified to produce more // physically meaningful results. // - Reporting of detailed results re-written. -// // Build 5.1.012: // - Modified upper limit for soil layer percolation. // - Modified upper limit on surface infiltration into rain gardens. // - Modified upper limit on drain flow for LIDs with storage layers. // - Used re-defined wasDry variable for LID reports to fix duplicate lines. -// // Build 5.1.013: // - Support added for open/closed head levels and multiplier v. head curve // to control underdrain flow. // - Support added for regenerating pavement permeability at fixed intervals. -// // Build 5.1.014: // - Fixed failure to initialize all LID layer moisture volumes to 0 before // computing LID unit performance in lidproc_getOutflow. -// +// Build 5.2.0: +// - Fixed failure to account for effect of Impervious Surface Fraction on +// pavement permeability for Permeable Pavement LID +// - Fixed units conversion for pavement depth in detailed report file. //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE @@ -179,7 +168,6 @@ static int modpuls_solve(int n, double* x, double* xOld, double* xPrev, double* qOld, double* q, double dt, double omega, void (*derivs)(double*, double*)); - //============================================================================= void lidproc_initWaterBalance(TLidUnit *lidUnit, double initVol) @@ -379,7 +367,7 @@ void lidproc_saveResults(TLidUnit* lidUnit, double ucfRainfall, double ucfRainDe double totalVolume; // total volume stored in LID (ft) double rptVars[MAX_RPT_VARS]; // array of reporting variables int isDry = FALSE; // true if current state of LID is dry - char timeStamp[24]; // date/time stamp + char timeStamp[TIME_STAMP_SIZE + 1]; // date/time stamp double elapsedHrs; // elapsed hours //... find total evap. rate and stored volume @@ -395,7 +383,7 @@ void lidproc_saveResults(TLidUnit* lidUnit, double ucfRainfall, double ucfRainDe SurfaceOutflow < MINFLOW && StorageDrain < MINFLOW && StorageExfil < MINFLOW && - totalEvap < MINFLOW + totalEvap < MINFLOW ) isDry = TRUE; //... update status of HasWetLids @@ -418,7 +406,7 @@ void lidproc_saveResults(TLidUnit* lidUnit, double ucfRainfall, double ucfRainDe //... convert storage results to original units (in or mm) ucf = ucfRainDepth; rptVars[SURF_DEPTH] = theLidUnit->surfaceDepth*ucf; - rptVars[PAVE_DEPTH] = theLidUnit->paveDepth; + rptVars[PAVE_DEPTH] = theLidUnit->paveDepth*ucf; rptVars[SOIL_MOIST] = theLidUnit->soilMoisture; rptVars[STOR_DEPTH] = theLidUnit->storageDepth*ucf; @@ -428,14 +416,15 @@ void lidproc_saveResults(TLidUnit* lidUnit, double ucfRainfall, double ucfRainDe if ( !isDry && theLidUnit->rptFile->wasDry > 1) { fprintf(theLidUnit->rptFile->file, "%s", - theLidUnit->rptFile->results); + theLidUnit->rptFile->results); } //... write the current results to a string which is saved between // reporting periods elapsedHrs = NewRunoffTime / 1000.0 / 3600.0; - datetime_getTimeStamp(M_D_Y, getDateTime(NewRunoffTime), 24, timeStamp); - sprintf(theLidUnit->rptFile->results, + datetime_getTimeStamp( + M_D_Y, getDateTime(NewRunoffTime), TIME_STAMP_SIZE, timeStamp); + snprintf(theLidUnit->rptFile->results, sizeof(theLidUnit->rptFile->results), "\n%20s\t %8.3f\t %8.3f\t %8.4f\t %8.3f\t %8.3f\t %8.3f\t %8.3f\t" "%8.3f\t %8.3f\t %8.3f\t %8.3f\t %8.3f\t %8.3f", timeStamp, elapsedHrs, rptVars[0], rptVars[1], rptVars[2], @@ -450,7 +439,7 @@ void lidproc_saveResults(TLidUnit* lidUnit, double ucfRainfall, double ucfRainDe if ( theLidUnit->rptFile->wasDry == 0 ) { fprintf(theLidUnit->rptFile->file, "%s", - theLidUnit->rptFile->results); + theLidUnit->rptFile->results); } //... increment the number of successive dry periods @@ -461,8 +450,8 @@ void lidproc_saveResults(TLidUnit* lidUnit, double ucfRainfall, double ucfRainDe else { //... write the current results to the report file - fprintf(theLidUnit->rptFile->file, "%s", - theLidUnit->rptFile->results); + fprintf(theLidUnit->rptFile->file, "%s", + theLidUnit->rptFile->results); //... re-set the number of successive dry periods to 0 theLidUnit->rptFile->wasDry = 0; @@ -663,8 +652,7 @@ void biocellFluxRates(double x[], double f[]) maxRate = (soilPorosity - soilTheta) * soilThickness / Tstep + SoilPerc + SoilEvap; SurfaceInfil = MIN(SurfaceInfil, maxRate); - - } + } //... storage & soil layers are full else if ( soilTheta >= soilPorosity && storageDepth >= storageThickness ) @@ -879,10 +867,10 @@ void pavementFluxRates(double x[], double f[]) SurfaceInfil = SurfaceInflow + (SurfaceVolume / Tstep); //... find perc rate out of pavement layer - PavePerc = getPavementPermRate(); + PavePerc = getPavementPermRate() * pervFrac; - //... surface infiltration can't exceed pavement permeability //(5.1.013) - SurfaceInfil = MIN(SurfaceInfil, PavePerc); // + //... surface infiltration can't exceed pavement permeability + SurfaceInfil = MIN(SurfaceInfil, PavePerc); //... limit pavement perc by available water maxRate = PaveVolume/Tstep + SurfaceInfil - PaveEvap; @@ -1182,7 +1170,7 @@ void barrelFluxRates(double x[], double f[]) // { double storageDepth = x[STOR]; - double head; + double head; double maxValue; //... assign values to layer volumes @@ -1198,16 +1186,16 @@ void barrelFluxRates(double x[], double f[]) //... compute outflow if time since last rain exceeds drain delay // (dryTime is updated in lid.evalLidUnit at each time step) if ( theLidProc->drain.delay == 0.0 || - theLidUnit->dryTime >= theLidProc->drain.delay ) - { - head = storageDepth - theLidProc->drain.offset; - if ( head > 0.0 ) - { - StorageDrain = getStorageDrainRate(storageDepth, 0.0, 0.0, 0.0); - maxValue = (head/Tstep); - StorageDrain = MIN(StorageDrain, maxValue); - } - } + theLidUnit->dryTime >= theLidProc->drain.delay ) + { + head = storageDepth - theLidProc->drain.offset; + if ( head > 0.0 ) + { + StorageDrain = getStorageDrainRate(storageDepth, 0.0, 0.0, 0.0); + maxValue = (head/Tstep); + StorageDrain = MIN(StorageDrain, maxValue); + } + } //... limit inflow to available storage StorageInflow = SurfaceInflow; @@ -1357,7 +1345,7 @@ double getStorageDrainRate(double storageDepth, double soilTheta, // layers above it (soil, pavement, and surface in that order) // minus the drain outlet offset. { - int curve = theLidProc->drain.qCurve; //(5.1.013) + int curve = theLidProc->drain.qCurve; double head = storageDepth; double outflow = 0.0; double paveThickness = theLidProc->pavement.thickness; @@ -1397,13 +1385,13 @@ double getStorageDrainRate(double storageDepth, double soilTheta, } } - // --- no outflow if: //(5.1.013) - // a) no prior outflow and head below open threshold // - // b) prior outflow and head below closed threshold // - if ( theLidUnit->oldDrainFlow == 0.0 && // - head <= theLidProc->drain.hOpen ) return 0.0; // - if ( theLidUnit->oldDrainFlow > 0.0 && // - head <= theLidProc->drain.hClose ) return 0.0; // + // --- no outflow if: + // a) no prior outflow and head below open threshold + // b) prior outflow and head below closed threshold + if ( theLidUnit->oldDrainFlow == 0.0 && + head <= theLidProc->drain.hOpen ) return 0.0; + if ( theLidUnit->oldDrainFlow > 0.0 && + head <= theLidProc->drain.hClose ) return 0.0; // --- make head relative to drain offset head -= theLidProc->drain.offset; @@ -1420,7 +1408,7 @@ double getStorageDrainRate(double storageDepth, double soilTheta, pow(head, theLidProc->drain.expon); // --- apply user-supplied control curve to outflow - if (curve >= 0) outflow *= table_lookup(&Curve[curve], head); //(5.1.013) + if (curve >= 0) outflow *= table_lookup(&Curve[curve], head); // --- convert outflow to ft/s outflow /= UCF(RAINFALL); @@ -1527,7 +1515,7 @@ void updateWaterBalance(TLidUnit *lidUnit, double inflow, double evap, // Output: none // { - lidUnit->volTreated += inflow * Tstep; //(5.1.013) + lidUnit->volTreated += inflow * Tstep; lidUnit->waterBalance.inflow += inflow * Tstep; lidUnit->waterBalance.evap += evap * Tstep; lidUnit->waterBalance.infil += infil * Tstep; diff --git a/swmm/swmm5/swmm5/link.c b/swmm/swmm5/swmm5/link.c index 3abc2f5..6de3688 100644 --- a/swmm/swmm5/swmm5/link.c +++ b/swmm/swmm5/swmm5/link.c @@ -2,54 +2,46 @@ // link.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/14 (Build 5.1.001) -// 09/15/14 (Build 5.1.007) -// 03/19/15 (Build 5.1.008) -// 08/05/15 (Build 5.1.010) -// 08/01/16 (Build 5.1.011) -// 03/14/17 (Build 5.1.012) -// 05/10/18 (Build 5.1.013) -// 03/01/20 (Build 5.1.014) -// Author: L. Rossman (EPA) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) +// Author: L. Rossman // M. Tryby (EPA) // // Conveyance system link functions // +// Update History +// ============== // Build 5.1.007: // - Optional surcharging of weirs introduced. -// // Build 5.1.008: // - Bug in finding flow through surcharged weir fixed. // - Bug in finding if conduit is upstrm/dnstrm full fixed. // - Monthly conductivity adjustment applied to conduit seepage. // - Conduit seepage limited by conduit's flow rate. -// // Build 5.1.010: // - Support added for new ROADWAY_WEIR object. // - Time of last setting change initialized for links. -// // Build 5.1.011: // - Crest elevation of regulator links raised to downstream invert. // - Fixed converting roadWidth weir parameter to internal units. // - Weir shape parameter deprecated. // - Extra geometric parameters ignored for non-conduit open rectangular // cross sections. -// // Build 5.1.012: // - Conduit seepage rate now based on flow width, not wetted perimeter. // - Formula for side flow weir corrected. // - Crest length contraction adjustments corrected. -// // Build 5.1.013: // - Maximum depth adjustments made for storage units that can surcharge. // - Support added for head-dependent weir coefficient curves. // - Adjustment of regulator link crest offset to match downstream node invert // now only done for Dynamic Wave flow routing. -// // Build 5.1.014: // - Conduit evap. and seepage losses initialized to 0 in conduit_initState() // and not allowed to exceed current flow rate in conduit_getLossRate(). +// Build 5.2.0: +// - Support added for Streets and Inlets. +// - Support added for variable speed pumps. //----------------------------------------------------------------------------- #define _CRT_SECURE_NO_DEPRECATE @@ -57,6 +49,7 @@ #include #include #include "headers.h" +#include "inlet.h" //----------------------------------------------------------------------------- // Constants @@ -102,7 +95,7 @@ static double conduit_getLength(int j); static double conduit_getLengthFactor(int j, int k, double roughness); static double conduit_getSlope(int j); static double conduit_getInflow(int j); -static double conduit_getLossRate(int j, double q); //(5.1.014) +static double conduit_getLossRate(int j, double q); static int pump_readParams(int j, int k, char* tok[], int ntoks); static void pump_validate(int j, int k); @@ -166,13 +159,19 @@ int link_readXsectParams(char* tok[], int ntoks) // Output: returns an error code // Purpose: reads a link's cross section parameters from a tokenized // line of input data. +// Formats: +// Link Shape Geom1 Geom2 Geom3 Geom4 (Barrels Culvert) +// Link IRREGULAR TransectID +// Link STREET StreetID // { int i, j, k; double x[4]; + // --- check for minimum number of tokens + if (ntoks < 3) return error_setInpError(ERR_ITEMS, ""); + // --- get index of link - if ( ntoks < 6 ) return error_setInpError(ERR_ITEMS, ""); j = project_findObject(LINK, tok[0]); if ( j < 0 ) return error_setInpError(ERR_NAME, tok[0]); @@ -193,9 +192,24 @@ int link_readXsectParams(char* tok[], int ntoks) if ( i < 0 ) return error_setInpError(ERR_NAME, tok[2]); Link[j].xsect.type = k; Link[j].xsect.transect = i; + return 0; } + + // --- for street cross section, find index of Street object + else if (k == STREET_XSECT) + { + i = project_findObject(STREET, tok[2]); + if (i < 0) return error_setInpError(ERR_NAME, tok[2]); + Link[j].xsect.type = k; + Link[j].xsect.transect = i; + return 0; + } + else { + // --- check that geometric parameters are present + if (ntoks < 6) return error_setInpError(ERR_ITEMS, ""); + // --- parse max. depth & shape curve for a custom shape if ( k == CUSTOM ) { @@ -241,7 +255,6 @@ int link_readXsectParams(char* tok[], int ntoks) if ( i < 0 ) return error_setInpError(ERR_NUMBER, tok[7]); else Link[j].xsect.culvertCode = i; } - } return 0; } @@ -360,7 +373,7 @@ void link_setParams(int j, int type, int n1, int n2, int k, double x[]) Weir[k].canSurcharge = (int)x[6]; Weir[k].roadWidth = x[7] / UCF(LENGTH); Weir[k].roadSurface = (int)x[8]; - Weir[k].cdCurve = (int)x[9]; //(5.1.013) + Weir[k].cdCurve = (int)x[9]; break; case OUTLET: @@ -407,13 +420,13 @@ void link_validate(int j) if ( Node[Link[j].node1].invertElev + Link[j].offset1 < Node[Link[j].node2].invertElev ) { - if (RouteModel == DW) //(5.1.013) + if (RouteModel == DW) { Link[j].offset1 = Node[Link[j].node2].invertElev - Node[Link[j].node1].invertElev; - report_writeWarningMsg(WARN10b, Link[j].ID); //(5.1.013) + report_writeWarningMsg(WARN10b, Link[j].ID); } - else report_writeWarningMsg(WARN10a, Link[j].ID); //(5.1.013) + else report_writeWarningMsg(WARN10a, Link[j].ID); } } @@ -427,7 +440,7 @@ void link_validate(int j) // --- extend upstream node's full depth to link's crown elevation n = Link[j].node1; - if ( Node[n].type != STORAGE || Node[n].surDepth > 0.0 ) //(5.1.013) + if ( Node[n].type != STORAGE || Node[n].surDepth > 0.0 ) { Node[n].fullDepth = MAX(Node[n].fullDepth, Link[j].offset1 + Link[j].xsect.yFull); @@ -435,7 +448,7 @@ void link_validate(int j) // --- do same for downstream node only for conduit links n = Link[j].node2; - if ( (Node[n].type != STORAGE || Node[n].surDepth > 0.0) && //(5.1.013) + if ( (Node[n].type != STORAGE || Node[n].surDepth > 0.0) && Link[j].type == CONDUIT ) { Node[n].fullDepth = MAX(Node[n].fullDepth, @@ -514,7 +527,7 @@ void link_initState(int j) { Link[j].oldQual[p] = 0.0; Link[j].newQual[p] = 0.0; - Link[j].totalLoad[p] = 0.0; + Link[j].totalLoad[p] = 0.0; } } @@ -870,7 +883,7 @@ double link_getPower(int j) //============================================================================= -double link_getLossRate(int j, double q) //(5.1.014) +double link_getLossRate(int j, double q) // // Input: j = link index // q = flow rate (ft3/sec) @@ -880,7 +893,7 @@ double link_getLossRate(int j, double q) / // evaporation and seepage. // { - if ( Link[j].type == CONDUIT ) return conduit_getLossRate(j, q); //(5.1.014) + if ( Link[j].type == CONDUIT ) return conduit_getLossRate(j, q); else return 0.0; } @@ -999,6 +1012,13 @@ void conduit_validate(int j, int k) Conduit[k].roughness = Transect[Link[j].xsect.transect].roughness; } + // --- if street xsection, then set its parameters + if (Link[j].xsect.type == STREET_XSECT) + { + xsect_setStreetXsectParams(&Link[j].xsect); + Conduit[k].roughness = Street[Link[j].xsect.transect].roughness; + } + // --- if force main xsection, adjust units on D-W roughness height if ( Link[j].xsect.type == FORCE_MAIN ) { @@ -1031,7 +1051,7 @@ void conduit_validate(int j, int k) report_writeWarningMsg(WARN03, Link[j].ID); Link[j].offset1 = 0.0; } - if ( Link[j].offset2 < 0.0 ) + if ( Link[j].offset2 < 0.0 ) { report_writeWarningMsg(WARN03, Link[j].ID); Link[j].offset2 = 0.0; @@ -1282,8 +1302,8 @@ void conduit_initState(int j, int k) { Link[j].newDepth = link_getYnorm(j, Link[j].q0 / Conduit[k].barrels); Link[j].oldDepth = Link[j].newDepth; - Conduit[k].evapLossRate = 0.0; //(5.1.014) - Conduit[k].seepLossRate = 0.0; //(5.1.014) + Conduit[k].evapLossRate = 0.0; + Conduit[k].seepLossRate = 0.0; } //============================================================================= @@ -1302,8 +1322,6 @@ double conduit_getInflow(int j) //============================================================================= -//// This function was modified for relese 5.1.014. //// //(5.1.014) - double conduit_getLossRate(int j, double q) // // Input: j = link index @@ -1457,7 +1475,7 @@ void pump_validate(int j, int k) else { if ( Curve[m].curveType < PUMP1_CURVE || - Curve[m].curveType > PUMP4_CURVE ) + Curve[m].curveType > PUMP5_CURVE ) report_writeErrorMsg(ERR_NO_CURVE, Link[j].ID); // --- store pump curve type with pump's parameters @@ -1521,6 +1539,7 @@ double pump_getInflow(int j) int n1, n2; double vol, depth, head; double qIn, qIn1, dh = 0.001; + double s = 1.0; // speed setting k = Link[j].subIndex; m = Pump[k].pumpCurve; @@ -1558,23 +1577,22 @@ double pump_getInflow(int j) break; case PUMP3_CURVE: - head = ( (Node[n2].newDepth + Node[n2].invertElev) - - (Node[n1].newDepth + Node[n1].invertElev) ); - - head = MAX(head, 0.0); - - qIn = table_lookup(&Curve[m], head*UCF(LENGTH)) / UCF(FLOW); - - // --- compute dQ/dh (slope of pump curve) and - // reverse sign since flow decreases with increasing head - Link[j].dqdh = -table_getSlope(&Curve[m], head*UCF(LENGTH)) * - UCF(LENGTH) / UCF(FLOW); - - // --- check if off of pump curve - head *= UCF(LENGTH); - if ( head < Pump[k].xMin || head > Pump[k].xMax ) - Link[j].flowClass = YES; - break; + case PUMP5_CURVE: + if (Curve[m].curveType == PUMP5_CURVE) s = Link[j].setting; + head = ((Node[n2].newDepth + Node[n2].invertElev) - + (Node[n1].newDepth + Node[n1].invertElev)) / s / s; + head = MAX(head, 0.0) * UCF(LENGTH); + qIn = table_lookup(&Curve[m], head) / UCF(FLOW); + + // --- compute dQ/dh (slope of pump curve) and + // reverse sign since flow decreases with increasing head + Link[j].dqdh = -table_getSlope(&Curve[m], head) * + UCF(LENGTH) / UCF(FLOW) / s; + + // --- check if off of pump curve + if (head < Pump[k].xMin || head > Pump[k].xMax) + Link[j].flowClass = YES; + break; case PUMP4_CURVE: depth = Node[n1].newDepth; @@ -1984,7 +2002,7 @@ int weir_readParams(int j, int k, char* tok[], int ntoks) { int m; int n1, n2; - double x[10]; //(5.1.013) + double x[10]; char* id; // --- check for valid ID and end node IDs @@ -2011,7 +2029,7 @@ int weir_readParams(int j, int k, char* tok[], int ntoks) x[6] = 1.0; x[7] = 0.0; x[8] = 0.0; - x[9] = -1.0; //(5.1.013) + x[9] = -1.0; if ( ntoks >= 7 && *tok[6] != '*' ) { m = findmatch(tok[6], NoYesWords); @@ -2050,12 +2068,12 @@ int weir_readParams(int j, int k, char* tok[], int ntoks) } } - if (ntoks >= 13 && *tok[12] != '*') //(5.1.013) - { // - m = project_findObject(CURVE, tok[12]); // coeff. curve // - if (m < 0) return error_setInpError(ERR_NAME, tok[12]); // - x[9] = m; // - } // + if (ntoks >= 13 && *tok[12] != '*') + { + m = project_findObject(CURVE, tok[12]); // coeff. curve + if (m < 0) return error_setInpError(ERR_NAME, tok[12]); + x[9] = m; + } // --- add parameters to weir object Link[j].ID = id; @@ -2303,8 +2321,8 @@ void weir_getFlow(int j, int k, double head, double dir, int hasFlapGate, double area; double veloc; int wType; - int cdCurve = Weir[k].cdCurve; //(5.1.013) - double cDisch1 = Weir[k].cDisch1; // + int cdCurve = Weir[k].cdCurve; + double cDisch1 = Weir[k].cDisch1; // --- q1 = flow through central portion of weir, // q2 = flow through end sections of trapezoidal weir @@ -2317,8 +2335,8 @@ void weir_getFlow(int j, int k, double head, double dir, int hasFlapGate, length = Link[j].xsect.wMax * UCF(LENGTH); h = head * UCF(LENGTH); - // --- lookup tabulated discharge coeff. //(5.1.013) - if ( cdCurve >= 0 ) cDisch1 = table_lookup(&Curve[cdCurve], h); // + // --- lookup tabulated discharge coeff. + if ( cdCurve >= 0 ) cDisch1 = table_lookup(&Curve[cdCurve], h); // --- use appropriate formula for weir flow wType = Weir[k].type; @@ -2331,7 +2349,7 @@ void weir_getFlow(int j, int k, double head, double dir, int hasFlapGate, // --- reduce length when end contractions present length -= 0.1 * Weir[k].endCon * h; length = MAX(length, 0.0); - *q1 = cDisch1 * length * pow(h, 1.5); //(5.1.013) + *q1 = cDisch1 * length * pow(h, 1.5); break; case SIDEFLOW_WEIR: @@ -2342,23 +2360,23 @@ void weir_getFlow(int j, int k, double head, double dir, int hasFlapGate, // --- weir behaves as a transverse weir under reverse flow if ( dir < 0.0 ) - *q1 = cDisch1 * length * pow(h, 1.5); //(5.1.013) + *q1 = cDisch1 * length * pow(h, 1.5); else // Corrected formula (see Metcalf & Eddy, Inc., // Wastewater Engineering, McGraw-Hill, 1972 p. 164). - *q1 = cDisch1 * pow(length, 0.83) * pow(h, 1.67); //(5.1.013) + *q1 = cDisch1 * pow(length, 0.83) * pow(h, 1.67); break; case VNOTCH_WEIR: - *q1 = cDisch1 * Weir[k].slope * pow(h, 2.5); //(5.1.013) + *q1 = cDisch1 * Weir[k].slope * pow(h, 2.5); break; case TRAPEZOIDAL_WEIR: y = (1.0 - Link[j].setting) * Link[j].xsect.yFull; length = xsect_getWofY(&Link[j].xsect, y) * UCF(LENGTH); - *q1 = cDisch1 * length * pow(h, 1.5); //(5.1.013) + *q1 = cDisch1 * length * pow(h, 1.5); *q2 = Weir[k].cDisch2 * Weir[k].slope * pow(h, 2.5); } diff --git a/swmm/swmm5/swmm5/macros.h b/swmm/swmm5/swmm5/macros.h index 6f10ae4..c15749e 100644 --- a/swmm/swmm5/swmm5/macros.h +++ b/swmm/swmm5/swmm5/macros.h @@ -2,8 +2,8 @@ // macros.h // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 03/20/07 (Build 5.1.001) +// Version: 5.2 +// Date: 11/01/21 (Build 5.2.0) // Author: L. Rossman //----------------------------------------------------------------------------- diff --git a/swmm/swmm5/swmm5/main.c b/swmm/swmm5/swmm5/main.c index 15f272b..0fe1b40 100644 --- a/swmm/swmm5/swmm5/main.c +++ b/swmm/swmm5/swmm5/main.c @@ -2,11 +2,11 @@ // main.c // // Project: EPA SWMM5 -// Version: 5.1 -// Date: 05/10/2018 +// Version: 5.2 +// Date: 03/24/2021 // Author: L. Rossman -// Main stub for the command line version of EPA SWMM 5.1 +// Main stub for the command line version of EPA SWMM 5.2 // to be run with swmm5.dll. #include @@ -19,9 +19,9 @@ int main(int argc, char *argv[]) // Input: argc = number of command line arguments // argv = array of command line arguments // Output: returns error status -// Purpose: runs the command line version of EPA SWMM 5.1. +// Purpose: runs the command line version of EPA SWMM 5.2. // -// Command line is: swmm5 f1 f2 f3 +// Command line is: runswmm f1 f2 f3 // where f1 = name of input file, f2 = name of report file, and // f3 = name of binary output file if saved (or blank if not saved). // @@ -56,12 +56,12 @@ int main(int argc, char *argv[]) if (strcmp(arg1, "--help") == 0 || strcmp(arg1, "-h") == 0) { // Help - printf("\n\nSTORMWATER MANAGEMENT MODEL (SWMM5) HELP\n\n"); + printf("\n\nSTORMWATER MANAGEMENT MODEL (SWMM) HELP\n\n"); printf("COMMANDS:\n"); - printf("\t--help (-h) Help Docs\n"); + printf("\t--help (-h) SWMM Help\n"); printf("\t--version (-v) Build Version\n"); printf("\nRUNNING A SIMULATION:\n"); - printf("\t swmm5