From d401acfd721405f17fe053d1016a00bf1cb032cb Mon Sep 17 00:00:00 2001 From: Caleb Date: Fri, 24 Jan 2025 05:10:05 -0500 Subject: [PATCH] WIP API and bindings unit testing; cleaning up; docs, csv support #204, #162, #163, #184, #180, #165, #31 --- .../workflows/build-and-regression-test.yml | 88 +- .github/workflows/build-and-unit-test.yml | 11 +- README.md | 22 +- python/MANIFEST.in | 12 +- python/epaswmm/output/_output.pyi | 34 +- python/epaswmm/output/_output.pyx | 86 +- python/epaswmm/solver/_solver.pyx | 4 +- python/setup.py | 8 +- .../tests/data/output/json_time_series.pickle | Bin 4110 -> 4122 bytes python/tests/test_swmm_solver.py | 35 +- python/tests/test_swwm_output.py | 557 ++-- src/outfile/errormanager.c | 6 +- src/outfile/errormanager.h | 6 +- src/outfile/include/swmm_output.h | 21 +- src/outfile/include/swmm_output_enums.h | 6 +- src/outfile/messages.h | 21 +- src/outfile/swmm_output.c | 261 +- src/run/main.c | 45 +- src/solver/climate.c | 935 +++--- src/solver/consts.h | 380 ++- src/solver/controls.c | 2327 +++++++++------ src/solver/datetime.h | 213 +- src/solver/dwflow.c | 101 +- src/solver/dynwave.c | 44 +- src/solver/enums.h | 1716 +++++++---- src/solver/error.h | 164 +- src/solver/exfil.h | 93 +- src/solver/findroot.c | 8 +- src/solver/findroot.h | 59 +- src/solver/flowrout.c | 14 +- src/solver/funcs.h | 2518 +++++++++++++---- src/solver/gage.c | 244 +- src/solver/gwater.c | 193 +- src/solver/include/swmm5.h | 507 +++- src/solver/inflow.c | 167 +- src/solver/kinwave.c | 56 +- src/solver/landuse.c | 147 +- src/solver/massbal.c | 216 +- src/solver/node.c | 373 ++- src/solver/output.c | 92 +- src/solver/project.c | 59 +- src/solver/qualrout.c | 22 +- src/solver/rdii.c | 87 +- src/solver/routing.c | 8 +- src/solver/runoff.c | 16 +- src/solver/snow.c | 187 +- src/solver/stats.c | 207 +- src/solver/subcatch.c | 485 ++-- src/solver/surfqual.c | 155 +- src/solver/swmm5.c | 751 +++-- src/solver/table.c | 43 +- src/solver/toposort.c | 10 +- src/solver/treatmnt.c | 82 +- 53 files changed, 9286 insertions(+), 4616 deletions(-) diff --git a/.github/workflows/build-and-regression-test.yml b/.github/workflows/build-and-regression-test.yml index 6c238896e..858e10590 100644 --- a/.github/workflows/build-and-regression-test.yml +++ b/.github/workflows/build-and-regression-test.yml @@ -1,4 +1,4 @@ -name: Build and Regression Test +name: Build and Unit Test on: push: @@ -6,9 +6,89 @@ on: # pull_request: # branches: [ master, develop, release ] +env: + OMP_NUM_THREADS: 1 + BUILD_HOME: build + TEST_HOME: nrtests + PACKAGE_NAME: vcpkg-export-20220826-200052.1.0.0 + PKG_NAME: vcpkg-export-20220826-200052 + jobs: - build: - runs-on: ubuntu-latest + unit_test: + name: Build and unit test + runs-on: windows-2019 + environment: testing + defaults: + run: + shell: cmd + + steps: + - name: Checkout repo + uses: actions/checkout@v3 + + - name: Install boost-test + env: + REMOTE_STORE: "https://nuget.pkg.github.com/michaeltryby/index.json" + USERNAME: michaeltryby + run: | + nuget sources add -Name github -Source ${{ env.REMOTE_STORE }} -Username ${{ env.USERNAME }} -Password ${{ secrets.ACCESS_TOKEN }} + nuget install ${{env.PKG_NAME}} -Source github + + - name: Build + env: + TOOL_CHAIN_PATH: \scripts\buildsystems\vcpkg.cmake + run: | + cmake -B .\build -DBUILD_TESTS=ON -DCMAKE_TOOLCHAIN_FILE=.\${{env.PACKAGE_NAME}}${{env.TOOL_CHAIN_PATH}} . + cmake --build .\build --config DEBUG + + - name: Unit Test + run: ctest --test-dir .\build -C Debug --output-on-failure + + + reg_test: + name: Build and reg test + runs-on: windows-2019 + defaults: + run: + shell: cmd + working-directory: ci-tools/windows steps: - - uses: actions/checkout@v3 + - name: Checkout swmm repo + uses: actions/checkout@v3 + + - name: Checkout ci-tools repo + uses: actions/checkout@v3 + with: + repository: michaeltryby/ci-tools + ref: master + path: ci-tools + + - name: Setup python + uses: actions/setup-python@v4 + with: + python-version: '3.11' + + - name: Install requirements + run: | + python -m pip install --upgrade pip + python -m pip install -r requirements-swmm.txt + + - name: Build + run: make.cmd /g "Visual Studio 16 2019" + + - name: Before reg test + env: + NRTESTS_URL: https://github.com/USEPA/swmm-nrtestsuite + BENCHMARK_TAG: v2.5.0-dev + run: before-nrtest.cmd ${{ env.BENCHMARK_TAG }} + + - name: Run reg test + run: run-nrtests.cmd %GITHUB_RUN_ID%_%GITHUB_RUN_NUMBER% + + - name: Upload artifacts + if: ${{ always() }} + uses: actions/upload-artifact@v3 + with: + name: build-test-artifacts + path: upload/*.* diff --git a/.github/workflows/build-and-unit-test.yml b/.github/workflows/build-and-unit-test.yml index 858e10590..cdba2e532 100644 --- a/.github/workflows/build-and-unit-test.yml +++ b/.github/workflows/build-and-unit-test.yml @@ -14,14 +14,17 @@ env: PKG_NAME: vcpkg-export-20220826-200052 jobs: - unit_test: - name: Build and unit test + engine_unit_test: + name: Build and unit test computational engine + strategy: + matrix: + os: [windows-latest, ubuntu-latest, macos-13, macos-latest] + python-version: [3.11] runs-on: windows-2019 environment: testing defaults: run: shell: cmd - steps: - name: Checkout repo uses: actions/checkout@v3 @@ -45,7 +48,7 @@ jobs: run: ctest --test-dir .\build -C Debug --output-on-failure - reg_test: + python_binding_unit_test: name: Build and reg test runs-on: windows-2019 defaults: diff --git a/README.md b/README.md index 40087e2db..b60e7095b 100644 --- a/README.md +++ b/README.md @@ -46,40 +46,38 @@ using CMake and the Microsoft Visual Studio C compiler on Windows: Readme file resides (which should have 'src' as a sub-directory underneath it). -2. Issue the following command to create the directory for storing the built binaries: +2. Use the following command to create the directory for storing the built binaries: ```bash mkdir build -cd build ``` -3. Then enter the following CMake commands to build the binaries: +3. Then the following CMake commands to build the binaries: ``` bash -cmake -G .. -A +cmake -G .. -A -B .\build cmake --build . --config Release ``` -where `` is the name of the Visual Studio compiler being used +where `` is the name of the compiler being used in double quotes (e.g., "Visual Studio 15 2017", "Visual Studio 16 2019", -or "Visual Studio 17 2022") and `` is Win32 for a 32-bit build -or x64 for a 64-bit build. The resulting engine DLL (swmm5.dll), command -line executable (runswmm.exe), and output processing libraries (swmm-output.dll) +or "Visual Studio 17 2022") and `` (e.g., Win32 for a 32-bit build +or x64 for a 64-bit build). The resulting engine shared libraries (i.e., swmm5.dll), command line executable (i.e., runswmm.exe), and output processing libraries (i.e., swmm-output.dll) will appear in the build\Release directory. For other platforms, such as Linux or MacOS, Step 3 can be replaced with: ```bash -cmake .. -cmake --build . +cmake . +cmake --build .\build ``` The resulting shared object library (libswmm5.so or libswmm5.dylib) and -command line executable (runswmm) will appear in the build directory. +command line executable (runswmm) will be compiled to the build directory. ### Python Bindings (Experimental) -Experimental python bindings for the SWMM API are being developed to support regression and benchmark testing as well as other applications. _**These bindings are still under development and testing and has yet to be cleared through US EPA ORD's official quality assurance review process**_. The exprimental python bindings can be built and installed locally using the following command. +Experimental python bindings for the SWMM API are being developed to support regression and benchmark testing as well as for other applications. _**These bindings are still under development and testing and has yet to be cleared through US EPA ORD's official quality assurance review process**_. The exprimental python bindings can be built and installed locally using the following command. ```bash cd python diff --git a/python/MANIFEST.in b/python/MANIFEST.in index d8d8be237..6993907f4 100644 --- a/python/MANIFEST.in +++ b/python/MANIFEST.in @@ -1,11 +1,11 @@ include LICENSE include README.md -recursive-include *.py -recursive-include *.txt -recursive-include *.md -recursive-include *.pyi -recursive-include *.inp -recursive-include *.pickle +recursive-include . *.py +recursive-include . *.txt +recursive-include . *.md +recursive-include . *.pyi +recursive-include . *.inp +recursive-include . *.pickle recursive-include docs *.rst recursive-include tests *.csv global-exclude *.pyc \ No newline at end of file diff --git a/python/epaswmm/output/_output.pyi b/python/epaswmm/output/_output.pyi index 538b09463..538dcbc01 100644 --- a/python/epaswmm/output/_output.pyi +++ b/python/epaswmm/output/_output.pyi @@ -267,13 +267,13 @@ class Output: def __init__(self, output_file: str) -> None: """ - Constructor to initialize the SWMM output file instance. + Constructor to open the SWMM output file. :param output_file: Path to the SWMM output file. :type output_file: str """ ... - + def __enter__(self): # -> Self@Output: """ Method to return the SWMM output file instance. @@ -356,7 +356,7 @@ class Output: """ ... - def get_time_attribute(self, time_atrribute: int) -> int: + def get_time_attribute(self, time_attribute: TimeAttribute) -> int: """ Method to get the temporal attributes of the simulation in the SWMM output file. @@ -365,7 +365,7 @@ class Output: """ ... - def get_element_name(self, element_type: int, element_index: int) -> str: + def get_element_name(self, element_type: ElementType, element_index: int) -> str: """ Method to get the name of an element in the SWMM output file. @@ -378,7 +378,7 @@ class Output: """ ... - def get_element_names(self, element_type: int) -> list: + def get_element_names(self, element_type: ElementType) -> List[str]: """ Method to get the names of all elements of a given type in the SWMM output file. @@ -389,7 +389,7 @@ class Output: """ ... - def get_subcatchment_timeseries(self, element_index: int, attribute: int, start_date_index: int = ..., end_date_index: int = ...) -> dict: + def get_subcatchment_timeseries(self, element_index: int, attribute: SubcatchAttribute, start_date_index: int = ..., end_date_index: int = ...) -> Dict[datetime, float]: """ Method to get the time series data for a subcatchment attribute in the SWMM output file. @@ -407,7 +407,7 @@ class Output: """ ... - def get_node_timeseries(self, element_index: int, attribute: int, start_date_index: int = ..., end_date_index: int = ...) -> dict: + def get_node_timeseries(self, element_index: int, attribute: NodeAttribute, start_date_index: int = ..., end_date_index: int = ...) -> Dict[datetime, float]: """ Method to get the time series data for a node attribute in the SWMM output file. @@ -424,7 +424,7 @@ class Output: """ ... - def get_link_timeseries(self, element_index: int, attribute: int, start_date_index: int = ..., end_date_index: int = ...) -> dict: + def get_link_timeseries(self, element_index: int, attribute: LinkAttribute, start_date_index: int = ..., end_date_index: int = ...) -> Dict[datetime, float]: """ Method to get the time series data for a link attribute in the SWMM output file. @@ -441,7 +441,7 @@ class Output: """ ... - def get_system_timeseries(self, attribute: int, start_date_index: int = ..., end_date_index: int = ...) -> dict: + def get_system_timeseries(self, attribute: SystemAttribute, start_date_index: int = ..., end_date_index: int = ...) -> Dict[datetime, float]: """ Method to get the time series data for a system attribute in the SWMM output file. @@ -456,7 +456,7 @@ class Output: """ ... - def get_subcatchment_values_by_time_and_attribute(self, time_index: int, attribute: int) -> dict: + def get_subcatchment_values_by_time_and_attribute(self, time_index: int, attribute: SubcatchAttribute) -> Dict[str, float]: """ Method to get the subcatchment values for all subcatchments for a given time index and attribute. @@ -469,7 +469,7 @@ class Output: """ ... - def get_node_values_by_time_and_attribute(self, time_index: int, attribute: int) -> dict: + def get_node_values_by_time_and_attribute(self, time_index: int, attribute: NodeAttribute) -> Dict[str, float]: """ Method to get the node values for all nodes for a given time index and attribute. @@ -482,7 +482,7 @@ class Output: """ ... - def get_link_values_by_time_and_attribute(self, time_index: int, attribute: int) -> dict: + def get_link_values_by_time_and_attribute(self, time_index: int, attribute: LinkAttribute) -> Dict[str, float]: """ Method to get the link values for all links for a given time index and attribute. @@ -495,7 +495,7 @@ class Output: """ ... - def get_system_values_by_time_and_attribute(self, time_index: int, attribute: int) -> dict: + def get_system_values_by_time_and_attribute(self, time_index: int, attribute: SystemAttribute) -> Dict[str, float]: """ Method to get the system values for a given time index and attribute. @@ -508,7 +508,7 @@ class Output: """ ... - def get_subcatchment_values_by_time_and_element_index(self, time_index: int, element_index: int) -> dict: + def get_subcatchment_values_by_time_and_element_index(self, time_index: int, element_index: int) -> Dict[str, float]: """ Method to get all attributes of a given subcatchment for specified time. @@ -521,7 +521,7 @@ class Output: """ ... - def get_node_values_by_time_and_element_index(self, time_index: int, element_index: int) -> dict: + def get_node_values_by_time_and_element_index(self, time_index: int, element_index: int) -> Dict[str, float]: """ Method to get all attributes of a given node for specified time. @@ -534,7 +534,7 @@ class Output: """ ... - def get_link_values_by_time_and_element_index(self, time_index: int, element_index: int) -> dict: + def get_link_values_by_time_and_element_index(self, time_index: int, element_index: int) -> Dict[str, float]: """ Method to get all attributes of a given link for specified time. @@ -548,7 +548,7 @@ class Output: """ ... - def get_system_values_by_time(self, time_index: int) -> dict: + def get_system_values_by_time(self, time_index: int) -> Dict[str, float]: """ Method to get all attributes of the system for specified time. diff --git a/python/epaswmm/output/_output.pyx b/python/epaswmm/output/_output.pyx index 834f3e2ad..8f85c2702 100644 --- a/python/epaswmm/output/_output.pyx +++ b/python/epaswmm/output/_output.pyx @@ -365,8 +365,8 @@ cdef class Output: self._output_size, self._output_size_length = self.__get_output_size() self._pollutant_units = [ConcentrationUnits(i) for i in self.__get_pollutant_units()] self._start_date = self.__get_start_date() - self._report_step = self.get_time_attribute(SMO_time.SMO_reportStep) - self._num_periods = self.get_time_attribute(SMO_time.SMO_numPeriods) + self._report_step = self.get_time_attribute(TimeAttribute.REPORT_STEP) + self._num_periods = self.get_time_attribute(TimeAttribute.NUM_PERIODS) self._times = [self._start_date + timedelta(seconds=self._report_step) * i for i in range(1, self._num_periods + 1)] def __enter__(self): @@ -584,7 +584,7 @@ cdef class Output: """ return self._times - cpdef int get_time_attribute(self, int time_atrribute): + def get_time_attribute(self, time_attribute: TimeAttribute) -> int: """ Method to get the temporal attributes of the simulation in the SWMM output file. @@ -593,14 +593,13 @@ cdef class Output: """ cdef int error_code = 0 cdef int temporal_attribute = -1 - cdef SMO_time SMO_time_atrribute = time_atrribute - error_code = SMO_getTimes(self._output_file_handle, SMO_time_atrribute, &temporal_attribute) + error_code = SMO_getTimes(self._output_file_handle, time_attribute.value, &temporal_attribute) self.__validate_error_code(error_code) return temporal_attribute - cpdef str get_element_name(self, int element_type, int element_index): + def get_element_name(self, element_type: ElementType, element_index: int) -> str: """ Method to get the name of an element in the SWMM output file. @@ -614,9 +613,8 @@ cdef class Output: cdef int error_code = 0 cdef int strlen = 0 cdef char* element_name = NULL - cdef SMO_elementType SMO_element_type = element_type - error_code = SMO_getElementName(self._output_file_handle, SMO_element_type, element_index, &element_name, &strlen) + error_code = SMO_getElementName(self._output_file_handle, element_type.value, element_index, &element_name, &strlen) self.__validate_error_code(error_code) # Convert the C string to a Python string and delete the C string @@ -627,7 +625,7 @@ cdef class Output: return element_name_str - cpdef list get_element_names(self, int element_type): + def get_element_names(self, element_type: ElementType) -> List[str]: """ Method to get the names of all elements of a given type in the SWMM output file. @@ -642,19 +640,18 @@ cdef class Output: cdef int strlen = 0 cdef char** c_element_names = NULL cdef list element_names - cdef SMO_elementType SMO_element_type = element_type - if element_type == SMO_elementType.SMO_sys: + if element_type.value == SMO_elementType.SMO_sys: raise SWMMOutputException(f"Cannot get element names for the system element type {ElementType.SYSTEM}.") - elif element_type > SMO_elementType.SMO_pollut: + elif element_type.value > SMO_elementType.SMO_pollut: raise SWMMOutputException("Invalid element type.") - num_elements = self._output_size[element_type] + num_elements = self._output_size[element_type.value] c_element_names = malloc(num_elements * sizeof(char*)) for i in range(num_elements): - error_code = SMO_getElementName(self._output_file_handle, SMO_element_type, i, &c_element_names[i], &strlen) + error_code = SMO_getElementName(self._output_file_handle, element_type.value, i, &c_element_names[i], &strlen) self.__validate_error_code(error_code) element_names = [c_element_names[i].decode('utf-8') for i in range(num_elements)] @@ -669,7 +666,7 @@ cdef class Output: return element_names - cpdef dict get_subcatchment_timeseries(self, int element_index, int attribute, int start_date_index = 0, int end_date_index = -1): + def get_subcatchment_timeseries(self, element_index: int, attribute: SubcatchAttribute, start_date_index: int = 0, end_date_index: int = -1) -> Dict[datetime, float]: """ Method to get the time series data for a subcatchment attribute in the SWMM output file. @@ -695,7 +692,7 @@ cdef class Output: error_code = SMO_getSubcatchSeries( self._output_file_handle, element_index, - attribute, + attribute.value, start_date_index, end_date_index, &values, &length @@ -709,7 +706,7 @@ cdef class Output: return results - cpdef dict get_node_timeseries(self, int element_index, int attribute, int start_date_index = 0, int end_date_index = -1): + def get_node_timeseries(self, element_index: int, attribute: NodeAttribute, start_date_index: int = 0, end_date_index: int = -1) -> Dict[datetime, float]: """ Method to get the time series data for a node attribute in the SWMM output file. @@ -734,7 +731,7 @@ cdef class Output: error_code = SMO_getNodeSeries( self._output_file_handle, element_index, - attribute, + attribute.value, start_date_index, end_date_index, &values, &length @@ -749,7 +746,7 @@ cdef class Output: return results - cpdef dict get_link_timeseries(self, int element_index, int attribute, int start_date_index = 0, int end_date_index = -1): + def get_link_timeseries(self, element_index: int, attribute: LinkAttribute, start_date_index: int = 0, end_date_index: int = -1) -> Dict[datetime, float]: """ Method to get the time series data for a link attribute in the SWMM output file. @@ -774,7 +771,7 @@ cdef class Output: error_code = SMO_getLinkSeries( self._output_file_handle, element_index, - attribute, + attribute.value, start_date_index, end_date_index, &values, &length @@ -789,7 +786,7 @@ cdef class Output: return results - cpdef dict get_system_timeseries(self, int attribute, int start_date_index = 0, int end_date_index = -1): + def get_system_timeseries(self, attribute: SystemAttribute, start_date_index: int = 0, end_date_index: int = -1) -> Dict[datetime, float]: """ Method to get the time series data for a system attribute in the SWMM output file. @@ -811,7 +808,7 @@ cdef class Output: error_code = SMO_getSystemSeries( self._output_file_handle, - attribute, + attribute.value, start_date_index, end_date_index, &values, &length @@ -826,7 +823,7 @@ cdef class Output: return results - cpdef dict get_subcatchment_values_by_time_and_attribute(self, int time_index, int attribute): + def get_subcatchment_values_by_time_and_attribute(self, time_index: int, attribute: SubcatchAttribute) -> Dict[str, float]: """ Method to get the subcatchment values for all subcatchments for a given time index and attribute. @@ -845,21 +842,21 @@ cdef class Output: error_code = SMO_getSubcatchAttribute( self._output_file_handle, time_index, - attribute, + attribute.value, &values, &length ) self.__validate_error_code(error_code) - subcatchment_values = dict(zip(self.get_element_names(ElementType.SUBCATCHMENT.value), values)) + subcatchment_values = dict(zip(self.get_element_names(ElementType.SUBCATCHMENT), values)) if values != NULL: free(values) return subcatchment_values - cpdef dict get_node_values_by_time_and_attribute(self, int time_index, int attribute): + def get_node_values_by_time_and_attribute(self, time_index: int, attribute: NodeAttribute) -> Dict[str, float]: """ Method to get the node values for all nodes for a given time index and attribute. @@ -877,21 +874,22 @@ cdef class Output: error_code = SMO_getNodeAttribute( self._output_file_handle, - time_index, attribute, + time_index, + attribute.value, &values, &length ) self.__validate_error_code(error_code) - node_values = dict(zip(self.get_element_names(ElementType.NODE.value), values)) + node_values = dict(zip(self.get_element_names(ElementType.NODE), values)) if values != NULL: free(values) return node_values - cpdef dict get_link_values_by_time_and_attribute(self, int time_index, int attribute): + def get_link_values_by_time_and_attribute(self, time_index: int, attribute: LinkAttribute) -> Dict[str, float]: """ Method to get the link values for all links for a given time index and attribute. @@ -909,21 +907,22 @@ cdef class Output: error_code = SMO_getLinkAttribute( self._output_file_handle, - time_index, attribute, + time_index, + attribute.value, &values, &length - ) + ) self.__validate_error_code(error_code) - link_values = dict(zip(self.get_element_names(ElementType.LINK.value), values)) + link_values = dict(zip(self.get_element_names(ElementType.LINK), values)) if values != NULL: free(values) return link_values - cpdef dict get_system_values_by_time_and_attribute(self, int time_index, int attribute): + def get_system_values_by_time_and_attribute(self, time_index: int, attribute: SystemAttribute) -> Dict[str, float]: """ Method to get the system values for a given time index and attribute. @@ -941,14 +940,15 @@ cdef class Output: error_code = SMO_getSystemAttribute( self._output_file_handle, - time_index, attribute, + time_index, + attribute.value, &values, &length - ) + ) self.__validate_error_code(error_code) - system_values = dict(zip([SubcatchAttribute(attribute).name], values)) + system_values = dict(zip([SystemAttribute(attribute).name], values)) if values != NULL: @@ -956,7 +956,7 @@ cdef class Output: return system_values - cpdef dict get_subcatchment_values_by_time_and_element_index(self, int time_index, int element_index): + def get_subcatchment_values_by_time_and_element_index(self, time_index: int, element_index: int) -> Dict[str, float]: """ Method to get all attributes of a given subcatchment for specified time. @@ -971,7 +971,7 @@ cdef class Output: cdef float* values = NULL cdef int length = 0 cdef int enum_values_length = len(SubcatchAttribute) - 1 - cdef list pollutant_names = self.get_element_names(ElementType.POLLUTANT.value) + cdef list pollutant_names = self.get_element_names(ElementType.POLLUTANT) cdef list attrib_names = [] error_code = SMO_getSubcatchResult( @@ -996,7 +996,7 @@ cdef class Output: return subcatchment_values - cpdef dict get_node_values_by_time_and_element_index(self, int time_index, int element_index): + def get_node_values_by_time_and_element_index(self, time_index: int, element_index: int) -> Dict[str, float]: """ Method to get all attributes of a given node for specified time. @@ -1012,7 +1012,7 @@ cdef class Output: cdef float* values = NULL cdef int length = 0 cdef int enum_values_length = len(NodeAttribute) - 1 - cdef list pollutant_names = self.get_element_names(ElementType.POLLUTANT.value) + cdef list pollutant_names = self.get_element_names(ElementType.POLLUTANT) cdef list attrib_names = [] error_code = SMO_getNodeResult( @@ -1037,7 +1037,7 @@ cdef class Output: return node_values - cpdef dict get_link_values_by_time_and_element_index(self, int time_index, int element_index): + def get_link_values_by_time_and_element_index(self, time_index: int, element_index: int) -> Dict[str, float]: """ Method to get all attributes of a given link for specified time. @@ -1054,7 +1054,7 @@ cdef class Output: cdef float* values = NULL cdef int length = 0 cdef int enum_values_length = len(LinkAttribute) - 1 - cdef list pollutant_names = self.get_element_names(ElementType.POLLUTANT.value) + cdef list pollutant_names = self.get_element_names(ElementType.POLLUTANT) cdef list attrib_names = [] error_code = SMO_getLinkResult( @@ -1079,7 +1079,7 @@ cdef class Output: return link_values - cpdef dict get_system_values_by_time(self, int time_index): + def get_system_values_by_time(self, time_index: int) -> Dict[str, float]: """ Method to get all attributes of the system for specified time. diff --git a/python/epaswmm/solver/_solver.pyx b/python/epaswmm/solver/_solver.pyx index b190377bb..c868e9f84 100644 --- a/python/epaswmm/solver/_solver.pyx +++ b/python/epaswmm/solver/_solver.pyx @@ -1006,7 +1006,7 @@ cdef class Solver: SWMMSystemProperties ], index: int, - value: double, + value: float, sub_index: int = -1 ) -> None: """ @@ -1038,7 +1038,7 @@ cdef class Solver: ], index: int, sub_index: int = -1 - ) -> double: + ) -> float: """ Get a SWMM system property value. diff --git a/python/setup.py b/python/setup.py index 0dc8e2843..4fe4858bb 100644 --- a/python/setup.py +++ b/python/setup.py @@ -12,32 +12,32 @@ import shutil # third party imports -from skbuild import setup +from skbuild import setup # local imports platform_system = platform.system() - # Get the directory containing this file here = os.path.abspath(os.path.dirname(__file__)) # Read the README file shutil.copyfile( - os.path.join(here, '..\README.md'), + os.path.join(here, r'..\README.md'), os.path.join(here, 'README.md') ) with open(os.path.join(here, 'README.md'), encoding='utf-8') as f: long_description = f.read() + def get_version(): """ Get version from toolkit TODO: This should be revised to get version information from the toolkit """ - return "5.3.0.dev3" + return "5.3.0.dev5" if os.environ.get('EPASWMM_CMAKE_ARGS') is not None: diff --git a/python/tests/data/output/json_time_series.pickle b/python/tests/data/output/json_time_series.pickle index 4c10076b0d12056ba9e3ba83a6f684a1a903ecb0..7fc11c60ee4a5d4ec8420155176837e76dfbb98f 100644 GIT binary patch delta 41 xcmeBEn5Dqdz%rG8Bg-T1$?E)klfQ7!;OpTI3ib1MbBhmh3~`;ZxsRuT82}o%4V(Y~ delta 30 mcmbQG(5JxCz&e$ABg-RhmLSIv*U7KA7cphb-8_q@ff)dr8VYFu diff --git a/python/tests/test_swmm_solver.py b/python/tests/test_swmm_solver.py index f24d6e042..1988972d3 100644 --- a/python/tests/test_swmm_solver.py +++ b/python/tests/test_swmm_solver.py @@ -204,7 +204,6 @@ def test_solver_get_time_attributes(self): rpt_file=self.site_drainage_rpt, out_file=self.site_drainage_out ) as swmm_solver: - # Initialize the solver swmm_solver.initialize() @@ -222,7 +221,6 @@ def test_get_object_count(self): rpt_file=self.site_drainage_rpt, out_file=self.site_drainage_out ) as swmm_solver: - swmm_solver.initialize() num_raingages = swmm_solver.get_object_count(solver.SWMMObjects.RAIN_GAGE) @@ -279,7 +277,6 @@ def test_get_object_names(self): rpt_file=self.site_drainage_rpt, out_file=self.site_drainage_out, ) as swmm_solver: - swmm_solver.initialize() raingage_names = swmm_solver.get_object_names(solver.SWMMObjects.RAIN_GAGE) @@ -309,7 +306,6 @@ def test_get_object_index(self): rpt_file=self.site_drainage_rpt, out_file=self.site_drainage_out, ) as swmm_solver: - swmm_solver.initialize() rg_index = swmm_solver.get_object_index(solver.SWMMObjects.RAIN_GAGE, 'RainGage') @@ -339,7 +335,6 @@ def test_get_gage_value(self): rpt_file=self.site_drainage_rpt, out_file=self.site_drainage_out, ) as swmm_solver: - swmm_solver.initialize() for t in range(12): @@ -364,7 +359,6 @@ def test_set_gage_value(self): rpt_file=self.site_drainage_rpt, out_file=self.site_drainage_out, ) as swmm_solver: - swmm_solver.initialize() swmm_solver.set_value( @@ -396,7 +390,6 @@ def test_get_subcatchment_value(self): rpt_file=self.site_drainage_rpt, out_file=self.site_drainage_out, ) as swmm_solver: - swmm_solver.initialize() for t in range(12): @@ -421,7 +414,6 @@ def test_set_subcatchment_value(self): rpt_file=self.site_drainage_rpt, out_file=self.site_drainage_out, ) as swmm_solver: - swmm_solver.initialize() swmm_solver.set_value( @@ -534,7 +526,7 @@ def test_set_link_value(self): ) as swmm_solver: swmm_solver.initialize() - error_code = swmm_solver.set_value( + swmm_solver.set_value( object_type=solver.SWMMObjects.LINK.value, property_type=solver.SWMMLinkProperties.OFFSET1.value, index=9, @@ -551,3 +543,28 @@ def test_set_link_value(self): ) self.assertAlmostEqual(link_value, 1.0) + + def test_run_banklick(self): + """ + Run the SWMM solver to solve the example input file + + :return: + """ + BANKLICK_EXAMPLE_INPUT_FILE = r'C:\Users\CBUAHIN\SourceCodes\DigitalWaterAnalytics\SERTO\tests\spatialswmm\swmm\test_model.inp' + if os.path.exists(self.site_drainage_rpt): + os.remove(self.site_drainage_rpt) + + if os.path.exists(self.site_drainage_out): + os.remove(self.site_drainage_out) + + error = solver.run_solver( + inp_file=BANKLICK_EXAMPLE_INPUT_FILE, + rpt_file=BANKLICK_EXAMPLE_INPUT_FILE.replace(".inp", ".rpt"), + out_file=BANKLICK_EXAMPLE_INPUT_FILE.replace(".inp", ".out"), + ) + + self.assertEqual(error, 0, "SWMM solver run successfully.") + + # Assert output and report files were created + self.assertTrue(os.path.exists(self.site_drainage_rpt)) + self.assertTrue(os.path.exists(self.site_drainage_out)) diff --git a/python/tests/test_swwm_output.py b/python/tests/test_swwm_output.py index 970fae30c..faa8e4283 100644 --- a/python/tests/test_swwm_output.py +++ b/python/tests/test_swwm_output.py @@ -31,7 +31,7 @@ def setUp(self): self.test_artifacts = {} - with open(example_output_data.JSON_TIME_SERIES_FILE, 'rb') as f: + with open(file=example_output_data.JSON_TIME_SERIES_FILE, mode='rb') as f: self.test_artifacts = pickle.load(f) def test_output_unit_system_enum(self): @@ -40,8 +40,13 @@ def test_output_unit_system_enum(self): :return: """ - self.assertEqual(output.UnitSystem.US.value, 0, "US unit system value should be 0") - self.assertEqual(output.UnitSystem.SI.value, 1, "SI unit system value should be 1") + self.assertEqual( + first=output.UnitSystem.US.value, second=0, msg="US unit system value should be 0" + ) + + self.assertEqual( + first=output.UnitSystem.SI.value, second=1, msg="SI unit system value should be 1" + ) def test_output_flow_units_enum(self): """ @@ -49,12 +54,24 @@ def test_output_flow_units_enum(self): :return: """ - self.assertEqual(output.FlowUnits.CFS.value, 0, "CFS flow unit value should be 0") - self.assertEqual(output.FlowUnits.GPM.value, 1, "GPM flow unit value should be 1") - self.assertEqual(output.FlowUnits.MGD.value, 2, "MGD flow unit value should be 2") - self.assertEqual(output.FlowUnits.CMS.value, 3, "CMS flow unit value should be 3") - self.assertEqual(output.FlowUnits.LPS.value, 4, "LPS flow unit value should be 4") - self.assertEqual(output.FlowUnits.MLD.value, 5, "MLD flow unit value should be 5") + self.assertEqual( + first=output.FlowUnits.CFS.value, second=0, msg="CFS flow unit value should be 0" + ) + self.assertEqual( + first=output.FlowUnits.GPM.value, second=1, msg="GPM flow unit value should be 1" + ) + self.assertEqual( + first=output.FlowUnits.MGD.value, second=2, msg="MGD flow unit value should be 2" + ) + self.assertEqual( + first=output.FlowUnits.CMS.value, second=3, msg="CMS flow unit value should be 3" + ) + self.assertEqual( + first=output.FlowUnits.LPS.value, second=4, msg="LPS flow unit value should be 4" + ) + self.assertEqual( + first=output.FlowUnits.MLD.value, second=5, msg="MLD flow unit value should be 5" + ) def test_output_concentration_units_enum(self): """ @@ -62,10 +79,18 @@ def test_output_concentration_units_enum(self): :return: """ - self.assertEqual(output.ConcentrationUnits.MG.value, 0, "MG concentration unit value should be 0") - self.assertEqual(output.ConcentrationUnits.UG.value, 1, "UG concentration unit value should be 1") - self.assertEqual(output.ConcentrationUnits.COUNT.value, 2, "COUNT concentration unit value should be 2") - self.assertEqual(output.ConcentrationUnits.NONE.value, 3, "NONE concentration unit value should be 3") + self.assertEqual( + first=output.ConcentrationUnits.MG.value, second=0, msg="MG concentration unit value should be 0" + ) + self.assertEqual( + first=output.ConcentrationUnits.UG.value, second=1, msg="UG concentration unit value should be 1" + ) + self.assertEqual( + first=output.ConcentrationUnits.COUNT.value, second=2, msg="COUNT concentration unit value should be 2" + ) + self.assertEqual( + first=output.ConcentrationUnits.NONE.value, second=3, msg="NONE concentration unit value should be 3" + ) def test_output_element_type_enum(self): """ @@ -73,11 +98,21 @@ def test_output_element_type_enum(self): :return: """ - self.assertEqual(output.ElementType.SUBCATCHMENT.value, 0, "SUBCATCHMENT element type value should be 0") - self.assertEqual(output.ElementType.NODE.value, 1, "NODE element type value should be 1") - self.assertEqual(output.ElementType.LINK.value, 2, "LINK element type value should be 2") - self.assertEqual(output.ElementType.SYSTEM.value, 3, "SYSTEM element type value should be 3") - self.assertEqual(output.ElementType.POLLUTANT.value, 4, "POLLUTANT element type value should be 4") + self.assertEqual( + first=output.ElementType.SUBCATCHMENT.value, second=0, msg="SUBCATCHMENT element type value should be 0" + ) + self.assertEqual( + first=output.ElementType.NODE.value, second=1, msg="NODE element type value should be 1" + ) + self.assertEqual( + first=output.ElementType.LINK.value, second=2, msg="LINK element type value should be 2" + ) + self.assertEqual( + first=output.ElementType.SYSTEM.value, second=3, msg="SYSTEM element type value should be 3" + ) + self.assertEqual( + first=output.ElementType.POLLUTANT.value, second=4, msg="POLLUTANT element type value should be 4" + ) def test_output_time_enum(self): """ @@ -85,33 +120,55 @@ def test_output_time_enum(self): :return: """ - self.assertEqual(output.TimeAttribute.REPORT_STEP.value, 0, "REPORT_STEP time value should be 0") - self.assertEqual(output.TimeAttribute.NUM_PERIODS.value, 1, "NUM_PERIODS time value should be 1") + self.assertEqual( + first=output.TimeAttribute.REPORT_STEP.value, second=0, msg="REPORT_STEP time value should be 0" + ) + self.assertEqual( + first=output.TimeAttribute.NUM_PERIODS.value, second=1, msg="NUM_PERIODS time value should be 1" + ) - def test_output_subcatch_attribute_enum(self): + def test_output_sub_catch_attribute_enum(self): """ - Test the output subcatchment attribute enum + Test the output sub-catchment attribute enum :return: """ - self.assertEqual(output.SubcatchAttribute.RAINFALL.value, 0, - "RAINFALL subcatchment attribute value should be 0") - self.assertEqual(output.SubcatchAttribute.SNOW_DEPTH.value, 1, - "SNOW_DEPTH subcatchment attribute value should be 1") - self.assertEqual(output.SubcatchAttribute.EVAPORATION_LOSS.value, 2, - "EVAPORATION_LOSS subcatchment attribute value should be 2") - self.assertEqual(output.SubcatchAttribute.INFILTRATION_LOSS.value, 3, - "INFILTRATION_LOSS subcatchment attribute value should be 3") - self.assertEqual(output.SubcatchAttribute.RUNOFF_RATE.value, 4, - "RUNOFF_RATE subcatchment attribute value should be 4") - self.assertEqual(output.SubcatchAttribute.GROUNDWATER_OUTFLOW.value, 5, - "GROUNDWATER_OUTFLOW subcatchment attribute value should be 5") - self.assertEqual(output.SubcatchAttribute.GROUNDWATER_TABLE_ELEVATION.value, 6, - "GROUNTWATER_TABLE subcatchment attribute value should be 6") - self.assertEqual(output.SubcatchAttribute.SOIL_MOISTURE.value, 7, - "SOIL_MOISTURE subcatchment attribute value should be 7") - self.assertEqual(output.SubcatchAttribute.POLLUTANT_CONCENTRATION.value, 8, - "POLLUTANT_CONCENTRATION subcatchment attribute value should be 8") + self.assertEqual( + first=output.SubcatchAttribute.RAINFALL.value, second=0, + msg="RAINFALL sub-catchment attribute value should be 0" + ) + self.assertEqual( + first=output.SubcatchAttribute.SNOW_DEPTH.value, second=1, + msg="SNOW_DEPTH sub-catchment attribute value should be 1" + ) + self.assertEqual( + first=output.SubcatchAttribute.EVAPORATION_LOSS.value, second=2, + msg="EVAPORATION_LOSS sub-catchment attribute value should be 2" + ) + self.assertEqual( + first=output.SubcatchAttribute.INFILTRATION_LOSS.value, second=3, + msg="INFILTRATION_LOSS sub-catchment attribute value should be 3" + ) + self.assertEqual( + first=output.SubcatchAttribute.RUNOFF_RATE.value, second=4, + msg="RUNOFF_RATE sub-catchment attribute value should be 4" + ) + self.assertEqual( + first=output.SubcatchAttribute.GROUNDWATER_OUTFLOW.value, second=5, + msg="GROUNDWATER_OUTFLOW sub-catchment attribute value should be 5" + ) + self.assertEqual( + first=output.SubcatchAttribute.GROUNDWATER_TABLE_ELEVATION.value, second=6, + msg="GROUNDWATER_TABLE sub-catchment attribute value should be 6" + ) + self.assertEqual( + first=output.SubcatchAttribute.SOIL_MOISTURE.value, second=7, + msg="SOIL_MOISTURE sub-catchment attribute value should be 7" + ) + self.assertEqual( + first=output.SubcatchAttribute.POLLUTANT_CONCENTRATION.value, second=8, + msg="POLLUTANT_CONCENTRATION sub-catchment attribute value should be 8" + ) def test_output_node_attribute_enum(self): """ @@ -119,17 +176,34 @@ def test_output_node_attribute_enum(self): :return: """ - self.assertEqual(output.NodeAttribute.INVERT_DEPTH.value, 0, "INVERT_DEPTH node attribute value should be 0") - self.assertEqual(output.NodeAttribute.HYDRAULIC_HEAD.value, 1, - "HYDRAULIC_HEAD node attribute value should be 1") - self.assertEqual(output.NodeAttribute.STORED_VOLUME.value, 2, "STORED_VOLUME node attribute value should be 2") - self.assertEqual(output.NodeAttribute.LATERAL_INFLOW.value, 3, - "LATERAL_INFLOW node attribute value should be 3") - self.assertEqual(output.NodeAttribute.TOTAL_INFLOW.value, 4, "TOTAL_INFLOW node attribute value should be 4") - self.assertEqual(output.NodeAttribute.FLOODING_LOSSES.value, 5, - "FLOODING_LOSSES node attribute value should be 5") - self.assertEqual(output.NodeAttribute.POLLUTANT_CONCENTRATION.value, 6, - "POLLUTANT_CONCENTRATION node attribute value should be 6") + self.assertEqual( + first=output.NodeAttribute.INVERT_DEPTH.value, second=0, + msg="INVERT_DEPTH node attribute value should be 0" + ) + self.assertEqual( + first=output.NodeAttribute.HYDRAULIC_HEAD.value, second=1, + msg="HYDRAULIC_HEAD node attribute value should be 1" + ) + self.assertEqual( + first=output.NodeAttribute.STORED_VOLUME.value, second=2, + msg="STORED_VOLUME node attribute value should be 2" + ) + self.assertEqual( + first=output.NodeAttribute.LATERAL_INFLOW.value, second=3, + msg="LATERAL_INFLOW node attribute value should be 3" + ) + self.assertEqual( + first=output.NodeAttribute.TOTAL_INFLOW.value, second=4, + msg="TOTAL_INFLOW node attribute value should be 4" + ) + self.assertEqual( + first=output.NodeAttribute.FLOODING_LOSSES.value, second=5, + msg="FLOODING_LOSSES node attribute value should be 5" + ) + self.assertEqual( + first=output.NodeAttribute.POLLUTANT_CONCENTRATION.value, second=6, + msg="POLLUTANT_CONCENTRATION node attribute value should be 6" + ) def test_output_link_attribute_enum(self): """ @@ -137,13 +211,30 @@ def test_output_link_attribute_enum(self): :return: """ - self.assertEqual(output.LinkAttribute.FLOW_RATE.value, 0, "FLOW_RATE link attribute value should be 0") - self.assertEqual(output.LinkAttribute.FLOW_DEPTH.value, 1, "FLOW_DEPTH link attribute value should be 1") - self.assertEqual(output.LinkAttribute.FLOW_VELOCITY.value, 2, "FLOW_VELOCITY link attribute value should be 2") - self.assertEqual(output.LinkAttribute.FLOW_VOLUME.value, 3, "FLOW_VOLUME link attribute value should be 3") - self.assertEqual(output.LinkAttribute.CAPACITY.value, 4, "CAPACITY link attribute value should be 4") - self.assertEqual(output.LinkAttribute.POLLUTANT_CONCENTRATION.value, 5, - "POLLUTANT_CONCENTRATION link attribute value should be 5") + self.assertEqual( + first=output.LinkAttribute.FLOW_RATE.value, second=0, + msg="FLOW_RATE link attribute value should be 0" + ) + self.assertEqual( + first=output.LinkAttribute.FLOW_DEPTH.value, second=1, + msg="FLOW_DEPTH link attribute value should be 1" + ) + self.assertEqual( + first=output.LinkAttribute.FLOW_VELOCITY.value, second=2, + msg="FLOW_VELOCITY link attribute value should be 2" + ) + self.assertEqual( + first=output.LinkAttribute.FLOW_VOLUME.value, second=3, + msg="FLOW_VOLUME link attribute value should be 3" + ) + self.assertEqual( + first=output.LinkAttribute.CAPACITY.value, second=4, + msg="CAPACITY link attribute value should be 4" + ) + self.assertEqual( + first=output.LinkAttribute.POLLUTANT_CONCENTRATION.value, second=5, + msg="POLLUTANT_CONCENTRATION link attribute value should be 5" + ) def test_output_system_attribute_enum(self): """ @@ -151,47 +242,80 @@ def test_output_system_attribute_enum(self): :return: """ - self.assertEqual(output.SystemAttribute.AIR_TEMP.value, 0, "AIR_TEMP system attribute value should be 0") - self.assertEqual(output.SystemAttribute.RAINFALL.value, 1, "RAINFALL system attribute value should be 1") - self.assertEqual(output.SystemAttribute.SNOW_DEPTH.value, 2, "SNOW_DEPTH system attribute value should be 2") - self.assertEqual(output.SystemAttribute.EVAP_INFIL_LOSS.value, 3, - "EVAP_INFIL_LOSS system attribute value should be 3") - self.assertEqual(output.SystemAttribute.RUNOFF_FLOW.value, 4, "RUNOFF_FLOW system attribute value should be 4") - self.assertEqual(output.SystemAttribute.DRY_WEATHER_INFLOW.value, 5, - "DRY_WEATHER_INFLOW system attribute value should be 5") - self.assertEqual(output.SystemAttribute.GROUNDWATER_INFLOW.value, 6, - "GROUNDWATER_INFLOW system attribute value should be 6") - self.assertEqual(output.SystemAttribute.RDII_INFLOW.value, 7, "RDII_INFLOW system attribute value should be 7") - self.assertEqual(output.SystemAttribute.DIRECT_INFLOW.value, 8, - "DIRECT_INFLOW system attribute value should be 8") - self.assertEqual(output.SystemAttribute.TOTAL_LATERAL_INFLOW.value, 9, - "TOTAL_LATERAL_INFLOW system attribute value should be 9") - self.assertEqual(output.SystemAttribute.FLOOD_LOSSES.value, 10, - "FLOOD_LOSSES system attribute value should be 10") - self.assertEqual(output.SystemAttribute.OUTFALL_FLOWS.value, 11, - "OUTFALL_FLOWS system attribute value should be 11") - self.assertEqual(output.SystemAttribute.VOLUME_STORED.value, 12, - "VOLUME_STORED system attribute value should be 12") - self.assertEqual(output.SystemAttribute.EVAPORATION_RATE.value, 13, - "EVAPORATION_RATE system attribute value should be 13") + self.assertEqual( + first=output.SystemAttribute.AIR_TEMP.value, second=0, + msg="AIR_TEMP system attribute value should be 0" + ) + self.assertEqual( + first=output.SystemAttribute.RAINFALL.value, second=1, + msg="RAINFALL system attribute value should be 1" + ) + self.assertEqual( + first=output.SystemAttribute.SNOW_DEPTH.value, second=2, + msg="SNOW_DEPTH system attribute value should be 2" + ) + self.assertEqual( + first=output.SystemAttribute.EVAP_INFIL_LOSS.value, second=3, + msg="EVAP_INFIL_LOSS system attribute value should be 3" + ) + self.assertEqual( + first=output.SystemAttribute.RUNOFF_FLOW.value, second=4, + msg="RUNOFF_FLOW system attribute value should be 4" + ) + self.assertEqual( + first=output.SystemAttribute.DRY_WEATHER_INFLOW.value, second=5, + msg="DRY_WEATHER_INFLOW system attribute value should be 5" + ) + self.assertEqual( + first=output.SystemAttribute.GROUNDWATER_INFLOW.value, second=6, + msg="GROUNDWATER_INFLOW system attribute value should be 6" + ) + self.assertEqual( + first=output.SystemAttribute.RDII_INFLOW.value, second=7, + msg="RDII_INFLOW system attribute value should be 7" + ) + self.assertEqual( + first=output.SystemAttribute.DIRECT_INFLOW.value, second=8, + msg="DIRECT_INFLOW system attribute value should be 8" + ) + self.assertEqual( + first=output.SystemAttribute.TOTAL_LATERAL_INFLOW.value, second=9, + msg="TOTAL_LATERAL_INFLOW system attribute value should be 9" + ) + self.assertEqual( + first=output.SystemAttribute.FLOOD_LOSSES.value, second=10, + msg="FLOOD_LOSSES system attribute value should be 10" + ) + self.assertEqual( + first=output.SystemAttribute.OUTFALL_FLOWS.value, second=11, + msg="OUTFALL_FLOWS system attribute value should be 11" + ) + self.assertEqual( + first=output.SystemAttribute.VOLUME_STORED.value, second=12, + msg="VOLUME_STORED system attribute value should be 12" + ) + self.assertEqual( + first=output.SystemAttribute.EVAPORATION_RATE.value, second=13, + msg="EVAPORATION_RATE system attribute value should be 13" + ) def test_output_open_and_close(self): """ Test the output open and close functions :return: """ - with Output(example_output_data.EXAMPLE_OUTPUT_FILE_1) as swmm_output: + with Output(output_file=example_output_data.EXAMPLE_OUTPUT_FILE_1) as swmm_output: pass - swmm_output = Output(example_output_data.EXAMPLE_OUTPUT_FILE_1) + swmm_output = Output(output_file=example_output_data.EXAMPLE_OUTPUT_FILE_1) def test_output_open_error(self): """ Test the output open error function :return: """ - with self.assertRaises(FileNotFoundError) as context: - swmm_output = Output(example_output_data.NON_EXISTENT_OUTPUT_FILE) + with self.assertRaises(expected_exception=FileNotFoundError) as context: + swmm_output = Output(output_file=example_output_data.NON_EXISTENT_OUTPUT_FILE) self.assertIn( member="Error opening the SWMM output file", @@ -204,37 +328,39 @@ def test_output_get_version(self): Test the output get version function :return: """ - swmm_output = Output(example_output_data.EXAMPLE_OUTPUT_FILE_1) + swmm_output = Output(output_file=example_output_data.EXAMPLE_OUTPUT_FILE_1) version = swmm_output.version - self.assertEqual(version, 51000, "Version should be 51000") + self.assertEqual(first=version, second=51000, msg="Version should be 51000") def test_output_get_size(self): """ Test the output get size function :return: """ - swmm_output = Output(example_output_data.EXAMPLE_OUTPUT_FILE_1) - size = swmm_output.output_size.values() - - self.assertListEqual(list(size), [8, 14, 13, 1, 2], "Size should be [8, 14, 13, 1, 2]") + swmm_output = Output(output_file=example_output_data.EXAMPLE_OUTPUT_FILE_1) + self.assertDictEqual( + d1={'subcatchments': 8, 'nodes': 14, 'links': 13, 'system': 1, 'pollutants': 2}, + d2=swmm_output.output_size, + msg="Output size should be {'subcatchments': 8, 'nodes': 14, 'links': 13, 'system': 1, 'pollutants': 2}" + ) def test_output_get_units(self): """ Test the output get units function :return: """ - swmm_output = Output(example_output_data.EXAMPLE_OUTPUT_FILE_1) + swmm_output = Output(output_file=example_output_data.EXAMPLE_OUTPUT_FILE_1) units = swmm_output.units self.assertListEqual( - list(units), - [ + list1=list(units), + list2 = [ output.UnitSystem.US, output.FlowUnits.CFS, [output.ConcentrationUnits.MG, output.ConcentrationUnits.UG] ], - "Units should be [US, CFS, [MG, UG]]" + msg="Units should be [US, CFS, [MG, UG]]" ) def test_output_get_flow_units(self): @@ -242,93 +368,125 @@ def test_output_get_flow_units(self): Test the output get flow units function :return: """ - swmm_output = Output(example_output_data.EXAMPLE_OUTPUT_FILE_1) + swmm_output = Output(output_file=example_output_data.EXAMPLE_OUTPUT_FILE_1) flow_units = swmm_output.flow_units - self.assertEqual(flow_units, output.FlowUnits.CFS, "Flow units should be CFS") + self.assertEqual( + first=flow_units, second=output.FlowUnits.CFS, msg="Flow units should be CFS" + ) def test_output_get_start_date(self): """ Test the output get start date function :return: """ - swmm_output = Output(example_output_data.EXAMPLE_OUTPUT_FILE_1) + swmm_output = Output(output_file=example_output_data.EXAMPLE_OUTPUT_FILE_1) start_date = swmm_output.start_date - self.assertEqual(start_date, datetime(year=1998, month=1, day=1), "Start date should be 01/01/1998") + self.assertEqual( + first=start_date, + second=datetime(year=1998, month=1, day=1), + msg="Start date should be 01/01/1998" + ) def test_output_get_time_attributes(self): """ Test the output get temporal attributes function :return: """ - swmm_output = Output(example_output_data.EXAMPLE_OUTPUT_FILE_1) - report_step = swmm_output.get_time_attribute(output.TimeAttribute.REPORT_STEP.value) - num_periods = swmm_output.get_time_attribute(output.TimeAttribute.NUM_PERIODS.value) + swmm_output = Output(output_file=example_output_data.EXAMPLE_OUTPUT_FILE_1) + report_step = swmm_output.get_time_attribute(time_attribute=output.TimeAttribute.REPORT_STEP) + num_periods = swmm_output.get_time_attribute(time_attribute=output.TimeAttribute.NUM_PERIODS) - self.assertEqual(report_step, 3600, "Report step should be 300") - self.assertEqual(num_periods, 36, "Number of periods should be 365") + self.assertEqual( + first=report_step, second=3600, + msg="Report step should be 3600" + ) + self.assertEqual( + first=num_periods, second=36, + msg="Number of periods should be 365" + ) def test_output_get_element_name(self): """ Test the output get element names function :return: """ - swmm_output = Output(example_output_data.EXAMPLE_OUTPUT_FILE_1) + swmm_output = Output(output_file=example_output_data.EXAMPLE_OUTPUT_FILE_1) - retrieved_subcatch_names = [ - swmm_output.get_element_name(output.ElementType.SUBCATCHMENT.value, i) for i in range(8) + retrieved_sub_catch_names = [ + swmm_output.get_element_name( + element_type=output.ElementType.SUBCATCHMENT, element_index=i + ) + for i in range(8) ] - subcatch_names = ['1', '2', '3', '4', '5', '6', '7', '8'] + sub_catch_names = ['1', '2', '3', '4', '5', '6', '7', '8'] + self.assertListEqual( - retrieved_subcatch_names, subcatch_names, "Subcatchment names should be [1, 2, 3, 4, 5, 6, 7, 8]" + list1=retrieved_sub_catch_names, list2=sub_catch_names, + msg="Sub-catchment names should be [1, 2, 3, 4, 5, 6, 7, 8]" ) retrieved_node_names = [ - swmm_output.get_element_name(output.ElementType.NODE.value, i) for i in range(14) + swmm_output.get_element_name( + element_type=output.ElementType.NODE, element_index=i + ) + for i in range(14) ] - node_names = [ - '9', '10', '13', '14', '15', '16', '17', '19', '20', '21', '22', '23', '24', '18' - ] + node_names = ['9', '10', '13', '14', '15', '16', '17', '19', '20', '21', '22', '23', '24', '18'] + self.assertListEqual( - retrieved_node_names, node_names, - "Node names should be [9, 10, 13, 14, 15, 16, 17, 19, 20, 21, 22, 23, 24, 18]" + list1=retrieved_node_names, list2=node_names, + msg="Node names should be [9, 10, 13, 14, 15, 16, 17, 19, 20, 21, 22, 23, 24, 18]" ) retrieved_link_names = [ - swmm_output.get_element_name(output.ElementType.LINK.value, i) for i in range(13) + swmm_output.get_element_name( + element_type=output.ElementType.LINK, element_index=i + ) for i in range(13) ] + link_names = ['1', '4', '5', '6', '7', '8', '10', '11', '12', '13', '14', '15', '16'] self.assertListEqual( - retrieved_link_names, link_names, - "Link names should be [1, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 15, 16]" + list1=retrieved_link_names, list2=link_names, + msg="Link names should be [1, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 15, 16]" ) retrieved_pollutant_names = [ - swmm_output.get_element_name(output.ElementType.POLLUTANT.value, i) for i in range(2) + swmm_output.get_element_name( + element_type=output.ElementType.POLLUTANT, element_index=i + ) + for i in range(2) ] + pollutant_names = ['TSS', 'Lead'] - self.assertListEqual(retrieved_pollutant_names, pollutant_names, "Pollutant names should be [TSS, TSS]") + self.assertListEqual( + list1=retrieved_pollutant_names, list2=pollutant_names, + msg="Pollutant names should be [TSS, TSS]" + ) def test_get_element_name_errors(self): """ Test the output get element name error function :return: """ - swmm_output = Output(example_output_data.EXAMPLE_OUTPUT_FILE_1) + swmm_output = Output(output_file=example_output_data.EXAMPLE_OUTPUT_FILE_1) - with self.assertRaises(Exception) as context: - swmm_output.get_element_name(output.ElementType.SYSTEM.value, 0) + with self.assertRaises(expected_exception=Exception) as context: + swmm_output.get_element_name( + element_type=output.ElementType.SYSTEM, element_index=0 + ) self.assertIn( - member="nvalid parameter code", - container=str(context.exception), + member="invalid parameter code", container=str(context.exception), msg="Error message should be 'Invalid element type'" ) - with self.assertRaises(Exception) as context: - swmm_output.get_element_name(output.ElementType.SUBCATCHMENT.value, 8) + with self.assertRaises(expected_exception=Exception) as context: + swmm_output.get_element_name( + element_type=output.ElementType.SUBCATCHMENT, element_index=8 + ) self.assertIn( member="element index out of range", @@ -341,36 +499,42 @@ def test_output_get_element_names(self): Test the output get element names error function :return: """ - swmm_output = Output(example_output_data.EXAMPLE_OUTPUT_FILE_1) + swmm_output = Output(output_file=example_output_data.EXAMPLE_OUTPUT_FILE_1) + + retrieved_sub_catch_names = swmm_output.get_element_names(element_type=output.ElementType.SUBCATCHMENT) + sub_catch_names = ['1', '2', '3', '4', '5', '6', '7', '8'] - retrieved_subcatch_names = swmm_output.get_element_names(output.ElementType.SUBCATCHMENT.value) - subcatch_names = ['1', '2', '3', '4', '5', '6', '7', '8'] self.assertListEqual( - retrieved_subcatch_names, subcatch_names, "Subcatchment names should be [1, 2, 3, 4, 5, 6, 7, 8]" + list1=retrieved_sub_catch_names, + list2=sub_catch_names, + msg="Sub-catchment names should be [1, 2, 3, 4, 5, 6, 7, 8]" ) - retrieved_node_names = swmm_output.get_element_names(output.ElementType.NODE.value) + retrieved_node_names = swmm_output.get_element_names(element_type=output.ElementType.NODE) node_names = [ '9', '10', '13', '14', '15', '16', '17', '19', '20', '21', '22', '23', '24', '18' ] self.assertListEqual( - retrieved_node_names, node_names, - "Node names should be [9, 10, 13, 14, 15, 16, 17, 19, 20, 21, 22, 23, 24, 18]" + list1=retrieved_node_names, list2=node_names, + msg="Node names should be [9, 10, 13, 14, 15, 16, 17, 19, 20, 21, 22, 23, 24, 18]" ) - retrieved_link_names = swmm_output.get_element_names(output.ElementType.LINK.value) + retrieved_link_names = swmm_output.get_element_names(element_type=output.ElementType.LINK) link_names = ['1', '4', '5', '6', '7', '8', '10', '11', '12', '13', '14', '15', '16'] self.assertListEqual( - retrieved_link_names, link_names, - "Link names should be [1, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 15, 16]" + list1=retrieved_link_names, list2=link_names, + msg="Link names should be [1, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 15, 16]" ) - retrieved_pollutant_names = swmm_output.get_element_names(output.ElementType.POLLUTANT.value) + retrieved_pollutant_names = swmm_output.get_element_names(element_type=output.ElementType.POLLUTANT) pollutant_names = ['TSS', 'Lead'] - self.assertListEqual(retrieved_pollutant_names, pollutant_names, "Pollutant names should be [TSS, TSS]") + self.assertListEqual( + list1=retrieved_pollutant_names, list2=pollutant_names, + msg="Pollutant names should be [TSS, TSS]" + ) - with self.assertRaises(SWMMOutputException) as context: - swmm_output.get_element_names(output.ElementType.SYSTEM.value) + with self.assertRaises(expected_exception=SWMMOutputException) as context: + swmm_output.get_element_names(element_type=output.ElementType.SYSTEM) self.assertIn( member="Cannot get element names for the system element type", @@ -383,43 +547,40 @@ def test_get_times(self): Test the output get timeseries function :return: """ - swmm_output = Output(example_output_data.EXAMPLE_OUTPUT_FILE_1) + swmm_output = Output(output_file=example_output_data.EXAMPLE_OUTPUT_FILE_1) times = swmm_output.times - self.assertEqual(len(times), 36, "Number of times should be 36") + self.assertEqual(first=len(times), second=36, msg="Number of times should be 36") self.assertEqual( - times[0], - datetime(1998, 1, 1, 1, 0), - "First time should be 01/01/1998 01:00" + first=times[0], second=datetime(year=1998, month=1, day=1, hour=1), + msg="First time should be 01/01/1998 01:00" ) self.assertEqual( - times[16], - datetime(1998, 1, 1, 17, 0), - "Middle time should be 01/01/1998 14:00" + first=times[16], second=datetime(year=1998, month=1, day=1, hour=17), + msg="Middle time should be 01/01/1998 14:00" ) self.assertEqual( - times[-1], - datetime(1998, 1, 2, 12, 0), - "Last time should be 01/02/1998 12:00" + first=times[-1], second=datetime(year=1998, month=1, day=2, hour=12), + msg="Last time should be 01/02/1998 12:00" ) - def test_get_subcatchment_timeseries(self): + def test_get_sub_catchment_timeseries(self): """ - Test the output get subcatchment timeseries function + Test the output get sub-catchment timeseries function :return: """ - swmm_output = Output(example_output_data.EXAMPLE_OUTPUT_FILE_1) - subcatchment_timeseries = swmm_output.get_subcatchment_timeseries( + swmm_output = Output(output_file=example_output_data.EXAMPLE_OUTPUT_FILE_1) + sub_catchment_timeseries = swmm_output.get_subcatchment_timeseries( element_index=5, - attribute=output.SubcatchAttribute.RUNOFF_RATE.value, + attribute=output.SubcatchAttribute.RUNOFF_RATE, ) TestSWMMOutput.assert_dict_almost_equal( - subcatchment_timeseries, - self.test_artifacts['test_get_subcatchment_timeseries'], + d1=sub_catchment_timeseries, + d2=self.test_artifacts['test_get_subcatchment_timeseries'], ) def test_get_node_timeseries(self): @@ -427,15 +588,15 @@ def test_get_node_timeseries(self): Test the output get node timeseries function :return: """ - swmm_output = Output(example_output_data.EXAMPLE_OUTPUT_FILE_1) + swmm_output = Output(output_file=example_output_data.EXAMPLE_OUTPUT_FILE_1) node_timeseries = swmm_output.get_node_timeseries( element_index=7, - attribute=output.NodeAttribute.TOTAL_INFLOW.value, + attribute=output.NodeAttribute.TOTAL_INFLOW, ) TestSWMMOutput.assert_dict_almost_equal( - node_timeseries, - self.test_artifacts['test_get_node_timeseries'], + d1=node_timeseries, + d2=self.test_artifacts['test_get_node_timeseries'], ) def test_get_link_timeseries(self): @@ -443,10 +604,10 @@ def test_get_link_timeseries(self): Test the output get link timeseries function :return: """ - swmm_output = Output(example_output_data.EXAMPLE_OUTPUT_FILE_1) + swmm_output = Output(output_file=example_output_data.EXAMPLE_OUTPUT_FILE_1) link_timeseries = swmm_output.get_link_timeseries( element_index=5, - attribute=output.LinkAttribute.FLOW_RATE.value, + attribute=output.LinkAttribute.FLOW_RATE, ) TestSWMMOutput.assert_dict_almost_equal( @@ -461,7 +622,7 @@ def test_get_system_timeseries(self): """ swmm_output = Output(example_output_data.EXAMPLE_OUTPUT_FILE_1) system_timeseries = swmm_output.get_system_timeseries( - attribute=output.SystemAttribute.RUNOFF_FLOW.value + attribute=output.SystemAttribute.RUNOFF_FLOW ) TestSWMMOutput.assert_dict_almost_equal( @@ -469,19 +630,19 @@ def test_get_system_timeseries(self): self.test_artifacts['test_get_system_timeseries'], ) - def test_get_subcatchment_values_by_time_and_attributes(self): + def test_get_sub_catchment_values_by_time_and_attributes(self): """ - Test the output get subcatchment values by time and attributes function + Test the output get sub-catchment values by time and attributes function :return: """ swmm_output = Output(example_output_data.EXAMPLE_OUTPUT_FILE_1) - subcatchment_values = swmm_output.get_subcatchment_values_by_time_and_attribute( + sub_catchment_values = swmm_output.get_subcatchment_values_by_time_and_attribute( time_index=5, - attribute=output.SubcatchAttribute.RUNOFF_RATE.value + attribute=output.SubcatchAttribute.RUNOFF_RATE ) TestSWMMOutput.assert_dict_almost_equal( - subcatchment_values, + sub_catchment_values, self.test_artifacts['test_get_subcatchment_values_by_time_and_attributes'], ) @@ -493,7 +654,7 @@ def test_get_node_values_by_time_and_attributes(self): swmm_output = Output(example_output_data.EXAMPLE_OUTPUT_FILE_1) node_values = swmm_output.get_node_values_by_time_and_attribute( time_index=8, - attribute=output.NodeAttribute.TOTAL_INFLOW.value + attribute=output.NodeAttribute.TOTAL_INFLOW ) TestSWMMOutput.assert_dict_almost_equal( @@ -509,12 +670,12 @@ def test_get_link_values_by_time_and_attributes(self): swmm_output = Output(example_output_data.EXAMPLE_OUTPUT_FILE_1) link_values = swmm_output.get_link_values_by_time_and_attribute( time_index=10, - attribute=output.LinkAttribute.FLOW_RATE.value + attribute=output.LinkAttribute.FLOW_RATE ) TestSWMMOutput.assert_dict_almost_equal( - link_values, - self.test_artifacts['test_get_link_values_by_time_and_attributes'], + d1=link_values, + d2=self.test_artifacts['test_get_link_values_by_time_and_attributes'], ) def test_get_system_values_by_time_and_attributes(self): @@ -522,31 +683,31 @@ def test_get_system_values_by_time_and_attributes(self): Test the output get system values by time and attributes function :return: """ - swmm_output = Output(example_output_data.EXAMPLE_OUTPUT_FILE_1) + swmm_output = Output(output_file=example_output_data.EXAMPLE_OUTPUT_FILE_1) system_values = swmm_output.get_system_values_by_time_and_attribute( time_index=12, - attribute=output.SystemAttribute.RUNOFF_FLOW.value + attribute=output.SystemAttribute.RUNOFF_FLOW ) TestSWMMOutput.assert_dict_almost_equal( - system_values, - self.test_artifacts['test_get_system_values_by_time_and_attributes'], + d1=system_values, + d2=self.test_artifacts['test_get_system_values_by_time_and_attributes'], ) - def test_get_subcatchment_values_by_time_and_index(self): + def test_get_sub_catchment_values_by_time_and_index(self): """ - Test the output get subcatchment values by time and index function + Test the output get sub-catchment values by time and index function :return: """ - swmm_output = Output(example_output_data.EXAMPLE_OUTPUT_FILE_1) - subcatchment_values = swmm_output.get_subcatchment_values_by_time_and_element_index( + swmm_output = Output(output_file=example_output_data.EXAMPLE_OUTPUT_FILE_1) + sub_catchment_values = swmm_output.get_subcatchment_values_by_time_and_element_index( time_index=5, element_index=3 ) TestSWMMOutput.assert_dict_almost_equal( - subcatchment_values, - self.test_artifacts['test_get_subcatchment_values_by_time_and_index'], + d1=sub_catchment_values, + d2=self.test_artifacts['test_get_subcatchment_values_by_time_and_index'], ) def test_get_node_values_by_time_and_index(self): @@ -554,15 +715,15 @@ def test_get_node_values_by_time_and_index(self): Test the output get node values by time and index function :return: """ - swmm_output = Output(example_output_data.EXAMPLE_OUTPUT_FILE_1) + swmm_output = Output(output_file=example_output_data.EXAMPLE_OUTPUT_FILE_1) node_values = swmm_output.get_node_values_by_time_and_element_index( time_index=8, element_index=4 ) TestSWMMOutput.assert_dict_almost_equal( - node_values, - self.test_artifacts['test_get_node_values_by_time_and_index'], + d1=node_values, + d2=self.test_artifacts['test_get_node_values_by_time_and_index'], ) def test_get_link_values_by_time_and_index(self): @@ -570,15 +731,15 @@ def test_get_link_values_by_time_and_index(self): Test the output get link values by time and index function :return: """ - swmm_output = Output(example_output_data.EXAMPLE_OUTPUT_FILE_1) + swmm_output = Output(output_file=example_output_data.EXAMPLE_OUTPUT_FILE_1) link_values = swmm_output.get_link_values_by_time_and_element_index( time_index=10, element_index=5 ) TestSWMMOutput.assert_dict_almost_equal( - link_values, - self.test_artifacts['test_get_link_values_by_time_and_index'], + d1=link_values, + d2=self.test_artifacts['test_get_link_values_by_time_and_index'], ) def test_get_system_values_by_time(self): @@ -586,22 +747,22 @@ def test_get_system_values_by_time(self): Test the output get system values by time :return: """ - swmm_output = Output(example_output_data.EXAMPLE_OUTPUT_FILE_1) + swmm_output = Output(output_file=example_output_data.EXAMPLE_OUTPUT_FILE_1) system_values = swmm_output.get_system_values_by_time(time_index=12) TestSWMMOutput.assert_dict_almost_equal( - system_values, - self.test_artifacts['test_get_system_values_by_time'] + d1=system_values, + d2=self.test_artifacts['test_get_system_values_by_time'] ) @staticmethod def assert_dict_almost_equal(d1: dict, d2: dict, rtol: float = 1e-5, atol: float = 1e-8): """ Assert that two dictionaries are almost equal - :param d1: First dictionary - :param d2: Second dictionary - :param rtol: Relative error - :param atol: Absolute error + :param d1: First dictionary to compare with d2 dictionary + :param d2: Second dictionary to compare with d1 dictionary + :param rtol: Relative error tolerance for floating point values + :param atol: Absolute error tolerance for floating point values :return: """ """Assert that two dictionaries are almost equal (with tolerance).""" diff --git a/src/outfile/errormanager.c b/src/outfile/errormanager.c index e3345e93a..830ae5ed6 100644 --- a/src/outfile/errormanager.c +++ b/src/outfile/errormanager.c @@ -1,9 +1,9 @@ /*! * \file errormanager.c -* \brief Source providing a simple interface for managing runtime error messages. +* \brief Source file providing a simple interface for managing SWMM output API runtime error messages. * \author Michael E. Tryby (US EPA - ORD/NRMRL) -* \date Created On: 2017-08-25 -* \date Last Edited: 2024-10-17 +* \date Created: 2017-08-25 +* \date Last edited: 2024-10-17 */ #include #include diff --git a/src/outfile/errormanager.h b/src/outfile/errormanager.h index e7050524f..5ba605fb6 100644 --- a/src/outfile/errormanager.h +++ b/src/outfile/errormanager.h @@ -1,10 +1,10 @@ /*! * \file errormanager.h -* \brief Header file for error handling +* \brief Header file for SWMM output API error handling. * \author Michael E. Tryby (US EPA - ORD/NRMRL) -* \date Created On: 2017-08-25 -* \date Last Edited: 2024-10-17 +* \date Created: 2017-08-25 +* \date Last edited: 2024-10-17 */ #ifndef ERRORMANAGER_H_ #define ERRORMANAGER_H_ diff --git a/src/outfile/include/swmm_output.h b/src/outfile/include/swmm_output.h index 6f8acb268..c3f7ae7ff 100644 --- a/src/outfile/include/swmm_output.h +++ b/src/outfile/include/swmm_output.h @@ -3,21 +3,30 @@ * \author Colleen Barr (US EPA - ORD/NHEERL) * \author Michael Tryby (US EPA) (Modified) * \author Bryant McDonnell (Modified) -* \brief SWMM Output API -* \date Created On: 2017-08-25 -* \date Last Edited: 2024-10-17 +* \brief Header file for SWMM output API. +* \date Created: 2017-08-25 +* \date Last edited: 2024-10-17 */ #ifndef SWMM_OUTPUT_H_ #define SWMM_OUTPUT_H_ -/*! \brief Maximum number of characters in a file path */ +/*! +* \def MAXFILENAME +* \brief Maximum number of characters in a file path +*/ #define MAXFILENAME 259 -/*! \brief Maximum number of characters in a element name */ +/*! +* \def MAXELENAME +* \brief Maximum number of characters in a element name +*/ #define MAXELENAME 31 -/*! \brief Opaque pointer to struct. Do not access variables. */ +/*! +* \typedef SMO_Handle +* \brief Opaque pointer to struct. Do not access variables. +*/ typedef void *SMO_Handle; diff --git a/src/outfile/include/swmm_output_enums.h b/src/outfile/include/swmm_output_enums.h index cd959389c..1cd4b24a6 100644 --- a/src/outfile/include/swmm_output_enums.h +++ b/src/outfile/include/swmm_output_enums.h @@ -1,9 +1,9 @@ /*! * \file swmm_output_enums.h * \author Michael Tryby (US EPA - ORD/CESER) - * \brief Header file for SWMM output enumeration types - * \date Created On: 2019-10-18 - * \date Last Edited: 2024-10-17 + * \brief Header file for SWMM output API enumeration types. + * \date Created: 2019-10-18 + * \date Last edited: 2024-10-17 */ #ifndef SWMM_OUTPUT_ENUMS_H_ diff --git a/src/outfile/messages.h b/src/outfile/messages.h index 97870d7db..b4b26c409 100644 --- a/src/outfile/messages.h +++ b/src/outfile/messages.h @@ -1,9 +1,9 @@ /*! * \file messages.h - * \brief Error and warning messages + * \brief Header file for SWMM output API error and warning messages. * \author Michael Tryby (US EPA - ORD/NRMRL) - * \date Created On: 2017-10-20 - * \date Last Edited: 2024-10-23 + * \date Created: 2017-10-20 + * \date Last edited: 2024-10-23 */ #ifndef SRC_MESSAGES_H_ #define SRC_MESSAGES_H_ @@ -16,8 +16,15 @@ #define MAXMSG 56 /*! -* \defgroup Error_Messages Error and warning messages -* \brief Error messages for the Output API +* \defgroup Output_Error_Warning_Local_Functions Output API Error, Warning, and Functions +* \brief Output API error, warning, and local functions +* \{ +*/ + +/*! +* \addtogroup Output_API_Error_Messages Output API Error and Warning Messages +* \brief Error messages for the output API +* \ingroup Output_Error_Warning_Local_Functions * \{ */ @@ -81,6 +88,10 @@ */ #define ERR440 "ERROR 440: an unspecified error has occurred" +/*! + * \} + */ + /*! * \} */ diff --git a/src/outfile/swmm_output.c b/src/outfile/swmm_output.c index a388b6b55..8c37e37b4 100644 --- a/src/outfile/swmm_output.c +++ b/src/outfile/swmm_output.c @@ -4,8 +4,8 @@ * \author Colleen Barr (US EPA - ORD/NHEERL) * \author Michael E. Tryby (US EPA - ORD/NHEERL) (Modified) * \author Bryant McDonnell (Modified) -* \date Created On: 2017-08-25 -* \date Last Edited: 2024-10-17 +* \date Created: 2017-08-25 +* \date Last edited: 2024-10-17 */ #include @@ -847,13 +847,21 @@ int EXPORT_OUT_API SMO_getNodeSeries(SMO_Handle p_handle, int nodeIndex, return set_error(p_data->error_handle, errorcode); } +/*! +* \brief Gets the time series results for a particular attribute for a link. Specify series +* start and length using timeIndex and length respectively. +* \param[in] p_handle Pointer to opaque SMO_Handle +* \param[in] linkIndex Link index +* \param[in] attr Link attribute +* \param[in] startPeriod Start period index +* \param[in] endPeriod End period index +* \param[out] outValueArray Array of values to use to store the results +* \param[out] length Length of outValueArray +* \return Error code 0 on success, -1 on failure or error code +*/ int EXPORT_OUT_API SMO_getLinkSeries(SMO_Handle p_handle, int linkIndex, SMO_linkAttribute attr, int startPeriod, int endPeriod, float **outValueArray, int *length) -// -// Purpose: Get time series results for particular attribute. Specify series -// start and length using timeIndex and length respectively. -// { int k, len, errorcode = 0; float *temp; @@ -884,12 +892,19 @@ int EXPORT_OUT_API SMO_getLinkSeries(SMO_Handle p_handle, int linkIndex, return set_error(p_data->error_handle, errorcode); } +/*! +* \brief Get system time series results for particular attribute. Specify series +* start and length using timeIndex and length respectively. +* \param[in] p_handle Pointer to opaque SMO_Handle +* \param[in] attr System attribute +* \param[in] startPeriod Start period index +* \param[in] endPeriod End period index +* \param[out] outValueArray Array of values to use to store the results +* \param[out] length Length of outValueArray +* \return Error code 0 on success, -1 on failure or error code +*/ int EXPORT_OUT_API SMO_getSystemSeries(SMO_Handle p_handle, SMO_systemAttribute attr, int startPeriod, int endPeriod, float **outValueArray, int *length) -// -// Purpose: Get time series results for particular attribute. Specify series -// start and length using timeIndex and length respectively. -// { int k, len, errorcode = 0; float *temp; @@ -918,11 +933,17 @@ int EXPORT_OUT_API SMO_getSystemSeries(SMO_Handle p_handle, SMO_systemAttribute return set_error(p_data->error_handle, errorcode); } +/*! +* \brief Get attribute for all subcatchments at a given time. +* \param[in] p_handle Pointer to opaque SMO_Handle +* \param[in] periodIndex Time index +* \param[in] attr Subcatchment attribute +* \param[out] outValueArray Array of values to use to store the results +* \param[out] length Length of outValueArray +* \return Error code 0 on success, -1 on failure or error code +*/ int EXPORT_OUT_API SMO_getSubcatchAttribute(SMO_Handle p_handle, int periodIndex, SMO_subcatchAttribute attr, float **outValueArray, int *length) -// -// Purpose: For all subcatchments at given time, get a particular attribute. -// { int k, errorcode = 0; float *temp; @@ -949,11 +970,17 @@ int EXPORT_OUT_API SMO_getSubcatchAttribute(SMO_Handle p_handle, int periodIndex return set_error(p_data->error_handle, errorcode); } +/*! +* \brief Get attribute for all nodes at a given time. +* \param[in] p_handle Pointer to opaque SMO_Handle +* \param[in] periodIndex Time index +* \param[in] attr Node attribute +* \param[out] outValueArray Array of values to use to store the results +* \param[out] length Length of outValueArray +* \return Error code 0 on success, -1 on failure or error code +*/ int EXPORT_OUT_API SMO_getNodeAttribute(SMO_Handle p_handle, int periodIndex, SMO_nodeAttribute attr, float **outValueArray, int *length) -// -// Purpose: For all nodes at given time, get a particular attribute. -// { int k, errorcode = 0; float *temp; @@ -980,11 +1007,17 @@ int EXPORT_OUT_API SMO_getNodeAttribute(SMO_Handle p_handle, int periodIndex, return set_error(p_data->error_handle, errorcode); } +/*! +* \brief Get attribute for all links at a given time. +* \param[in] p_handle Pointer to opaque SMO_Handle +* \param[in] periodIndex Time index +* \param[in] attr Link attribute +* \param[out] outValueArray Array of values to use to store the results +* \param[out] length Length of outValueArray +* \return Error code 0 on success, -1 on failure or error code +*/ int EXPORT_OUT_API SMO_getLinkAttribute(SMO_Handle p_handle, int periodIndex, SMO_linkAttribute attr, float **outValueArray, int *length) -// -// Purpose: For all links at given time, get a particular attribute. -// { int k, errorcode = 0; float *temp; @@ -1011,11 +1044,17 @@ int EXPORT_OUT_API SMO_getLinkAttribute(SMO_Handle p_handle, int periodIndex, return set_error(p_data->error_handle, errorcode); } +/*! +* \brief Get attribute for the system at a given time. +* \param[in] p_handle Pointer to opaque SMO_Handle +* \param[in] periodIndex Time index +* \param[in] attr System attribute +* \param[out] outValueArray Array of values to use to store the results +* \param[out] length Length of outValueArray +* \return Error code 0 on success, -1 on failure or error code +*/ int EXPORT_OUT_API SMO_getSystemAttribute(SMO_Handle p_handle, int periodIndex, SMO_systemAttribute attr, float **outValueArray, int *length) -// -// Purpose: For the system at given time, get a particular attribute. -// { int errorcode = 0; float *temp; @@ -1040,11 +1079,17 @@ int EXPORT_OUT_API SMO_getSystemAttribute(SMO_Handle p_handle, int periodIndex, return set_error(p_data->error_handle, errorcode); } +/*! +* \brief Get subcatchment result for all attributes at a given time. +* \param[in] p_handle Pointer to opaque SMO_Handle +* \param[in] periodIndex Time index +* \param[in] subcatchIndex Subcatchment index +* \param[out] outValueArray Array of values to use to store the results +* \param[out] length Length of outValueArray +* \return Error code 0 on success, -1 on failure or error code +*/ int EXPORT_OUT_API SMO_getSubcatchResult(SMO_Handle p_handle, int periodIndex, int subcatchIndex, float **outValueArray, int *arrayLength) -// -// Purpose: For a subcatchment at given time, get all attributes. -// { int errorcode = 0; float *temp; @@ -1078,11 +1123,17 @@ int EXPORT_OUT_API SMO_getSubcatchResult(SMO_Handle p_handle, int periodIndex, return set_error(p_data->error_handle, errorcode); } +/*! +* \brief Get node result for all attributes at a given time. +* \param[in] p_handle Pointer to opaque SMO_Handle +* \param[in] periodIndex Time index +* \param[in] nodeIndex Node index +* \param[out] outValueArray Array of values to use to store the results +* \param[out] length Length of outValueArray +* \return Error code 0 on success, -1 on failure or error code +*/ int EXPORT_OUT_API SMO_getNodeResult(SMO_Handle p_handle, int periodIndex, int nodeIndex, float **outValueArray, int *arrayLength) -// -// Purpose: For a node at given time, get all attributes. -// { int errorcode = 0; float *temp; @@ -1118,11 +1169,17 @@ int EXPORT_OUT_API SMO_getNodeResult(SMO_Handle p_handle, int periodIndex, return set_error(p_data->error_handle, errorcode); } +/*! +* \brief Get link result for all attributes at a given time. +* \param[in] p_handle Pointer to opaque SMO_Handle +* \param[in] periodIndex Time index +* \param[in] linkIndex Link index +* \param[out] outValueArray Array of values to use to store the results +* \param[out] length Length of outValueArray +* \return Error code 0 on success, -1 on failure or error code +*/ int EXPORT_OUT_API SMO_getLinkResult(SMO_Handle p_handle, int periodIndex, int linkIndex, float **outValueArray, int *arrayLength) -// -// Purpose: For a link at given time, get all attributes. -// { int errorcode = 0; float *temp; @@ -1159,11 +1216,17 @@ int EXPORT_OUT_API SMO_getLinkResult(SMO_Handle p_handle, int periodIndex, return set_error(p_data->error_handle, errorcode); } +/*! +* \brief Get system result for all attributes at a given time. +* \param[in] p_handle Pointer to opaque SMO_Handle +* \param[in] periodIndex Time index +* \param[in] dummyIndex Dummy index +* \param[out] outValueArray Array of values to use to store the results +* \param[out] length Length of outValueArray +* \return Error code 0 on success, -1 on failure or error code +*/ int EXPORT_OUT_API SMO_getSystemResult(SMO_Handle p_handle, int periodIndex, int dummyIndex, float **outValueArray, int *arrayLength) -// -// Purpose: For the system at given time, get all attributes. -// { int errorcode = 0; float *temp; @@ -1199,10 +1262,11 @@ int EXPORT_OUT_API SMO_getSystemResult(SMO_Handle p_handle, int periodIndex, return set_error(p_data->error_handle, errorcode); } +/*! +* \brief Frees memory allocated by API calls +* \param[in] array Pointer to array +*/ void EXPORT_OUT_API SMO_free(void **array) -// -// Purpose: Frees memory allocated by API calls -// { if (array != NULL) { free(*array); @@ -1210,6 +1274,10 @@ void EXPORT_OUT_API SMO_free(void **array) } } +/*! +* \brief Clears error message stored in the error handle +* \param[in] p_handle Pointer to opaque SMO_Handle +*/ void EXPORT_OUT_API SMO_clearError(SMO_Handle p_handle) { data_t *p_data; @@ -1217,6 +1285,13 @@ void EXPORT_OUT_API SMO_clearError(SMO_Handle p_handle) { clear_error(p_data->error_handle); } +/*! +* \brief Checks for error in the error handle and copies the error message to the +* message buffer +* \param[in] p_handle Pointer to opaque SMO_Handle +* \param[out] msg_buffer Error message buffer +* \return Error code +*/ int EXPORT_OUT_API SMO_checkError(SMO_Handle p_handle, char **msg_buffer) { int errorcode = 0; char *temp = NULL; @@ -1237,10 +1312,13 @@ int EXPORT_OUT_API SMO_checkError(SMO_Handle p_handle, char **msg_buffer) { return errorcode; } +/*! +* \brief Takes error code and returns error message +* \param[in] errcode Error code +* \param[out] dest_msg Destination message buffer +* \param[in] dest_len Length of destination message buffer +*/ void errorLookup(int errcode, char *dest_msg, int dest_len) -// -// Purpose: takes error code returns error message -// { const char *msg; @@ -1284,7 +1362,18 @@ void errorLookup(int errcode, char *dest_msg, int dest_len) } -// Local functions: +/*! +* \addtogroup SWMM_Output_API_Local_Functions SWMM Output API Local Functions +* \brief Local functions used by the SWMM output API functions +* \ingroup Output_Error_Warning_Local_Functions +* \{ +*/ + +/*! +* \brief Validates the SWMM binary output file +* \param[in] p_data Pointer to data_t structure +* \return Error code +*/ int validateFile(data_t *p_data) { INT4 magic1, magic2, errcode; int errorcode = 0; @@ -1315,6 +1404,10 @@ int validateFile(data_t *p_data) { return errorcode; } +/*! +* \brief Initializes the element names array in the data_t structure +* \param[in] p_data Pointer to data_t structure +*/ void initElementNames(data_t *p_data) { int j, numNames; @@ -1338,6 +1431,12 @@ void initElementNames(data_t *p_data) { } } +/*! +* \brief Gets the value of a particular time index +* \param[in] p_data Pointer to data_t structure +* \param[in] timeIndex Time index +* \return Time value at the given indexes +*/ double getTimeValue(data_t *p_data, int timeIndex) { F_OFF offset; @@ -1353,6 +1452,14 @@ double getTimeValue(data_t *p_data, int timeIndex) { return value; } +/*! +* \brief Gets the value of a particular subcatchment attribute at a given time index +* \param[in] p_data Pointer to data_t structure +* \param[in] timeIndex Time index +* \param[in] subcatchIndex Subcatchment index +* \param[in] attr Subcatchment attribute +* \return Subcatchment value at the given indexes +*/ float getSubcatchValue(data_t *p_data, int timeIndex, int subcatchIndex, SMO_subcatchAttribute attr) { @@ -1372,6 +1479,14 @@ float getSubcatchValue(data_t *p_data, int timeIndex, int subcatchIndex, return value; } +/*! +* \brief Gets the value of a particular node attribute at a given time index +* \param[in] p_data Pointer to data_t structure +* \param[in] timeIndex Time index +* \param[in] nodeIndex Node index +* \param[in] attr Node attribute +* \return Node value at the given indexes +*/ float getNodeValue(data_t *p_data, int timeIndex, int nodeIndex, SMO_nodeAttribute attr) { @@ -1392,6 +1507,14 @@ float getNodeValue(data_t *p_data, int timeIndex, int nodeIndex, return value; } +/*! +* \brief Gets the value of a particular link attribute at a given time index +* \param[in] p_data Pointer to data_t structure +* \param[in] timeIndex Time index +* \param[in] linkIndex Link index +* \param[in] attr Link attribute +* \return Link value at the given indexes +*/ float getLinkValue(data_t *p_data, int timeIndex, int linkIndex, SMO_linkAttribute attr) { @@ -1413,6 +1536,13 @@ float getLinkValue(data_t *p_data, int timeIndex, int linkIndex, return value; } +/*! +* \brief Gets the value of a particular system attribute at a given time index +* \param[in] p_data Pointer to data_t structure +* \param[in] timeIndex Time index +* \param[in] attr System attribute +* \return System value at the given indexes +*/ float getSystemValue(data_t *p_data, int timeIndex, SMO_systemAttribute attr) { F_OFF offset; @@ -1433,11 +1563,15 @@ float getSystemValue(data_t *p_data, int timeIndex, SMO_systemAttribute attr) { return value; } +/*! +* \brief Wrapper function for opening file to account for different fopen functions on different platforms +* \param[in] f Pointer to file pointer +* \param[in] name File name +* \param[in] mode File mode +* \return Error code +* \note fopen_s is part of C++11 standard +*/ int _fopen(FILE **f, const char *name, const char *mode) { - // - // Purpose: Substitute for fopen_s on platforms where it doesn't exist - // Note: fopen_s is part of C++11 standard - // int ret = 0; #ifdef _MSC_VER ret = (int)fopen_s(f, name, mode); @@ -1449,10 +1583,14 @@ int _fopen(FILE **f, const char *name, const char *mode) { return ret; } +/*! +* \brief Wrapper function for fseek to account for different fseek functions on different platforms +* \param[in] stream Pointer to file stream +* \param[in] offset Offset value +* \param[in] whence Whence value +* \return Error code +*/ int _fseek(FILE *stream, F_OFF offset, int whence) -// -// Purpose: Selects platform fseek() for large file support -// { #ifdef _MSC_VER #define FSEEK64 _fseeki64 @@ -1463,10 +1601,12 @@ int _fseek(FILE *stream, F_OFF offset, int whence) return FSEEK64(stream, offset, whence); } +/*! +* \brief Wrapper function for ftell to account for different ftell functions on different platforms +* \param[in] stream Pointer to file stream +* \return Error code +*/ F_OFF _ftell(FILE *stream) -// -// Purpose: Selects platform ftell() for large file support -// { #ifdef _MSC_VER #define FTELL64 _ftelli64 @@ -1476,6 +1616,11 @@ F_OFF _ftell(FILE *stream) return FTELL64(stream); } +/*! +* \brief Creates a new float array of size n +* \param[in] n Size of the array +* \return Pointer to the float array +*/ float *newFloatArray(int n) // // Warning: Caller must free memory allocated by this function. @@ -1484,6 +1629,11 @@ float *newFloatArray(int n) return (float *)malloc((n) * sizeof(float)); } +/*! +* \brief Creates a new int array of size n +* \param[in] n Size of the array +* \return Pointer to the int array +*/ int *newIntArray(int n) // // Warning: Caller must free memory allocated by this function. @@ -1492,6 +1642,11 @@ int *newIntArray(int n) return (int *)malloc((n) * sizeof(int)); } +/*! +* \brief Creates a new char array of size n +* \param[in] n Size of the array +* \return Pointer to the char array +*/ char *newCharArray(int n) // // Warning: Caller must free memory allocated by this function. @@ -1499,3 +1654,7 @@ char *newCharArray(int n) { return (char *)malloc((n) * sizeof(char)); } + +/*! +* \} +*/ diff --git a/src/run/main.c b/src/run/main.c index 0fe1b40b7..e82bb0e03 100644 --- a/src/run/main.c +++ b/src/run/main.c @@ -1,30 +1,33 @@ -//----------------------------------------------------------------------------- -// main.c -// -// Project: EPA SWMM5 -// Version: 5.2 -// Date: 03/24/2021 -// Author: L. Rossman - -// Main stub for the command line version of EPA SWMM 5.2 -// to be run with swmm5.dll. - +/*! +* \file main.c +* \author L. Rossman +* \date 2021-03-24 +* \brief Main stub for the command line version of EPA SWMM 5.2 +* \details This is the main stub for the command line version of EPA SWMM 5.2 +* to be run with swmm5.dll. +* \version 5.3 +*/ #include #include #include #include "swmm5.h" +/*! +* \brief Main function for the command line version of EPA SWMM 5.2 +* \param[in] argc Number of command line arguments +* \param[in] argv Array of command line arguments +* \return Error status +* \details Runs the command line version of EPA SWMM 5.2. +* Command line may be executed using the following command: +* ```bash +* runswmm f1 f2 f3 +* ``` +* where f1 = name of input file (typically with extension .inp), +* f2 = name of report file (typically with extension .rpt), and +* f3 = name of binary output file if saved or blank if not +* saved (typically with extension .out). +*/ 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.2. -// -// 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). -// { char *inputFile; char *reportFile; diff --git a/src/solver/climate.c b/src/solver/climate.c index 336f0aaf0..a6799e4ac 100644 --- a/src/solver/climate.c +++ b/src/solver/climate.c @@ -1,34 +1,37 @@ -//----------------------------------------------------------------------------- -// climate.c -// -// Project: EPA SWMM5 -// 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. -///----------------------------------------------------------------------------- +/*! +* \file climate.c +* \brief Climate related functions. +* \author L. Rossman +* \date Created: 2021-11-01 +* \date Last edited: 2024-12-23 +* \details 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. +*/ + +/*! +* \def _CRT_SECURE_NO_DEPRECATE +* \brief Define to prevent deprecation warnings from MS Visual C++ compilers +*/ #define _CRT_SECURE_NO_DEPRECATE #include @@ -37,76 +40,289 @@ #include #include "headers.h" -//----------------------------------------------------------------------------- -// Constants -//----------------------------------------------------------------------------- -enum ClimateFileFormats {UNKNOWN_FORMAT, - USER_PREPARED, // SWMM 5's own user format - GHCND, // NCDC GHCN Daily format - TD3200, // NCDC TD3200 format - DLY0204}; // Canadian DLY02 or DLY04 format -static const int MAXCLIMATEVARS = 4; -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 -//----------------------------------------------------------------------------- +/*! +* \defgroup Climate_File_Constants_and_Shared_Variables Climate File Constants, Enumerations, Shared Variables, and Functions +* \brief Climate file constants, enumeration types, shared variables, and functions. +* \{ +*/ +/*! +* \addtogroup Climate_File_Constants Climate File Constants and Enumerations +* \brief Climate files local constants and enumeration types. +* \ingroup Climate_File_Constants_and_Shared_Variables +* \{ +*/ +/*! +* \enum ClimateFileFormats Climate file formats. +*/ +enum ClimateFileFormats { + /*! \brief Unknown format */ + UNKNOWN_FORMAT, + /*! \brief SWMM 5's own user format */ + USER_PREPARED, + /*! \brief NCDC GHCN Daily format */ + GHCND, + /*! \brief NCDC TD3200 format */ + TD3200, + /*! \brief Canadian DLY02 or DLY04 format */ + DLY0204 +}; + +/*! +* \var MAXCLIMATEVARS +* \brief Maximum number of climate variables +*/ +static const int MAXCLIMATEVARS = 4; + +/*! +* \var MAXDAYSPERMONTH +* \brief Maximum number of days per month +*/ +static const int MAXDAYSPERMONTH = 32; + +/*! +* \enum ClimateVarType Climate variable types. +These variables are used when processing climate files. +*/ +enum ClimateVarType { + /*! \brief Minimum temperature */ + TMIN, + /*! \brief Maximum temperature */ + TMAX, + /*! \brief Evaporation */ + EVAP, + /*! \brief Wind speed */ + WIND +}; + +/*! +* \enum WindSpeedType Wind speed types. +These variables are used when processing climate files. +*/ +enum WindSpeedType { + /*! \brief Wind speed */ + WDMV, + /*! \brief Wind direction */ + AWND +}; + +/*! +* \enum TempUnitsType Temperature units types. +These variables are used when processing climate files. +*/ +enum TempUnitsType { + /*! \brief Temperature in tenths of a degree Celsius */ + DEG_C10, + /*! \brief Temperature in degrees Celsius */ + DEG_C, + /*! \brief Temperature in degrees Fahrenheit */ + DEG_F +}; + +/*! +* \var ClimateVarWords +* \brief Climate variable words. These words are used when processing climate files. +*/ +static char* ClimateVarWords[] = { + "TMIN", "TMAX", "EVAP", "WDMV", "AWND", NULL +}; + +/*! +* \var TempUnitsWords +* \brief Temperature units words. These words are used when processing climate files. +*/ +static char* TempUnitsWords[] = { + "C10", "C", "F", NULL +}; +/*! +* \} +*/ + +/*! +* \struct TMovAve +* \brief Structure for moving average of daily temperatures. +*/ typedef struct -{ - double tAve; // moving avg. for daily temperature (deg F) - double tRng; // moving avg. for daily temp. range (deg F) - double ta[7]; // data window for tAve - double tr[7]; // data window for tRng - int count; // length of moving average window - int maxCount; // maximum length of moving average window - int front; // index of front of moving average window +{ + /*! \brief Moving average for daily temperature (deg F) */ + double tAve; + /*! \brief Moving average for daily temperature range (deg F) */ + double tRng; + /*! \brief Data window for tAve */ + double ta[7]; + /*! \brief Data window for tRng */ + double tr[7]; + /*! \brief Length of moving average window */ + int count; + /*! \brief Maximum length of moving average window */ + int maxCount; + /*! \brief Index of front of moving average window */ + int front; } TMovAve; -//----------------------------------------------------------------------------- -// Shared variables -//----------------------------------------------------------------------------- +/*! +* \defgroup Climate_File_Shared_Variables Climate Files Shared Variables +* \brief Climate files shared variables. +* \{ +*/ // Temperature variables -static double Tmin; // min. daily temperature (deg F) -static double Tmax; // max. daily temperature (deg F) -static double Trng; // 1/2 range of daily temperatures -static double Trng1; // prev. max - current min. temp. -static double Tave; // average daily temperature (deg F) -static double Hrsr; // time of min. temp. (hrs) -static double Hrss; // time of max. temp (hrs) -static double Hrday; // avg. of min/max temp times -static double Dhrdy; // hrs. between min. & max. temp. times -static double Dydif; // hrs. between max. & min. temp. times -static DateTime LastDay; // date of last day with temp. data -static TMovAve Tma; // moving average of daily temperatures +/*! + * \var Tmin + * \brief Minimum daily temperature (deg F) + */ +static double Tmin; +/*! + * \var Tmax + * \brief Maximum daily temperature (deg F) + */ +static double Tmax; +/*! + * \var Trng + * \brief 1/2 range of daily temperatures + */ +static double Trng; +/*! + * \var Trng1 + * \brief Previous day's max - current day's min temperature + */ +static double Trng1; +/*! + * \var Tave + * \brief Average daily temperature (deg F) + */ +static double Tave; +/*! + * \var Hrsr + * \brief Time of min. temp. (hrs) + */ +static double Hrsr; +/*! + * \var Hrss + * \brief Time of max. temp. (hrs) + */ +static double Hrss; +/*! + * \var Hrday + * \brief Avg. of min/max temp times + */ +static double Hrday; +/*! + * \var Dhrdy + * \brief Hrs. between min. & max. temp. times + */ +static double Dhrdy; +/*! + * \var Dydif + * \brief Hrs. between max. & min. temp. times + */ +static double Dydif; +/*! + * \var LastDay + * \brief Date of last day with temp. data + */ +static DateTime LastDay; +/*! + * \var Tma + * \brief Moving average of daily temperatures + */ +static TMovAve Tma; // Evaporation variables -static DateTime NextEvapDate; // next date when evap. rate changes + +/*! + * \var NextEvapDate + * \brief Next date when evap. rate changes + */ +static DateTime NextEvapDate; + +/*! + * \var NextEvapRate + * \brief Next evaporation rate (user units) + */ static double NextEvapRate; // next evaporation rate (user units) // Climate file variables + +/*! +* \var FileFormat +* \brief File format (see ClimateFileFormats) +*/ static int FileFormat; // file format (see ClimateFileFormats) + +/*! +* \var FileYear +* \brief Current year of file data +*/ static int FileYear; // current year of file data + +/*! +* \var FileMonth +* \brief Current month of year of file data +*/ static int FileMonth; // current month of year of file data + +/*! +* \var FileDay +* \brief Current day of month of file data +*/ static int FileDay; // current day of month of file data + +/*! +* \var FileLastDay +* \brief Last day of current month of file data +*/ static int FileLastDay; // last day of current month of file data + +/*! +* \var FileElapsedDays +* \brief Number of days read from file +*/ static int FileElapsedDays; // number of days read from file + +/*! +* \var FileStartDate +* \brief Starting date of file data +*/ static double FileValue[4]; // current day's values of climate data + +/*! +* \var FileData +* \brief Month's worth of daily climate data +*/ static double FileData[4][32]; // month's worth of daily climate data + +/*! +* \var FileLine +* \brief Line from climate data file +*/ static char FileLine[MAXLINE+1]; // line from climate data file +/*! +* \var FileFieldPos +* \brief Start of data fields for file record +*/ static int FileFieldPos[4]; // start of data fields for file record + +/*! +* \var FileDateFieldPos +* \brief Start of date field for file record +*/ static int FileDateFieldPos; // start of date field for file record + +/*! +* \var FileWindType +* \brief Wind speed type +*/ static int FileWindType; // wind speed type + +/*! +* \var FileTempUnits +* \brief GHCND file temperature units (C10, C or F) +*/ static int FileTempUnits; // GHCND file temperature units (C10, C or F) +/*! +* \} +*/ //----------------------------------------------------------------------------- // External functions (defined in funcs.h) @@ -122,49 +338,180 @@ static int FileTempUnits; // GHCND file temperature units (C10, C o //----------------------------------------------------------------------------- // Local functions //----------------------------------------------------------------------------- +/*! +* \defgroup Climate_Local_Function Climate Related Local Functions +* \brief Climate related local functions. +* \{ +*/ + +/*! +* \brief Determines the format of a climate file. +* \return The format code (TD3200, DLY0204, USER_PREPARED, GHCND, UNKNOWN_FORMAT) +*/ static int getFileFormat(void); + +/*! +* \brief Reads a line of data from a climate file. +* \param[out] y Year +* \param[out] m Month +*/ static void readFileLine(int *year, int *month); + +/*! +* \brief Reads year & month from line of User-Prepared climate file. +* \param[out] y Year +* \param[out] m Month +*/ static void readUserFileLine(int *year, int *month); + +/*! +* \brief Reads year & month from line of TD-3200 climate file. +* \param[out] y Year +* \param[out] m Month +*/ static void readTD3200FileLine(int *year, int *month); + +/*! +* \brief Reads year & month from line of DLY02 or DLY04 climate file. +* \param[out] y Year +* \param[out] m Month +*/ static void readDLY0204FileLine(int *year, int *month); + +/*! +* \brief Reads next month's worth of data from a GHCND climate file. +*/ static void readFileValues(void); +/*! +* \brief Finds date for next change in evaporation after the current date. +* \param[in] theDate Current simulation date +* \return The new value for NextEvapDate +*/ static void setNextEvapDate(DateTime thedate); + +/*! +* \brief Sets evaporation rate (ft/sec) for a specified date. +* \param[in] theDate Simulation date +*/ static void setEvap(DateTime theDate); + +/*! +* \brief Updates temperatures for new simulation date. +* \param[in] theDate Simulation date +*/ static void setTemp(DateTime theDate); + +/*! +* \brief Sets wind speed (mph) for a specified date. +* \param[in] theDate Simulation date +*/ static void setWind(DateTime theDate); + +/*! +* \brief Computes time of day when min/max temperatures occur. +* (min. temp occurs at sunrise, max. temp. at 3 hrs. < sunset) +* \param[in] day Day of year +*/ static void updateTempTimes(int day); + +/*! +* \brief Updates moving averages of average daily temperature and daily temperature range +* stored in structure Tma. +* \param[in] tmin Minimum daily temperature (deg F) +* \param[in] tmax Maximum daily temperature (deg F) +*/ static void updateTempMoveAve(double tmin, double tmax); -static double getTempEvap(int day, double ta, double tr); +/*! +* \brief Uses Hargreaves method to compute daily evaporation rate +* from daily average temperatures and Julian day. +* \param[in] day Day of year +* \param[in] tave 7-day average temperature (deg F) +* \param[in] trng 7-day average daily temperature range (deg F) +* \return Evaporation rate in user's units (US:in/day, SI:mm/day) +*/ +static double getTempEvap(int day, double tave, double trng); + +/*! +* \brief Updates daily climate variables for new day or reads in +* another month worth of values if a new month begins. +* \param[in] theDate Current simulation date +* \note Counters FileElapsedDays, FileDay, FileMonth, FileYear and +* FileLastDay were initialized in climate_openFile(). +*/ static void updateFileValues(DateTime theDate); + +/*! +* \brief Parses climate variable values from a line of a user-prepared climate file. +*/ static void parseUserFileLine(void); + +/*! +* \brief Parses climate variable values from a line of a TD3200 file. +*/ static void parseTD3200FileLine(void); + +/*! +* \brief Parses a month's worth of climate variable values from a line of +* a DLY02 or DLY04 climate file. +*/ static void parseDLY0204FileLine(void); + +/*! +* \brief Reads month worth of values for climate variable from TD-3200 file. +* \param[in] i Climate variable code +*/ static void setTD3200FileValues(int param); +/*! +* \brief Checks if a climate file is in the NCDC GHCN Daily format +* and determines the position of each climate variable field. +* \param[in] line Line from the climate file +* \return 1 if line is in GHCN format, 0 if not +*/ static int isGhcndFormat(char* line); + +/*! +* \brief Reads year & month from line of a NCDC GHCN Daily climate file. +* \param[out] y Year +* \param[out] m Month +*/ static void readGhcndFileLine(int *year, int *month); -static void parseGhcndFileLine(void); -static double convertGhcndValue(int var, double v); -//============================================================================= +/*! +* \brief Parses a line of a NCDC GHCN Daily file for daily values of max/min temperature, +* pan evaporation and wind speed. +*/ +static void parseGhcndFileLine(void); +/*! +* \brief Converts a climate variable value read from a NCDC GHCN Daily file +* to SWMM's internal units. +* \param[in] var Climate variable code +* \param[in] v Climate variable value +* \return Climate variable value in SWMM's internal units +*/ +static double convertGhcndValue(int var, double v); +/*! +* \} +*/ + +/*! +* \brief Reads climate/temperature parameters from input line of data. +* \param[in] tok Array of string tokens +* \param[in] ntoks Number of tokens +* \return Error code +* \details +* Format of data can be: +* - TIMESERIES name +* - FILE name (start) (units) +* - WINDSPEED MONTHLY v1 v2 ... v12 +* - WINDSPEED FILE +* - SNOWMELT v1 v2 ... v6 +* - ADC IMPERV/PERV v1 v2 ... v10 +*/ int climate_readParams(char* tok[], int ntoks) -// -// Input: tok[] = array of string tokens -// ntoks = number of tokens -// Output: returns error code -// Purpose: reads climate/temperature parameters from input line of data -// -// Format of data can be -// TIMESERIES name -// FILE name (start) (units) -// WINDSPEED MONTHLY v1 v2 ... v12 -// WINDSPEED FILE -// SNOWMELT v1 v2 ... v6 -// ADC IMPERV/PERV v1 v2 ... v10 -// { int i, j, k; double x[6], y; @@ -280,24 +627,22 @@ int climate_readParams(char* tok[], int ntoks) return 0; } -//============================================================================= - +/*! +* \brief Reads evaporation parameters from input line of data. +* \param[in] tok Array of string tokens +* \param[in] ntoks Number of tokens +* \return Error code +* \details +* Data formats are: +* - CONSTANT value +* - MONTHLY v1 ... v12 +* - TIMESERIES name +* - TEMPERATURE +* - FILE (v1 ... v12) +* - RECOVERY name +* - DRY_ONLY YES/NO +*/ int climate_readEvapParams(char* tok[], int ntoks) -// -// Input: tok[] = array of string tokens -// ntoks = number of tokens -// Output: returns error code -// Purpose: reads evaporation parameters from input line of data. -// -// Data formats are: -// CONSTANT value -// MONTHLY v1 ... v12 -// TIMESERIES name -// TEMPERATURE -// FILE (v1 ... v12) -// RECOVERY name -// DRY_ONLY YES/NO -// { int i, k; double x; @@ -372,24 +717,22 @@ int climate_readEvapParams(char* tok[], int ntoks) return 0; } -//============================================================================= - +/*! +* \brief Reads adjustments to monthly evaporation or rainfall from input line of data. +* \param[in] tok Array of string tokens +* \param[in] ntoks Number of tokens +* \return Error code +* \details +* Data formats are: +* - TEMPERATURE v1 ... v12 +* - EVAPORATION v1 ... v12 +* - RAINFALL v1 ... v12 +* - CONDUCTIVITY v1 ... v12 +* - N-PERV subcatchID patternID +* - DSTORE subcatchID patternID +* - INFIL subcatchID patternID +*/ int climate_readAdjustments(char* tok[], int ntoks) -// -// Input: tok[] = array of string tokens -// ntoks = number of tokens -// Output: returns error code -// Purpose: reads adjustments to monthly evaporation or rainfall -// from input line of data. -// -// Data formats are: -// TEMPERATURE v1 ... v12 -// EVAPORATION v1 ... v12 -// RAINFALL v1 ... v12 -// CONDUCTIVITY v1 ... v12 -// N-PERV subcatchID patternID -// DSTORE subcatchID patternID -// INFIL subcatchID patternID { int i, j; @@ -475,14 +818,10 @@ int climate_readAdjustments(char* tok[], int ntoks) return error_setInpError(ERR_KEYWORD, tok[0]); } -//============================================================================= - +/*! +* \brief Validates climatological variables. +*/ void climate_validate() -// -// Input: none -// Output: none -// Purpose: validates climatological variables -// { int i; double a, z, pa; @@ -526,14 +865,10 @@ void climate_validate() } } -//============================================================================= - +/*! +* \brief Opens a climate file and reads in first set of values. +*/ void climate_openFile() -// -// Input: none -// Output: none -// Purpose: opens a climate file and reads in first set of values. -// { int i, m, y; @@ -593,14 +928,10 @@ void climate_openFile() } } -//============================================================================= - +/*! +* \brief Initializes climate state variables. +*/ void climate_initState() -// -// Input: none -// Output: none -// Purpose: initializes climate state variables. -// { LastDay = NO_DATE; Temp.tmax = MISSING; @@ -636,14 +967,11 @@ void climate_initState() } } -//============================================================================= - +/*! +* \brief Sets climate variables for current date. +* \param[in] theDate Simulation date +*/ void climate_setState(DateTime theDate) -// -// Input: theDate = simulation date -// Output: none -// Purpose: sets climate variables for current date. -// { if ( Fclimate.mode == USE_FILE ) updateFileValues(theDate); if ( Temp.dataSource != NO_TEMP ) setTemp(theDate); @@ -654,26 +982,21 @@ void climate_setState(DateTime theDate) setNextEvapDate(theDate); } -//============================================================================= - +/*! +* \brief Gets the next date when evaporation rate changes. +* \return The current value of NextEvapDate +*/ DateTime climate_getNextEvapDate() -// -// Input: none -// Output: returns the current value of NextEvapDate -// Purpose: gets the next date when evaporation rate changes. -// { return NextEvapDate; } -//============================================================================= - +/*! +* \brief Finds date for next change in evaporation after the current date. +* \param[in] theDate Current simulation date +* \return The new value for NextEvapDate +*/ void setNextEvapDate(DateTime theDate) -// -// Input: theDate = current simulation date -// Output: sets a new value for NextEvapDate -// Purpose: finds date for next change in evaporation after the current date. -// { int yr, mon, day, k; double d, e; @@ -729,18 +1052,14 @@ void setNextEvapDate(DateTime theDate) } } -//============================================================================= - +/*! +* \brief Updates daily climate variables for new day or reads in +* another month worth of values if a new month begins. +* \param[in] theDate Current simulation date +* \note Counters FileElapsedDays, FileDay, FileMonth, FileYear and +* FileLastDay were initialized in climate_openFile(). +*/ void updateFileValues(DateTime theDate) -// -// Input: theDate = current simulation date -// Output: none -// Purpose: updates daily climate variables for new day or reads in -// another month worth of values if a new month begins. -// -// NOTE: counters FileElapsedDays, FileDay, FileMonth, FileYear and -// FileLastDay were initialized in climate_openFile(). -// { int i; int deltaDays; @@ -777,14 +1096,11 @@ void updateFileValues(DateTime theDate) } } -//============================================================================= - +/*! +* \brief Updates temperatures for new simulation date. +* \param[in] theDate Simulation date +*/ void setTemp(DateTime theDate) -// -// Input: theDate = simulation date -// Output: none -// Purpose: updates temperatures for new simulation date. -// { int j; // snow data object index int k; // time series index @@ -867,14 +1183,11 @@ void setTemp(DateTime theDate) Temp.ea = 8.1175e6 * exp(-7701.544 / (Temp.ta + 405.0265) ); } -//============================================================================= - +/*! +* \brief Sets evaporation rate (ft/sec) for a specified date. +* \param[in] theDate Simulation date +*/ void setEvap(DateTime theDate) -// -// Input: theDate = simulation date -// Output: none -// Purpose: sets evaporation rate (ft/sec) for a specified date. -// { int k; int mon = datetime_monthOfYear(theDate); @@ -918,14 +1231,11 @@ void setEvap(DateTime theDate) } } -//============================================================================= - +/*! +* \brief Sets wind speed (mph) for a specified date. +* \param[in] theDate Simulation date +*/ void setWind(DateTime theDate) -// -// Input: theDate = simulation date -// Output: none -// Purpose: sets wind speed (mph) for a specified date. -// { int yr, mon, day; @@ -944,15 +1254,12 @@ void setWind(DateTime theDate) } } -//============================================================================= - +/*! +* \brief Computes time of day when min/max temperatures occur. +* (min. temp occurs at sunrise, max. temp. at 3 hrs. < sunset) +* \param[in] day Day of year +*/ void updateTempTimes(int day) -// -// Input: day = day of year -// Output: none -// Purpose: computes time of day when min/max temperatures occur. -// (min. temp occurs at sunrise, max. temp. at 3 hrs. < sunset) -// { double decl; // earth's declination double hrang; // hour angle of sunrise/sunset @@ -976,17 +1283,15 @@ void updateTempTimes(int day) Temp.tmax = Tmax; } -//============================================================================= - +/*! +* \brief Uses Hargreaves method to compute daily evaporation rate +* from daily average temperatures and Julian day. +* \param[in] day Day of year +* \param[in] tave 7-day average temperature (deg F) +* \param[in] trng 7-day average daily temperature range (deg F) +* \return Evaporation rate in user's units (US:in/day, SI:mm/day) +*/ double getTempEvap(int day, double tave, double trng) -// -// Input: day = day of year -// tave = 7-day average temperature (deg F) -// trng = 7-day average daily temperature range (deg F) -// Output: returns evaporation rate in user's units (US:in/day, SI:mm/day) -// Purpose: uses Hargreaves method to compute daily evaporation rate -// from daily average temperatures and Julian day. -// { double a = 2.0*PI/365.0; double ta = (tave - 32.0)*5.0/9.0; //average temperature (deg C) @@ -1005,14 +1310,11 @@ double getTempEvap(int day, double tave, double trng) return e; } -//============================================================================= - +/*! +* \brief Determines the format of a climate file. +* \return The format code (TD3200, DLY0204, USER_PREPARED, GHCND, UNKNOWN_FORMAT) +*/ int getFileFormat() -// -// Input: none -// Output: returns code number of climate file's format -// Purpose: determines what format the climate file is in. -// { char recdType[4] = ""; char elemType[4] = ""; @@ -1050,15 +1352,12 @@ int getFileFormat() return UNKNOWN_FORMAT; } -//============================================================================= - +/*! +* \brief Reads a line of data from a climate file. +* \param[out] y Year +* \param[out] m Month +*/ void readFileLine(int *y, int *m) -// -// Input: none -// Output: y = year -// m = month -// Purpose: reads year & month from next line of climate file. -// { // --- read next line from climate data file while ( strlen(FileLine) == 0 ) @@ -1077,15 +1376,12 @@ void readFileLine(int *y, int *m) } } -//============================================================================= - +/*! +* \brief Reads year & month from line of User-Prepared climate file. +* \param[out] y Year +* \param[out] m Month +*/ void readUserFileLine(int* y, int* m) -// -// Input: none -// Output: y = year -// m = month -// Purpose: reads year & month from line of User-Prepared climate file. -// { int n; char staID[80]; @@ -1096,15 +1392,12 @@ void readUserFileLine(int* y, int* m) } } -//============================================================================= - +/*! +* \brief Reads year & month from line of TD-3200 climate file. +* \param[out] y Year +* \param[out] m Month +*/ void readTD3200FileLine(int* y, int* m) -// -// Input: none -// Output: y = year -// m = month -// Purpose: reads year & month from line of TD-3200 climate file. -// { char recdType[4] = ""; char year[5] = ""; @@ -1132,15 +1425,12 @@ void readTD3200FileLine(int* y, int* m) *m = atoi(month); } -//============================================================================= - +/*! +* \brief Reads year & month from line of DLY02 or DLY04 climate file. +* \param[out] y Year +* \param[out] m Month +*/ void readDLY0204FileLine(int* y, int* m) -// -// Input: none -// Output: y = year -// m = month -// Purpose: reads year & month from line of DLY02 or DLY04 climate file. -// { char year[5] = ""; char month[3] = ""; @@ -1159,14 +1449,10 @@ void readDLY0204FileLine(int* y, int* m) *m = atoi(month); } -//============================================================================= - +/*! +* \brief Reads next month's worth of data from a GHCND climate file. +*/ void readFileValues() -// -// Input: none -// Output: none -// Purpose: reads next month's worth of data from climate file. -// { int i, j; int y, m; @@ -1196,15 +1482,10 @@ void readFileValues() } } -//============================================================================= - +/*! +* \brief Parses climate variable values from a line of a user-prepared climate file. +*/ void parseUserFileLine() -// -// Input: none -// Output: none -// Purpose: parses climate variable values from a line of a user-prepared -// climate file. -// { int n; int y, m, d; @@ -1244,14 +1525,10 @@ void parseUserFileLine() if ( strlen(s3) > 0 && *s3 != '*' ) FileData[WIND][d] = atof(s3); } -//============================================================================= - +/*! +* \brief Parses climate variable values from a line of a TD3200 file. +*/ void parseTD3200FileLine() -// -// Input: none -// Output: none -// Purpose: parses climate variable values from a line of a TD3200 file. -// { int i; char param[5] = ""; @@ -1266,14 +1543,11 @@ void parseTD3200FileLine() } } -//============================================================================= - +/*! +* \brief Reads month worth of values for climate variable from TD-3200 file. +* \param[in] i Climate variable code +*/ void setTD3200FileValues(int i) -// -// Input: i = climate variable code -// Output: none -// Purpose: reads month worth of values for climate variable from TD-3200 file. -// { char valCount[4] = ""; char day[3] = ""; @@ -1331,15 +1605,11 @@ void setTD3200FileValues(int i) } } -//============================================================================= - +/*! +* \brief Parses a month's worth of climate variable values from a line of +* a DLY02 or DLY04 climate file. +*/ void parseDLY0204FileLine() -// -// Input: none -// Output: none -// Purpose: parses a month's worth of climate variable values from a line of -// a DLY02 or DLY04 climate file. -// { int j, k, p; char param[4] = ""; @@ -1396,15 +1666,13 @@ void parseDLY0204FileLine() } } -//============================================================================= - +/*! +* \brief Checks if a climate file is in the NCDC GHCN Daily format and +* determines the position of each climate variable field. +* \param[in] line First line of text from a climate file +* \return TRUE if climate file is in NCDC GHCN Daily format, FALSE otherwise +*/ int isGhcndFormat(char* line) -// -// Input: line = first line of text from a climate file -// Output: returns TRUE if climate file is in NCDC GHCN Daily format. -// Purpose: Checks if a climate file is in the NCDC GHCN Daily format -// and determines the position of each climate variable field. -// { int i; char* ptr; @@ -1440,15 +1708,12 @@ int isGhcndFormat(char* line) return FALSE; } -//============================================================================= - +/*! +* \brief Reads year & month from line of a NCDC GHCN Daily climate file. +* \param[out] y Year +* \param[out] m Month +*/ void readGhcndFileLine(int* y, int* m) -// -// Input: none -// Output: y = year -// m = month -// Purpose: reads year & month from line of a NCDC GHCN Daily climate file. -// { int n = sscanf(&FileLine[FileDateFieldPos], "%4d%2d", y, m); if ( n != 2 ) @@ -1458,16 +1723,11 @@ void readGhcndFileLine(int* y, int* m) } } -//============================================================================= - +/*! +* \brief Parses a line of a NCDC GHCN Daily file for daily values of max/min temperature, +* pan evaporation and wind speed. +*/ void parseGhcndFileLine() -// -// Input: none -// Output: none -// Purpose: parses a line of a NCDC GHCN Daily file for daily -// values of max/min temperature, pan evaporation and -// wind speed. -// { int y, m, d, n, i; double v; @@ -1491,16 +1751,14 @@ void parseGhcndFileLine() } } -//============================================================================= - +/*! +* \brief Converts a climate variable value read from a NCDC GHCN Daily file +* to SWMM's internal units. +* \param[in] var Climate variable code +* \param[in] v Climate variable value +* \return Climate variable value in SWMM's internal units +*/ 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) { @@ -1564,16 +1822,13 @@ double convertGhcndValue(int var, double v) } } -//============================================================================= - +/*! +* \brief Updates moving averages of average daily temperature and daily temperature range +* stored in structure Tma. +* \param[in] tmin Minimum daily temperature (deg F) +* \param[in] tmax Maximum daily temperature (deg F) +*/ void updateTempMoveAve(double tmin, double tmax) -// -// Input: tmin = minimum daily temperature (deg F) -// tmax = maximum daily temperature (deg F) -// Output: none -// Purpose: updates moving averages of average daily temperature -// and daily temperature range stored in structure Tma. -// { double ta, // new day's average temperature (deg F) tr; // new day's temperature range (deg F) diff --git a/src/solver/consts.h b/src/solver/consts.h index a7942a101..86a745665 100644 --- a/src/solver/consts.h +++ b/src/solver/consts.h @@ -1,72 +1,248 @@ -//----------------------------------------------------------------------------- -// consts.h -// -// Project: EPA SWMM5 -// Version: 5.3 -// Date: 07/15/23 (Build 5.3.0) -// Author: L. Rossman -// -// Various Constants -// -// Update history -// ============== -// Build 5.3.0 -// - Added MAXHOTSTARTFILES constant to support saving multiple hotstart files -// at different times. -// -//----------------------------------------------------------------------------- - +/*! +* \file consts.h +* \brief Header file for SWMM constants. +* \author L. Rossman +* \date Created: 2023-07-15 +* \date Last updated: 2024-12-30 +* \version 5.3.0 +* \details +* Various constants used in the SWMM model. +* +* Update history +* ============== +* Build 5.3.0 (2024-12-30) +* - Added MAXHOTSTARTFILES constant to support saving multiple hotstart files +* at different times. +*/ #ifndef CONSTS_H #define CONSTS_H -//------------------ -// General Constants -//------------------ +/*! +* \defgroup SWMM_Constants SWMM Constants, Variables, and Local and API Functions +* \brief Constants, variables, local, and API functions used in the SWMM model. +* \{ +*/ + +/*! +* \addtogroup SWMM_General_Constants SWMM General Constants +* \brief General constants used in the SWMM model. +* \ingroup SWMM_Constants +* \{ +*/ + +/*! +* \def VERSION +* \brief SWMM version number +*/ #define VERSION 53000 + +/*! +* \def MAGICNUMBER +* \brief Magic number used to identify a SWMM binary file +*/ #define MAGICNUMBER 516114522 + +/*! +* \def EOFMARK +* \brief End of file mark used in SWMM binary files +* \note Use 0x04 for UNIX systems +*/ #define EOFMARK 0x1A // Use 0x04 for UNIX systems + +/*! +* \def MAXTITLE +* \brief Maximum number of title lines +*/ #define MAXTITLE 3 // Max. # title lines + +/*! +* \def MAXMSG +* \brief Maximum number of characters in a message +*/ #define MAXMSG 1024 // Max. # characters in message text + +/*! +* \def MAXLINE +* \brief Maximum number of characters per input line +*/ #define MAXLINE 1024 // Max. # characters per input line + +/*! +* \def MAXFNAME +* \brief Maximum number of characters in a file name +*/ #define MAXFNAME 259 // Max. # characters in file name + +/*! +* \def MAXTOKS +* \brief Maximum number of items per line of input +*/ #define MAXTOKS 40 // Max. items per line of input + +/*! +* \def MAXSTATES +* \brief Maximum number of computed hydraulic variables +*/ #define MAXSTATES 10 // Max. # computed hyd. variables + +/*! +* \def MAXODES +* \brief Maximum number of ODE's to be solved +*/ #define MAXODES 4 // Max. # ODE's to be solved + +/*! +* \def NA +* \brief NOT APPLICABLE code +*/ #define NA -1 // NOT APPLICABLE code + +/*! +* \def TRUE +* \brief Value for TRUE state +*/ #define TRUE 1 // Value for TRUE state + +/*! +* \def FALSE +* \brief Value for FALSE state +*/ #define FALSE 0 // Value for FALSE state + +/*! +* \def BIG +* \brief Generic large value +*/ #define BIG 1.E10 // Generic large value + +/*! +* \def TINY +* \brief Generic small value +*/ #define TINY 1.E-6 // Generic small value + +/*! +* \def ZERO +* \brief Effective zero value +*/ #define ZERO 1.E-10 // Effective zero value + +/*! +* \def MISSING +* \brief Missing value code +*/ #define MISSING -1.E10 // Missing value code + +/*! +* \def PI +* \brief Value of pi +*/ #define PI 3.141592654 // Value of pi + +/*! +* \def GRAVITY +* \brief Acceleration of gravity in US units +*/ #define GRAVITY 32.2 // accel. of gravity in US units + +/*! +* \def SI_GRAVITY +* \brief Acceleration of gravity in SI units +*/ #define SI_GRAVITY 9.81 // accel of gravity in SI units /* DEPRECATED #define MAXFILESIZE 2147483647L // largest file size in bytes */ + +/*! +* \def MAXHOTSTARTFILES +* \brief Maximum number of hotstart files +*/ #define MAXHOTSTARTFILES 10 // largest file size in bytes +/*! +* \} +*/ -//----------------------------- -// Units factor in Manning Eqn. -//----------------------------- +/*! +* \addtogroup Mannings_Unit_Factor Units factor in Manning Equation +* \brief Units factor in Manning Equation +* \ingroup SWMM_Constants +* \{ +*/ + +/*! +* \def PHI +* \brief Units factor in Manning Equation +*/ #define PHI 1.486 +/*! +* \} +*/ -//---------------------------------------------- -// Definition of measureable runoff flow & depth -//---------------------------------------------- +/*! +* \addtogroup Definitions_Measurable_Runoff_Flow_Depth Definition of Measureable Runoff, Flow, and Depth +* \brief Definition of measureable runoff, flow, and depth +* \ingroup SWMM_Constants +* \{ +*/ + +/*! +* \def MIN_RUNOFF_FLOW +* \brief Minimum runoff flow (cfs) +*/ #define MIN_RUNOFF_FLOW 0.001 // cfs + +/*! +* \def MIN_EXCESS_DEPTH +* \brief Minimum excess depth (ft) = 0.03 mm +* \note Currently not used +*/ #define MIN_EXCESS_DEPTH 0.0001 // ft, = 0.03 mm + +/*! +* \def MIN_TOTAL_DEPTH +* \brief Minimum total depth (ft). = 0.05 inches +*/ #define MIN_TOTAL_DEPTH 0.004167 // ft, = 0.05 inches + +/*! +* \def MIN_RUNOFF +* \brief Minimum runoff (ft/sec) = 0.001 in/hr +*/ #define MIN_RUNOFF 2.31481e-8 // ft/sec = 0.001 in/hr +/*! +* \} +*/ -//---------------------------------------------------------------------- -// Minimum flow, depth & volume used to evaluate steady state conditions -//---------------------------------------------------------------------- +/*! +* \addtogroup Min_Flow_Depth_and_Vol Minimum Flow, Depth, and Volume Used to Evaluate Steady State Conditions +* \brief Minimum flow, depth, and volume used to evaluate steady state conditions +* \ingroup SWMM_Constants +* \{ +*/ +/*! +* \def FLOW_TOL +* \brief Flow tolerance (cfs) +*/ #define FLOW_TOL 0.00001 // cfs + +/*! +* \def DEPTH_TOL +* \brief Depth tolerance (ft) +* \note Currently not used +*/ #define DEPTH_TOL 0.00001 // ft + +/*! +* \def VOLUME_TOL +* \brief Volume tolerance (ft3) +* \note Currently not used +*/ #define VOLUME_TOL 0.01 // ft3 +/*! +* \} +*/ //--------------------------------------------------- // Minimum depth for reporting non-zero water quality @@ -74,37 +250,163 @@ //#define MIN_WQ_DEPTH 0.01 // ft (= 3 mm) //#define MIN_WQ_FLOW 0.001 // cfs -//----------------------------------------------------- -// Minimum flow depth and area for dynamic wave routing -//----------------------------------------------------- +/*! +* \addtogroup Fudge Minimum Flow Depth and Area for Dynamic Wave Routing +* \brief Minimum Flow Depth and Area for Dynamic Wave Routing +* \ingroup SWMM_Constants +* \{ +*/ +/*! +* \def FUDGE +* \brief Fudge factor (ft or ft2) +*/ #define FUDGE 0.0001 // ft or ft2 +/*! +* \} +*/ -//--------------------------- -// Various conversion factors -//--------------------------- +/*! +* \addtogroup Conversion_Factors Various Conversion Factors +* \brief Various Conversion Factors +* \ingroup SWMM_Constants +* \{ +*/ +/*! +* \def GPMperCFS +* \brief Gallons per minute per cfs +*/ #define GPMperCFS 448.831 + +/*! +* \def AFDperCFS +* \brief Acre-feet per day per cfs +*/ #define AFDperCFS 1.9837 + +/*! +* \def MGDperCFS +* \brief Million gallons per day per cfs +*/ #define MGDperCFS 0.64632 + +/*! +* \def IMGDperCFS +* \brief Imperial million gallons per day per cfs +*/ #define IMGDperCFS 0.5382 + +/*! +* \def LPSperCFS +* \brief Liters per second per cfs +*/ #define LPSperCFS 28.317 + +/*! +* \def LPMperCFS +* \brief Liters per minute per cfs +*/ #define LPMperCFS 1699.0 + +/*! +* \def CMHperCFS +* \brief Cubic meters per hour per cfs +*/ #define CMHperCFS 101.94 + +/*! +* \def CMDperCFS +* \brief Cubic meters per day per cfs +*/ #define CMDperCFS 2446.6 + +/*! +* \def MLDperCFS +* \brief Million liters per day per cfs +*/ #define MLDperCFS 2.4466 + +/*! +* \def M3perFT3 +* \brief Cubic meters per ft3 +*/ #define M3perFT3 0.028317 + +/*! +* \def LperFT3 +* \brief Liters per ft3 +*/ #define LperFT3 28.317 + +/*! +* \def MperFT +* \brief Meters per ft +*/ #define MperFT 0.3048 + +/*! +* \def PSIperFT +* \brief Pounds per square inch per ft +*/ #define PSIperFT 0.4333 + +/*! +* \def KPAperPSI +* \brief Kilopascals per psi +*/ #define KPAperPSI 6.895 + +/*! +* \def KWperHP +* \brief Kilowatts per horsepower +*/ #define KWperHP 0.7457 + +/*! +* \def SECperDAY +* \brief Seconds per day +*/ #define SECperDAY 86400 + +/*! +* \def MSECperDAY +* \brief Milliseconds per day +*/ #define MSECperDAY 8.64e7 + +/*! +* \def MMperINCH +* \brief Millimeters per inch +*/ #define MMperINCH 25.40 +/*! +* \} +*/ + +/*! +* \addtogroup Token_Separators Token Separator Characters +* \brief Token separator characters +* \ingroup SWMM_Constants +* \{ +*/ -//--------------------------- -// Token separator characters -//--------------------------- +/*! +* \def SEPSTR +* \brief Separator string +*/ #define SEPSTR " \t\n\r" +/*! +* \addtogroup Token_Separators Table Separator Characters +* \brief Table separator characters +*/ +#define TBLSEPSTR " \t\n\r," + +/*! +* \} +*/ + +/*! +* \} +*/ #endif //CONSTS_H diff --git a/src/solver/controls.c b/src/solver/controls.c index b46054006..ba5d5ddb0 100644 --- a/src/solver/controls.c +++ b/src/solver/controls.c @@ -1,57 +1,60 @@ -//----------------------------------------------------------------------------- -// controls.c -// -// Project: EPA SWMM5 -// Version: 5.2 -// Date: 06/01/22 (Build 5.2.1) -// Author: L. Rossman -// -// Rule-based controls functions. -// -// Control rules have the format: -// RULE name -// IF -// AND / OR -// etc. -// THEN -// AND -// etc. -// ELSE -// AND -// etc. -// PRIORITY

-// -// consists of: -// value / -// where is -// E.g.: Node 123 Depth > 4.5 -// Node 456 Depth < Node 123 Depth -// -// consists of: -// = setting -// 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. -// Build 5.2.1: -// - A refactoring bug from 5.2.0 causing duplicate actions to be added -// to the list of control actions to take was fixed. -//----------------------------------------------------------------------------- +/*! + * \file controls.c + * \brief Rule-based controls functions. + * \author L. Rossman + * \date Created: 2022-06-01 + * \date Last updated: 2024-12-30 + * \version 5.3 + * \details + * Control rules have the format: + * RULE name + * IF + * AND / OR + * etc. + * THEN + * AND + * etc. + * ELSE + * AND + * etc. + * PRIORITY

+ * + * consists of: + * value / + * where is + * E.g.: Node 123 Depth > 4.5 + * Node 456 Depth < Node 123 Depth + * + * consists of: + * = setting + * 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. + * Build 5.2.1: + * - A refactoring bug from 5.2.0 causing duplicate actions to be added + * to the list of control actions to take was fixed. + */ + +/*! + * \def _CRT_SECURE_NO_DEPRECATE + * \brief Define to prevent deprecation warnings from MS Visual C++ compilers + */ #define _CRT_SECURE_NO_DEPRECATE #include @@ -62,120 +65,506 @@ //----------------------------------------------------------------------------- // Constants //----------------------------------------------------------------------------- -enum RuleState {r_RULE, r_IF, r_AND, r_OR, r_THEN, r_ELSE, r_PRIORITY, - 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[] = - {"GAGE", "NODE", "LINK", "CONDUIT", "PUMP", "ORIFICE", "WEIR", "OUTLET", - "SIMULATION", NULL}; -static char* AttribWords[] = - {"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 -//----------------------------------------------------------------------------- -// Rule Premise Variable +/*! + * \defgroup Controls_Enums_Structs_Variables_Functions Controls File Constants, Enumerations, Structs, Shared Variables, and Functions + * \brief Controls file constants, enumerations, structs, shared variables, and functions + * \{ + */ + +/*! + * \addtogroup Controls_Enums Controls File Enumerations + * \brief Controls enumeration types. + * \ingroup Controls_Enums_Structs_Variables_Functions + * \{ + */ +/*! + * \enum RuleState + * \brief Enumeration of rule interpreter states. + */ +enum RuleState +{ + /*! \brief Rule interpreter is idle */ + r_RULE, + /*! \brief If clause */ + r_IF, + /*! \brief And clause */ + r_AND, + /*! \brief Or clause */ + r_OR, + /*! \brief Then clause */ + r_THEN, + /*! \brief Else clause */ + r_ELSE, + /*! \brief Priority of control rule */ + r_PRIORITY, + /*! \brief Variable definition */ + r_VARIABLE, + /*! \brief Expression definition */ + r_EXPRESSION, + /*! \brief Error */ + r_ERROR +}; + +/*! + * \enum RuleObject + * \brief Enumeration of rule object types. + */ +enum RuleObject +{ + /*! \brief Rain gage */ + r_GAGE, + /*! \brief Node */ + r_NODE, + /*! \brief Link */ + r_LINK, + /*! \brief Conduit */ + r_CONDUIT, + /*! \brief Pump */ + r_PUMP, + /*! \brief Orifice */ + r_ORIFICE, + /*! \brief Weir */ + r_WEIR, + /*! \brief Outlet */ + r_OUTLET, + /*! \brief Simulation */ + r_SIMULATION +}; + +/*! + * \enum RuleAttrib + * \brief Enumeration of rule attribute types. + * \todo Might be useful to make all use same enum type and used in API to avoid duplication. + */ +enum RuleAttrib +{ + /*! \brief Depth */ + r_DEPTH, + /*! \brief Maximum depth */ + r_MAXDEPTH, + /*! \brief Head */ + r_HEAD, + /*! \brief Volume */ + r_VOLUME, + /*! \brief Inflow */ + r_INFLOW, + /*! \brief Flow */ + r_FLOW, + /*! \brief Full flow */ + r_FULLFLOW, + /*! \brief Full depth */ + r_FULLDEPTH, + /*! \brief Status */ + r_STATUS, + /*! \brief Setting */ + r_SETTING, + /*! \brief Length */ + r_LENGTH, + /*! \brief Slope */ + r_SLOPE, + /*! \brief Velocity */ + r_VELOCITY, + /*! \brief Time open */ + r_TIMEOPEN, + /*! \brief Time closed */ + r_TIMECLOSED, + /*! \brief Time */ + r_TIME, + /*! \brief Date */ + r_DATE, + /*! \brief Clock time */ + r_CLOCKTIME, + /*! \brief Day of year */ + r_DAYOFYEAR, + /*! \brief Day */ + r_DAY, + /*! \brief Month */ + r_MONTH +}; + +/*! + * \enum RuleRelation + * \brief Enumeration of rule relational operators. + */ +enum RuleRelation +{ + /*! \brief Equal */ + EQ, + /*! \brief Not equal */ + NE, + /*! \brief Less than */ + LT, + /*! \brief Less than or equal */ + LE, + /*! \brief Greater than */ + GT, + /*! \brief Greater than or equal */ + GE +}; + +/*! + * \enum RuleSetting + * \brief Enumeration of rule setting types. + */ +enum RuleSetting +{ + /*! \brief Curve */ + r_CURVE, + /*! \brief Timeseries */ + r_TIMESERIES, + /*! \brief PID */ + r_PID, + /*! \brief Numeric */ + r_NUMERIC +}; + +/*! + * \} + */ + +/*! + * \addtogroup Controls_Vars Controls File Variables + * \brief Controls file variables and definitions. + * \ingroup Controls_Enums_Structs_Variables_Functions + * \{ + */ + +/*! + * \def MAXVARNAME + * \brief Maximum length of a variable name + */ +#define MAXVARNAME 32 + +/*! + * \var ObjectWords + * \brief Rule object words + */ +static char *ObjectWords[] = { + "GAGE", + "NODE", + "LINK", + "CONDUIT", + "PUMP", + "ORIFICE", + "WEIR", + "OUTLET", + "SIMULATION", + NULL +}; + +/*! + * \var AttribWords + * \brief Rule attribute words + */ +static char *AttribWords[] = { + "DEPTH", + "MAXDEPTH", + "HEAD", + "VOLUME", + "INFLOW", + "FLOW", + "FULLFLOW", + "FULLDEPTH", + "STATUS", + "SETTING", + "LENGTH", + "SLOPE", + "VELOCITY", + "TIMEOPEN", + "TIMECLOSED", + "TIME", + "DATE", + "CLOCKTIME", + "DAYOFYEAR", + "DAY", + "MONTH", + NULL +}; + +/*! + * \var RelOpWords + * \brief Rule relational operator words + */ +static char *RelOpWords[] = {"=", "<>", "<", "<=", ">", ">=", NULL}; + +/*! + * \var StatusWords + * \brief Rule status words + */ +static char *StatusWords[] = {"OFF", "ON", NULL}; + +/*! + * \var ConduitWords + * \brief Rule conduit status words + */ +static char *ConduitWords[] = {"CLOSED", "OPEN", NULL}; + +/*! + * \var SettingTypeWords + * \brief Rule setting type words + */ +static char *SettingTypeWords[] = {"CURVE", "TIMESERIES", "PID", NULL}; + +/*! + * \var IntensityWord + * \brief Rule intensity word + */ +static char *IntensityWord = "INTENSITY"; + +/*! + * \} + */ + +/*! + * \addtogroup Controls_Structs Controls File Structures + * \brief Controls structures. + * \ingroup Controls_Enums_Structs_Variables_Functions + * \{ + */ + +/*! + * \struct TVariable + * \brief Rule premise variable. + */ struct TVariable { - int object; // type of object - int index; // index in object's array - int attribute; // object's attribute + /*! \brief Type of object */ + int object; + /*! \brief Index in object's array */ + int index; + /*! \brief Object's attribute */ + int attribute; }; -// Named Variable +/*! + * \struct TNamedVariable + * \brief Rule premise named variable. + */ struct TNamedVariable { - struct TVariable variable; // a rule premise variable - char name[MAXVARNAME+1]; // name used in math expression + /*! \brief Rule premise variable */ + struct TVariable variable; // a rule premise variable + + /*! \brief Name used in math expression */ + char name[MAXVARNAME + 1]; // name used in math expression }; -// Rule Premise Function +/*! + * \struct TExpression + * \brief Rule premise math expression. + */ struct TExpression { - MathExpr* expression; // tokenized math expression - char name[MAXVARNAME+1]; // expression name + /*! \brief Tokenized math expression */ + MathExpr *expression; // tokenized math expression + + /*! \brief Name of expression */ + char name[MAXVARNAME + 1]; // expression name }; -// Rule Premise Clause -struct TPremise +/*! + * \struct TPremise + * \brief 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) - double value; // right hand side value - struct TPremise *next; // next premise clause of rule + /*! \brief Clause type (IF/AND/OR) */ + int type; // clause type (IF/AND/OR) + + /*! \brief Index of expression (-1 if N/A) */ + int exprIndex; // expression index (-1 if N/A) + + /*! \brief Left hand side variable */ + struct TVariable lhsVar; // left hand side variable + + /*! \brief Right hand side variable */ + struct TVariable rhsVar; // right hand side variable + + /*! \brief Relational operator (>, <, =, etc) */ + int relation; // relational operator (>, <, =, etc) + + /*! \brief Right hand side value */ + double value; // right hand side value + + /*! \brief Next premise clause of rule */ + struct TPremise *next; // next premise clause of rule }; -// Rule Action Clause -struct TAction +/*! + * \struct TAction + * \brief Rule action clause. + */ +struct TAction { - int rule; // index of rule that action belongs to - int link; // index of link being controlled - int attribute; // attribute of link being controlled - int curve; // index of curve for modulated control - int tseries; // index of time series for modulated control - double value; // control setting for link attribute - double kp, ki, kd; // coeffs. for PID modulated control - double e1, e2; // PID set point error from previous time steps - struct TAction *next; // next action clause of rule + /*! \brief Index of rule that action belongs to */ + int rule; // index of rule that action belongs to + + /*! link index */ + int link; // index of link being controlled + + /*! \brief Attribute of link being controlled */ + int attribute; // attribute of link being controlled + + /*! \brief Index of curve for modulated control */ + int curve; // index of curve for modulated control + + /*! \brief Index of time series for modulated control */ + int tseries; // index of time series for modulated control + + /*! \brief Value of control setting */ + double value; // control setting for link attribute + + /*! \brief PID coefficient Kp */ + double kp; + + /*! \brief PID coefficient Ki */ + double ki; + + /*! \brief PID coefficient Kd */ + double kd; // coeffs. for PID modulated control + + /*! \brief PID set point error from previous time steps */ + double e1; + + /*! \brief PID set point error from previous time steps */ + double e2; // PID set point error from previous time steps + + /*! \brief Next action clause of rule */ + struct TAction *next; // next action clause of rule }; -// List of Control Actions -struct TActionList +/*! + * \struct TActionList + * \brief Linked list of control actions. + */ +struct TActionList { - struct TAction* action; - struct TActionList* next; + /*! \brief Action clause */ + struct TAction *action; + + /*! \brief Next action in list */ + struct TActionList *next; }; -// Control Rule -struct TRule +/*! + * \struct TRule + * \brief Control rule. + */ +struct TRule { - char* ID; // rule ID - double priority; // priority level - struct TPremise* firstPremise; // pointer to first premise of rule - struct TPremise* lastPremise; // pointer to last premise of rule - struct TAction* thenActions; // linked list of actions if true - struct TAction* elseActions; // linked list of actions if false + /*! \brief Rule ID */ + char *ID; // rule ID + /*! \brief Priority level */ + double priority; // priority level + /*! \brief Pointer to first premise of rule */ + struct TPremise *firstPremise; // pointer to first premise of rule + /*! \brief Pointer to last premise of rule */ + struct TPremise *lastPremise; // pointer to last premise of rule + /*! \brief Pointer to actions if true */ + struct TAction *thenActions; // linked list of actions if true + /*! \brief Pointer to actions if false */ + struct TAction *elseActions; // linked list of actions if false }; +/*! + * \} + */ //----------------------------------------------------------------------------- // Shared variables //----------------------------------------------------------------------------- -struct TRule* Rules; // array of control rules -struct TActionList* ActionList; // linked list of control actions -int InputState; // state of rule interpreter -int RuleCount; // total number of rules -double ControlValue; // value of controller variable -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 +/*! + * \addtogroup Controls_Vars Controls File Variables + * \brief Controls file variables and definitions. + * \ingroup Controls_Enums_Structs_Variables_Functions + * \{ + */ + +/*! + * \var Rules + * \brief Array of control rules. + */ +struct TRule *Rules; // array of control rules + +/*! + * \var ActionList + * \brief Linked list of control actions. + */ +struct TActionList *ActionList; // linked list of control actions + +/*! + * \var InputState + * \brief State of rule interpreter. + */ +int InputState; // state of rule interpreter + +/*! + * \var RuleCount + * \brief Total number of rules. + */ +int RuleCount; // total number of rules + +/*! + * \var ControlValue + * \brief Value of controller variable. + */ +double ControlValue; // value of controller variable + +/*! + * \var SetPoint + * \brief Value of controller setpoint. + */ +double SetPoint; // value of controller setpoint + +/*! + * \var CurrentDate + * \brief Current date in whole days. + */ +DateTime CurrentDate; // current date in whole days + +/*! + * \var CurrentTime + * \brief Current time of day (decimal). + */ +DateTime CurrentTime; // current time of day (decimal) + +/*! + * \var VariableCount + * \brief Number of named variables. + */ +int VariableCount; + +/*! + * \var ExpressionCount + * \brief Number of math expressions. + */ +int ExpressionCount; + +/*! + * \var CurrentVariable + * \brief Index of current named variable. + */ +int CurrentVariable; + +/*! + * \var CurrentExpression + * \brief Index of current math expression. + */ +int CurrentExpression; + +/*! + * \var NamedVariable + * \brief Array of named variables. + */ +struct TNamedVariable *NamedVariable; // array of named variables + +/*! + * \var Expression + * \brief Array of math expressions. + */ +struct TExpression *Expression; // array of math expressions +/*! + * \} + */ //----------------------------------------------------------------------------- // External functions (declared in funcs.h) @@ -192,43 +581,195 @@ struct TExpression* Expression; // array of math expressions //----------------------------------------------------------------------------- // Local functions //----------------------------------------------------------------------------- -int addPremise(int r, int type, char* Tok[], int nToks); -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); - -int evaluatePremise(struct TPremise* p, double tStep); +/*! + * \addtogroup Controls_Functions Controls File Functions + * \brief Controls file local functions. + * \ingroup Controls_Enums_Structs_Variables_Functions + * \{ + */ + +/*! + * \brief Adds a premise clause to a control rule. + * \param[in] r Rule index + * \param[in] type Premise type + * \param[in] Tok Array of string tokens + * \param[in] nToks Number of tokens + * \return Error code + */ +int addPremise(int r, int type, char *Tok[], int nToks); + +/*! + * \brief Retrieves a variable from a tokenized line of input. + * \param[in] tok Array of string tokens + * \param[in] nToks Number of tokens + * \param[in] k Index of current token + * \param[in, out] v Variable structure + * \return Error code + */ +int getPremiseVariable(char *tok[], int nToks, int *k, struct TVariable *v); + +/*! + * \brief Retrieves a premise value from a tokenized line of input. + * \param[in] token Token to evaluate + * \param[in] attrib Attribute of object + * \param[out] value Value of object + * \return Error code + */ +int getPremiseValue(char *token, int attrib, double *value); + +/*! + * \brief Adds an action clause to a control rule. + * \param[in] r Rule index + * \param[in] Tok Array of string tokens + * \param[in] nToks Number of tokens + * \return Error code + */ +int addAction(int r, char *Tok[], int nToks); + +/*! + * \brief Evaluates premise clause of a control rule. + * \param[in] p Premise structure to evaluate + * \param[in] tStep Time step + * \return TRUE if premise is true, FALSE if not + */ +int evaluatePremise(struct TPremise *p, double tStep); + +/*! + * \brief Gets the value of a variable. + * \param[in] v Variable structure + * \return Value of variable + */ double getVariableValue(struct TVariable v); -int compareTimes(double lhsValue, int relation, double rhsValue, - double halfStep); -int compareValues(double lhsValue, int relation, double rhsValue); - -void updateActionList(struct TAction* a); -int executeActionList(DateTime currentTime); -void clearActionList(void); -void deleteActionList(void); -void deleteRules(void); - -int findExactMatch(char *s, char *keyword[]); -int setActionSetting(char* tok[], int nToks, int* curve, int* tseries, - int* attrib, double value[]); -void updateActionValue(struct TAction* a, DateTime currentTime, double dt); -double getPIDSetting(struct TAction* a, double dt); - -int getVariableIndex(char* varName); + +/*! + * \brief Compares two times. + * \param[in] lhsValue Left hand side value + * \param[in] relation Relation operator + * \param[in] rhsValue Right hand side value + * \param[in] halfStep Half time step + * \return TRUE if relation is true, FALSE if not + */ +int compareTimes(double lhsValue, int relation, double rhsValue, double halfStep); + +/*! + * \brief Compares two values. + * \param[in] lhsValue Left hand side value + * \param[in] relation Relation operator + * \param[in] rhsValue Right hand side value + * \return TRUE if relation is true, FALSE if not + */ +int compareValues(double lhsValue, int relation, double rhsValue); + +/*! + * \brief Updates action list. + * \param[in] a Action structure + */ +void updateActionList(struct TAction *a); + +/*! + * \brief Executes action list. + * \param[in] currentTime Current time + * \return Error code + */ +int executeActionList(DateTime currentTime); + +/*! + * \brief Clears action list. + */ +void clearActionList(void); + +/*! + * \brief Deletes action list. + */ +void deleteActionList(void); + +/*! + * \brief Deletes control rules. + */ +void deleteRules(void); + +/*! + * \brief Finds an exact match in a list of keywords. + * \param[in] s String to match + * \param[in] keyword Array of keywords + * \return Index of keyword if found, -1 if not + */ +int findExactMatch(char *s, char *keyword[]); + +/*! + * \brief Sets the value of a control action. + * \param[in] tok Array of string tokens + * \param[in] nToks Number of tokens + * \param[in] curve Curve index + * \param[in] tseries Time series index + * \param[in] attrib Attribute index + * \param[out] value Value of action + * \return Error code + */ +int setActionSetting(char *tok[], int nToks, int *curve, int *tseries, + int *attrib, double value[]); + +/*! +* \brief Updates the value of a control action. +* \param[in] a Action structure +* \param[in] currentTime Current time +* \param[in] dt Time step +*/ +void updateActionValue(struct TAction *a, DateTime currentTime, double dt); + +/*! + * \brief Gets PID setting. + * \param[in] a Action structure + * \param[in] dt Time step + * \return Value of action + */ +double getPIDSetting(struct TAction *a, double dt); + +/*! + * \brief Gets variable index. + * \param[in] varName Name of variable to find index of + * \return Index of variable + */ +int getVariableIndex(char *varName); + +/*! + * \brief Gets named variable value. + * \param[in] varIndex Index of variable + * \return Value of variable + */ double getNamedVariableValue(int varIndex); -int getExpressionIndex(char* exprName); -int getGageAttrib(char* token); + +/*! + * \brief Gets expression index. + * \param[in] exprName Name of expression to find index of + * \return Index of expression + */ +int getExpressionIndex(char *exprName); + +/*! + * \brief Gets gauge attribute. + * \param[in] token Token to evaluate + * \return Attribute of gauge + */ +int getGageAttrib(char *token); + +/*! + * \brief Gets rain value. + * \param v Variable structure + */ double getRainValue(struct TVariable v); +/*! + * \} + */ -//============================================================================= +/*! + * \} + */ +/*! + * \brief Initializes the control rule system. + */ void controls_init() -// -// Input: none -// Output: none -// Purpose: initializes the control rule system. -// { Rules = NULL; NamedVariable = NULL; @@ -238,28 +779,25 @@ void controls_init() 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. -// +/*! +* \brief Updates the number of named variables or math expressions used +* by control rules. +* \param[in] s String containing either "VARIABLE" or "EXPRESSION" +*/ +void controls_addToCount(char *s) { - if (match(s, w_VARIABLE)) VariableCount++; - else if (match(s, w_EXPRESSION)) ExpressionCount++; + if (match(s, w_VARIABLE)) + VariableCount++; + else if (match(s, w_EXPRESSION)) + ExpressionCount++; } -//============================================================================= - -int controls_create(int n) -// -// Input: n = total number of control rules -// Output: returns error code -// Purpose: creates an array of control rules. -// +/*! + * \brief Creates an array of control rules. + * \param[in] n Total number of control rules + * \return Error code + */ +int controls_create(int n) { int r; ActionList = NULL; @@ -267,109 +805,110 @@ int controls_create(int n) RuleCount = n; if (RuleCount > 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) - { - Expression = (struct TExpression *) calloc(ExpressionCount, - sizeof(struct TExpression)); - if (Expression == NULL) return ERR_MEMORY; - } - return 0; -} + } -//============================================================================= + CurrentVariable = -1; + CurrentExpression = -1; + if (VariableCount > 0) + { + NamedVariable = (struct TNamedVariable *)calloc(VariableCount, + sizeof(struct TNamedVariable)); + if (NamedVariable == NULL) + return ERR_MEMORY; + } + if (ExpressionCount > 0) + { + Expression = (struct TExpression *)calloc(ExpressionCount, + sizeof(struct TExpression)); + if (Expression == NULL) + return ERR_MEMORY; + } + return 0; +} +/*! + * \brief Deletes all control rules. + */ void controls_delete(void) -// -// Input: none -// Output: none -// 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(); -} + int i; -//============================================================================= + for (i = 0; i < ExpressionCount; i++) + { + mathexpr_delete(Expression[i].expression); + Expression[i].expression = NULL; + } + FREE(Expression); + FREE(NamedVariable); -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 -// + if (RuleCount == 0) + return; + deleteActionList(); + deleteRules(); +} + +/*! +* \brief 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 +* \param[in] tok Array of string tokens +* \param[in] nToks Number of tokens +* \return Error code +*/ +int controls_addVariable(char *tok[], int nToks) { struct TVariable v1; int k, err; CurrentVariable++; - if (nToks < 5) return ERR_ITEMS; + 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; + 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; + 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 = -// +/*! +* \brief Adds a math expression to the control rule system from a +* a tokenized line of input with format: +* EXPRESSION name = +* \param[in] tok Array of string tokens +* \param[in] nToks Number of tokens +* \return Error code +*/ +int controls_addExpression(char *tok[], int nToks) { int i, k; - char s[MAXLINE + 1]; - MathExpr* expr; + char s[MAXLINE + 1]; + MathExpr *expr; CurrentExpression++; - if (nToks < 4) return ERR_ITEMS; + if (nToks < 4) + return ERR_ITEMS; k = CurrentExpression; Expression[k].expression = NULL; sstrncpy(Expression[k].name, tok[1], MAXVARNAME); @@ -388,123 +927,125 @@ int controls_addExpression(char* tok[], int nToks) 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. -// +/*! +* \brief Finds the array index of a named variable. +* \param[in] varName String containing a variable name +* \return Index of the named variable or -1 if not found +*/ +int getVariableIndex(char *varName) { int i; for (i = 0; i < VariableCount; i++) { - if (match(varName, NamedVariable[i].name)) return i; + if (match(varName, NamedVariable[i].name)) + return i; } return -1; } -//============================================================================= - +/*! +* \brief Finds the value of a named variable. +* \param[in] varIndex Index of a named variable +* \return Current value of the variable +*/ 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 -// +/*! +* \brief Finds the array index of a math expression. +* \param[in] exprName String containing an expression name +* \return Index of the expression or -1 if not found +*/ +int getExpressionIndex(char *exprName) { int i; for (i = 0; i < ExpressionCount; i++) { - if (match(exprName, Expression[i].name)) return 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 -// keyword = the clause's keyword code (IF, THEN, etc.) -// tok = an array of string tokens that comprises the clause -// nToks = number of tokens -// Output: returns an error code -// Purpose: addd a new clause to a control rule. -// +/*! +* \brief Adds a new clause to a control rule. +* \param[in] r Rule index +* \param[in] keyword Clause's keyword code (IF, THEN, etc.) +* \param[in] tok Array of string tokens that comprises the clause +* \param[in] nToks Number of tokens +* \return Error code +*/ +int controls_addRuleClause(int r, int keyword, char *tok[], int nToks) { switch (keyword) { - case r_RULE: - if ( Rules[r].ID == NULL ) + case r_RULE: + if (Rules[r].ID == NULL) Rules[r].ID = project_findID(CONTROL, tok[1]); InputState = r_RULE; - if ( nToks > 2 ) return ERR_RULE; + if (nToks > 2) + return ERR_RULE; return 0; - case r_IF: - if ( InputState != r_RULE ) return ERR_RULE; + case r_IF: + if (InputState != r_RULE) + return ERR_RULE; InputState = r_IF; return addPremise(r, r_AND, tok, nToks); - case r_AND: - if ( InputState == r_IF ) return addPremise(r, r_AND, tok, nToks); - else if ( InputState == r_THEN || InputState == r_ELSE ) + case r_AND: + if (InputState == r_IF) + return addPremise(r, r_AND, tok, nToks); + else if (InputState == r_THEN || InputState == r_ELSE) return addAction(r, tok, nToks); - else return ERR_RULE; + else + return ERR_RULE; - case r_OR: - if ( InputState != r_IF ) return ERR_RULE; + case r_OR: + if (InputState != r_IF) + return ERR_RULE; return addPremise(r, r_OR, tok, nToks); - case r_THEN: - if ( InputState != r_IF ) return ERR_RULE; + case r_THEN: + if (InputState != r_IF) + return ERR_RULE; InputState = r_THEN; return addAction(r, tok, nToks); - case r_ELSE: - if ( InputState != r_THEN ) return ERR_RULE; + case r_ELSE: + if (InputState != r_THEN) + return ERR_RULE; InputState = r_ELSE; return addAction(r, tok, nToks); - case r_PRIORITY: - if ( InputState != r_THEN && InputState != r_ELSE ) return ERR_RULE; + case r_PRIORITY: + if (InputState != r_THEN && InputState != r_ELSE) + return ERR_RULE; InputState = r_PRIORITY; - if ( !getDouble(tok[1], &Rules[r].priority) ) return ERR_NUMBER; - if ( nToks > 2 ) return ERR_RULE; + if (!getDouble(tok[1], &Rules[r].priority)) + return ERR_NUMBER; + if (nToks > 2) + return ERR_RULE; return 0; } return 0; } -//============================================================================= - +/*! +* \brief Evaluates all control rules at the current time of the simulation. +* \param[in] currentTime Current simulation date/time +* \param[in] elapsedTime Decimal days since start of simulation +* \param[in] tStep Simulation time step (days) +* \return Number of new actions taken +*/ int controls_evaluate(DateTime currentTime, DateTime elapsedTime, double tStep) -// -// Input: currentTime = current simulation date/time -// elapsedTime = decimal days since start of simulation -// tStep = simulation time step (days) -// Output: returns number of new actions taken -// Purpose: evaluates all control rules at current time of the simulation. -// { - int r; // control rule index - int result; // TRUE if rule premises satisfied - struct TPremise* p; // pointer to rule premise clause - struct TAction* a; // pointer to rule action clause + int r; // control rule index + int result; // TRUE if rule premises satisfied + struct TPremise *p; // pointer to rule premise clause + struct TAction *a; // pointer to rule action clause // --- save date and time to shared variables CurrentDate = floor(currentTime); @@ -512,32 +1053,36 @@ int controls_evaluate(DateTime currentTime, DateTime elapsedTime, double tStep) ElapsedTime = elapsedTime; // --- evaluate each rule - if ( RuleCount == 0 ) return 0; + if (RuleCount == 0) + return 0; clearActionList(); - for (r=0; rtype == r_OR ) + if (p->type == r_OR) { - if ( result == FALSE ) + if (result == FALSE) result = evaluatePremise(p, tStep); } else { - if ( result == FALSE ) break; + if (result == FALSE) + break; result = evaluatePremise(p, tStep); } p = p->next; - } + } // --- if premises true, add THEN clauses to action list // else add ELSE clauses to action list - if ( result == TRUE ) a = Rules[r].thenActions; - else a = Rules[r].elseActions; + if (result == TRUE) + a = Rules[r].thenActions; + else + a = Rules[r].elseActions; while (a) { updateActionValue(a, currentTime, tStep); @@ -547,31 +1092,32 @@ int controls_evaluate(DateTime currentTime, DateTime elapsedTime, double tStep) } // --- execute actions on action list - if ( ActionList ) return executeActionList(currentTime); - else return 0; + if (ActionList) + return executeActionList(currentTime); + else + return 0; } -//============================================================================= - -int addPremise(int r, int type, char* tok[], int nToks) -// -// Input: r = control rule index -// type = type of premise (IF, AND, OR) -// tok = array of string tokens containing premise statement -// nToks = number of string tokens -// Output: returns an error code -// Purpose: adds a new premise to a control rule. -// +/*! +* \brief Adds a new premise to a control rule. +* \param[in] r Control rule index +* \param[in] type Type of premise (IF, AND, OR) +* \param[in] tok Array of string tokens containing premise statement +* \param[in] nToks Number of string tokens +* \return Error code +*/ +int addPremise(int r, int type, char *tok[], int nToks) { - int relation, n, err = 0; + int relation, n, err = 0; double value = MISSING; - struct TPremise* p; + struct TPremise *p; struct TVariable v1; struct TVariable v2; - int obj, exprIndex, varIndex = -1; + int obj, exprIndex, varIndex = -1; // --- initialize LHS variable v1 - if (nToks < 4) return ERR_ITEMS; + if (nToks < 4) + return ERR_ITEMS; v1.attribute = -1; v1.object = -1; v1.index = -1; @@ -593,22 +1139,26 @@ int addPremise(int r, int type, char* tok[], int nToks) else { err = getPremiseVariable(tok, nToks, &n, &v1); - if ( err > 0 ) return err; + if (err > 0) + return err; } } // --- get relational operator n++; - if ( n >= nToks ) return error_setInpError(ERR_ITEMS, ""); + if (n >= nToks) + return error_setInpError(ERR_ITEMS, ""); relation = findExactMatch(tok[n], RelOpWords); - if ( relation < 0 ) return error_setInpError(ERR_KEYWORD, tok[n]); + if (relation < 0) + return error_setInpError(ERR_KEYWORD, tok[n]); // --- initialize RHS variable v2 v2.attribute = -1; v2.object = -1; v2.index = -1; n++; - if (n >= nToks) return error_setInpError(ERR_ITEMS, ""); + if (n >= nToks) + return error_setInpError(ERR_ITEMS, ""); // --- check for named RHS variable varIndex = getVariableIndex(tok[n]); @@ -624,7 +1174,8 @@ int addPremise(int r, int type, char* tok[], int nToks) if (obj >= 0) { err = getPremiseVariable(tok, nToks, &n, &v2); - if ( err > 0 ) return ERR_RULE; + if (err > 0) + return ERR_RULE; if (exprIndex < 0 && v1.attribute != v2.attribute) report_writeWarningMsg(WARN11, Rules[r].ID); } @@ -633,25 +1184,28 @@ int addPremise(int r, int type, char* tok[], int nToks) 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; + 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 = (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; - p->value = value; - p->next = NULL; - if ( Rules[r].firstPremise == NULL ) + p->lhsVar = v1; + p->rhsVar = v2; + p->relation = relation; + p->value = value; + p->next = NULL; + if (Rules[r].firstPremise == NULL) { Rules[r].firstPremise = p; } @@ -663,146 +1217,162 @@ int addPremise(int r, int type, char* tok[], int nToks) return 0; } -//============================================================================= - -int getPremiseVariable(char* tok[], int nToks, int* k, struct TVariable* v) -// -// 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) used in a control rule. -// +/*! +* \brief Parses a variable (e.g., Node 123 Depth) used in a control rule. +* \param[in] tok Array of string tokens +* \param[in] nToks Number of tokens +* \param[in] k Index of current token +* \param[in, out] v Variable structure +* \return Error code; updates k to new current token and places identity of specified variable in v +*/ +int getPremiseVariable(char *tok[], int nToks, int *k, struct TVariable *v) { - int n = *k; - int object = -1; - int index = -1; - int obj, attrib; + int n = *k; + int object = -1; + int index = -1; + int obj, attrib; // --- get object type obj = findmatch(tok[n], ObjectWords); - if ( obj < 0 ) return error_setInpError(ERR_KEYWORD, tok[n]); + if (obj < 0) + return error_setInpError(ERR_KEYWORD, tok[n]); // --- get object index from its name n++; - if (n >= nToks) return error_setInpError(ERR_ITEMS, ""); + if (n >= nToks) + return error_setInpError(ERR_ITEMS, ""); switch (obj) { - case r_GAGE: + case r_GAGE: index = project_findObject(GAGE, tok[n]); - if (index < 0) return error_setInpError(ERR_NAME, tok[n]); + if (index < 0) + return error_setInpError(ERR_NAME, tok[n]); object = r_GAGE; break; - case r_NODE: + case r_NODE: index = project_findObject(NODE, tok[n]); - if ( index < 0 ) return error_setInpError(ERR_NAME, tok[n]); + if (index < 0) + return error_setInpError(ERR_NAME, tok[n]); object = r_NODE; break; - case r_LINK: - case r_CONDUIT: - case r_PUMP: - case r_ORIFICE: - case r_WEIR: - case r_OUTLET: + case r_LINK: + case r_CONDUIT: + case r_PUMP: + case r_ORIFICE: + case r_WEIR: + case r_OUTLET: index = project_findObject(LINK, tok[n]); - if ( index < 0 ) return error_setInpError(ERR_NAME, tok[n]); + if (index < 0) + return error_setInpError(ERR_NAME, tok[n]); object = r_LINK; break; - default: n--; + default: + n--; } n++; - if (n >= nToks) return error_setInpError(ERR_ITEMS, ""); + if (n >= nToks) + return error_setInpError(ERR_ITEMS, ""); // --- get attribute index from its name if (object == r_GAGE) attrib = getGageAttrib(tok[n]); else attrib = findmatch(tok[n], AttribWords); - if ( attrib < 0 ) return error_setInpError(ERR_KEYWORD, tok[n]); + if (attrib < 0) + return error_setInpError(ERR_KEYWORD, tok[n]); // --- check that attribute belongs to object type 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; - default: return error_setInpError(ERR_KEYWORD, tok[n]); - } + else if (obj == r_NODE) + switch (attrib) + { + case r_DEPTH: + case r_MAXDEPTH: + case r_HEAD: + case r_VOLUME: + case r_INFLOW: + break; + default: + return error_setInpError(ERR_KEYWORD, tok[n]); + } // --- check for link TIMEOPEN & TIMECLOSED attributes - else if ( object == r_LINK && index >= 0 && - ( (attrib == r_TIMEOPEN || attrib == r_TIMECLOSED) - )) + else if (object == r_LINK && index >= 0 && + ((attrib == r_TIMEOPEN || attrib == r_TIMECLOSED))) { - // nothing to do here + // nothing to do here } - else if ( obj == r_LINK || obj == r_CONDUIT ) switch (attrib) - { - case r_STATUS: - case r_DEPTH: - 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]); - } - else switch (attrib) - { - case r_TIME: - case r_DATE: - case r_CLOCKTIME: - case r_DAY: - case r_MONTH: - case r_DAYOFYEAR: break; - default: return error_setInpError(ERR_KEYWORD, tok[n]); - } + else if (obj == r_LINK || obj == r_CONDUIT) + switch (attrib) + { + case r_STATUS: + case r_DEPTH: + 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]); + } + else + switch (attrib) + { + case r_TIME: + case r_DATE: + case r_CLOCKTIME: + case r_DAY: + case r_MONTH: + case r_DAYOFYEAR: + break; + default: + return error_setInpError(ERR_KEYWORD, tok[n]); + } // --- populate variable structure - v->object = object; - v->index = index; + v->object = object; + v->index = index; v->attribute = attrib; *k = n; return 0; } -//============================================================================= - -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). -// +/*! +* \brief Determines the attribute code for a rain gage variable. +* \param[in] token String token +* \return Attribute code or -1 if an error occurred +* \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 getGageAttrib(char *token) { int attrib; @@ -819,190 +1389,201 @@ int getGageAttrib(char* token) return attrib; } -//============================================================================= - -int getPremiseValue(char* token, int attrib, double* value) -// -// Input: token = a string token -// attrib = index of a node/link attribute -// Output: value = attribute value; -// returns an error code; -// Purpose: parses the numerical value of a particular node/link attribute -// in the premise clause of a control rule. -// +/*! +* \brief Parses the numerical value of a particular node/link attribute +* in the premise clause of a control rule. +* \param[in] token String token +* \param[in] attrib Index of a node/link attribute +* \param[out] value Attribute value +* \return Error code +*/ +int getPremiseValue(char *token, int attrib, double *value) { - char strDate[25]; + char strDate[25]; switch (attrib) { - case r_STATUS: + case r_STATUS: *value = findmatch(token, StatusWords); - if ( *value < 0.0 ) *value = findmatch(token, ConduitWords); - if ( *value < 0.0 ) return error_setInpError(ERR_KEYWORD, token); + if (*value < 0.0) + *value = findmatch(token, ConduitWords); + if (*value < 0.0) + return error_setInpError(ERR_KEYWORD, token); break; - case r_TIME: - case r_CLOCKTIME: - case r_TIMEOPEN: - case r_TIMECLOSED: - if ( !datetime_strToTime(token, value) ) + case r_TIME: + case r_CLOCKTIME: + case r_TIMEOPEN: + case r_TIMECLOSED: + if (!datetime_strToTime(token, value)) return error_setInpError(ERR_DATETIME, token); break; - case r_DATE: - if ( !datetime_strToDate(token, value) ) + case r_DATE: + if (!datetime_strToDate(token, value)) return error_setInpError(ERR_DATETIME, token); break; - case r_DAY: - if ( !getDouble(token, value) ) + case r_DAY: + if (!getDouble(token, value)) return error_setInpError(ERR_NUMBER, token); - if ( *value < 1.0 || *value > 7.0 ) - return error_setInpError(ERR_DATETIME, token); + if (*value < 1.0 || *value > 7.0) + return error_setInpError(ERR_DATETIME, token); break; - case r_MONTH: - if ( !getDouble(token, value) ) + case r_MONTH: + if (!getDouble(token, value)) return error_setInpError(ERR_NUMBER, token); - if ( *value < 1.0 || *value > 12.0 ) - return error_setInpError(ERR_DATETIME, token); + if (*value < 1.0 || *value > 12.0) + return error_setInpError(ERR_DATETIME, token); break; - case r_DAYOFYEAR: + case r_DAYOFYEAR: sstrncpy(strDate, token, 6); sstrcat(strDate, "/1947", 25); - if ( datetime_strToDate(strDate, value) ) + if (datetime_strToDate(strDate, value)) { *value = datetime_dayOfYear(*value); } - else if ( !getDouble(token, value) || *value < 1 || *value > 365 ) + else if (!getDouble(token, value) || *value < 1 || *value > 365) return error_setInpError(ERR_DATETIME, token); break; - - default: if ( !getDouble(token, value) ) - return error_setInpError(ERR_NUMBER, token); + + default: + if (!getDouble(token, value)) + return error_setInpError(ERR_NUMBER, token); } return 0; } -//============================================================================= - -int addAction(int r, char* tok[], int nToks) -// -// Input: r = control rule index -// tok = array of string tokens containing action statement -// nToks = number of string tokens -// Output: returns an error code -// Purpose: adds a new action to a control rule. -// +/*! +* \brief Adds a new action to a control rule. +* \param[in] r Rule index +* \param[in] tok Array of string tokens containing action statement +* \param[in] nToks Number of string tokens +* \return Error code +*/ +int addAction(int r, char *tok[], int nToks) { - int obj, link, attrib; - int curve = -1, tseries = -1; - int n; - int err; + int obj, link, attrib; + int curve = -1, tseries = -1; + int n; + int err; double values[] = {1.0, 0.0, 0.0}; - struct TAction* a; + struct TAction *a; // --- check for proper number of tokens - if ( nToks < 6 ) return error_setInpError(ERR_ITEMS, ""); + if (nToks < 6) + return error_setInpError(ERR_ITEMS, ""); // --- check for valid object type obj = findmatch(tok[1], ObjectWords); - if ( obj != r_LINK && obj != r_CONDUIT && obj != r_PUMP && - obj != r_ORIFICE && obj != r_WEIR && obj != r_OUTLET ) + if (obj != r_LINK && obj != r_CONDUIT && obj != r_PUMP && + obj != r_ORIFICE && obj != r_WEIR && obj != r_OUTLET) return error_setInpError(ERR_KEYWORD, tok[1]); // --- check that object name exists and is of correct type link = project_findObject(LINK, tok[2]); - if ( link < 0 ) return error_setInpError(ERR_NAME, tok[2]); + if (link < 0) + return error_setInpError(ERR_NAME, tok[2]); switch (obj) { - case r_CONDUIT: - if ( Link[link].type != CONDUIT ) + case r_CONDUIT: + if (Link[link].type != CONDUIT) return error_setInpError(ERR_NAME, tok[2]); break; - case r_PUMP: - if ( Link[link].type != PUMP ) + case r_PUMP: + if (Link[link].type != PUMP) return error_setInpError(ERR_NAME, tok[2]); break; - case r_ORIFICE: - if ( Link[link].type != ORIFICE ) + case r_ORIFICE: + if (Link[link].type != ORIFICE) return error_setInpError(ERR_NAME, tok[2]); break; - case r_WEIR: - if ( Link[link].type != WEIR ) + case r_WEIR: + if (Link[link].type != WEIR) return error_setInpError(ERR_NAME, tok[2]); break; - case r_OUTLET: - if ( Link[link].type != OUTLET ) + case r_OUTLET: + if (Link[link].type != OUTLET) return error_setInpError(ERR_NAME, tok[2]); break; } // --- check for valid attribute name attrib = findmatch(tok[3], AttribWords); - if ( attrib < 0 ) return error_setInpError(ERR_KEYWORD, tok[3]); + if (attrib < 0) + return error_setInpError(ERR_KEYWORD, tok[3]); // --- get control action setting - if ( obj == r_CONDUIT ) + if (obj == r_CONDUIT) { - if ( attrib == r_STATUS ) + if (attrib == r_STATUS) { values[0] = findmatch(tok[5], ConduitWords); - if ( values[0] < 0.0 ) + if (values[0] < 0.0) return error_setInpError(ERR_KEYWORD, tok[5]); } - else return error_setInpError(ERR_KEYWORD, tok[3]); + else + return error_setInpError(ERR_KEYWORD, tok[3]); } - else if ( obj == r_PUMP ) + else if (obj == r_PUMP) { - if ( attrib == r_STATUS ) + if (attrib == r_STATUS) { values[0] = findmatch(tok[5], StatusWords); - if ( values[0] < 0.0 ) + if (values[0] < 0.0) return error_setInpError(ERR_KEYWORD, tok[5]); } - else if ( attrib == r_SETTING ) + else if (attrib == r_SETTING) { err = setActionSetting(tok, nToks, &curve, &tseries, &attrib, values); - if ( err > 0 ) return err; + if (err > 0) + return err; } - else return error_setInpError(ERR_KEYWORD, tok[3]); + else + return error_setInpError(ERR_KEYWORD, tok[3]); } - else if ( obj == r_ORIFICE || obj == r_WEIR || obj == r_OUTLET ) + else if (obj == r_ORIFICE || obj == r_WEIR || obj == r_OUTLET) { - if ( attrib == r_SETTING ) + if (attrib == r_SETTING) { - err = setActionSetting(tok, nToks, &curve, &tseries, - &attrib, values); - if ( err > 0 ) return err; - if ( attrib == r_SETTING - && (values[0] < 0.0 || values[0] > 1.0) ) - return error_setInpError(ERR_NUMBER, tok[5]); + err = setActionSetting(tok, nToks, &curve, &tseries, + &attrib, values); + if (err > 0) + return err; + if (attrib == r_SETTING && (values[0] < 0.0 || values[0] > 1.0)) + return error_setInpError(ERR_NUMBER, tok[5]); } - else return error_setInpError(ERR_KEYWORD, tok[3]); + else + return error_setInpError(ERR_KEYWORD, tok[3]); } - else return error_setInpError(ERR_KEYWORD, tok[1]); + else + return error_setInpError(ERR_KEYWORD, tok[1]); // --- check if another clause is on same line n = 6; - if ( curve >= 0 || tseries >= 0 ) n = 7; - if ( attrib == r_PID ) n = 9; - if ( n < nToks && findmatch(tok[n], RuleKeyWords) >= 0 ) return ERR_RULE; + if (curve >= 0 || tseries >= 0) + n = 7; + if (attrib == r_PID) + n = 9; + if (n < nToks && findmatch(tok[n], RuleKeyWords) >= 0) + return ERR_RULE; // --- create the action object - a = (struct TAction *) malloc(sizeof(struct TAction)); - if ( !a ) return ERR_MEMORY; - a->rule = r; - a->link = link; + a = (struct TAction *)malloc(sizeof(struct TAction)); + if (!a) + return ERR_MEMORY; + a->rule = r; + a->link = link; a->attribute = attrib; - a->curve = curve; - a->tseries = tseries; - a->value = values[0]; - if ( attrib == r_PID ) + a->curve = curve; + a->tseries = tseries; + a->value = values[0]; + if (attrib == r_PID) { a->kp = values[0]; a->ki = values[1]; @@ -1010,7 +1591,7 @@ int addAction(int r, char* tok[], int nToks) a->e1 = 0.0; a->e2 = 0.0; } - if ( InputState == r_THEN ) + if (InputState == r_THEN) { a->next = Rules[r].thenActions; Rules[r].thenActions = a; @@ -1023,51 +1604,54 @@ int addAction(int r, char* tok[], int nToks) return 0; } -//============================================================================= - -int setActionSetting(char* tok[], int nToks, int* curve, int* tseries, - int* attrib, double values[]) -// -// Input: tok = array of string tokens containing action statement -// nToks = number of string tokens -// Output: curve = index of controller curve -// tseries = index of controller time series -// attrib = r_PID if PID controller used -// values = values of control settings -// returns an error code -// Purpose: identifies how control actions settings are determined. -// +/*! +* \brief Identifies how control actions settings are determined. +* \param[in] tok Array of string tokens containing action statement +* \param[in] nToks Number of string tokens +* \param[in] curve Index of controller curve +* \param[in] tseries Index of controller time series +* \param[in] attrib r_PID if PID controller used +* \param[out] values Values of control settings +* \return Error code +*/ +int setActionSetting(char *tok[], int nToks, int *curve, int *tseries, + int *attrib, double values[]) { int k, m; // --- see if control action is determined by a Curve or Time Series - if (nToks < 6) return error_setInpError(ERR_ITEMS, ""); + if (nToks < 6) + return error_setInpError(ERR_ITEMS, ""); k = findmatch(tok[5], SettingTypeWords); - if ( k >= 0 && nToks < 7 ) return error_setInpError(ERR_ITEMS, ""); + if (k >= 0 && nToks < 7) + return error_setInpError(ERR_ITEMS, ""); switch (k) { // --- control determined by a curve - find curve index case r_CURVE: m = project_findObject(CURVE, tok[6]); - if ( m < 0 ) return error_setInpError(ERR_NAME, tok[6]); + if (m < 0) + return error_setInpError(ERR_NAME, tok[6]); *curve = m; break; // --- control determined by a time series - find time series index case r_TIMESERIES: m = project_findObject(TSERIES, tok[6]); - if ( m < 0 ) return error_setInpError(ERR_NAME, tok[6]); + if (m < 0) + return error_setInpError(ERR_NAME, tok[6]); *tseries = m; Tseries[m].refersTo = CONTROL; break; - // --- control determined by PID controller + // --- control determined by PID controller case r_PID: - if (nToks < 9) return error_setInpError(ERR_ITEMS, ""); - for (m=6; m<=8; m++) + if (nToks < 9) + return error_setInpError(ERR_ITEMS, ""); + for (m = 6; m <= 8; m++) { - if ( !getDouble(tok[m], &values[m-6]) ) + if (!getDouble(tok[m], &values[m - 6])) return error_setInpError(ERR_NUMBER, tok[m]); } *attrib = r_PID; @@ -1075,15 +1659,19 @@ int setActionSetting(char* tok[], int nToks, int* curve, int* tseries, // --- direct numerical control is used default: - if ( !getDouble(tok[5], &values[0]) ) + if (!getDouble(tok[5], &values[0])) return error_setInpError(ERR_NUMBER, tok[5]); } return 0; } -//============================================================================= - -void updateActionValue(struct TAction* a, DateTime currentTime, double dt) +/*! +* \brief Updates the value of actions found from Curves or Time Series. +* \param[in] a Action structure +* \param[in] currentTime Current time of simulation in days (days) +* \param[in] dt Time step (days) +*/ +void updateActionValue(struct TAction *a, DateTime currentTime, double dt) // // Input: a = an action object // currentTime = current simulation date/time (days) @@ -1092,34 +1680,33 @@ void updateActionValue(struct TAction* a, DateTime currentTime, double dt) // Purpose: updates value of actions found from Curves or Time Series. // { - if ( a->curve >= 0 ) + if (a->curve >= 0) { a->value = table_lookup(&Curve[a->curve], ControlValue); } - else if ( a->tseries >= 0 ) + else if (a->tseries >= 0) { a->value = table_tseriesLookup(&Tseries[a->tseries], currentTime, TRUE); } - else if ( a->attribute == r_PID ) + else if (a->attribute == r_PID) { a->value = getPIDSetting(a, dt); } } -//============================================================================= - -double getPIDSetting(struct TAction* a, double dt) -// -// Input: a = an action object -// dt = current time step (days) -// Output: returns a new link setting -// Purpose: computes a new setting for a link subject to a PID controller. -// -// Note: a->kp = gain coefficient, -// a->ki = integral time (minutes) -// a->k2 = derivative time (minutes) -// a->e1 = error from previous time step -// a->e2 = error from two time steps ago +/*! +* \brief Computes a new setting for a link subject to a PID controller. +* \param[in] a Action object +* \param[in] dt Current time step (days) +* \return New link setting value +* \note +* - a->kp = gain coefficient, +* - a->ki = integral time (minutes), +* - a->k2 = derivative time (minutes), +* - a->e1 = error from previous time step, +* - a->e2 = error from two time steps ago +*/ +double getPIDSetting(struct TAction *a, double dt) { double e0, setting; double p, i, d, update; @@ -1130,10 +1717,12 @@ double getPIDSetting(struct TAction* a, double dt) // --- determine relative error in achieving controller set point e0 = SetPoint - ControlValue; - if ( fabs(e0) > TINY ) + if (fabs(e0) > TINY) { - if ( SetPoint != 0.0 ) e0 = e0/SetPoint; - else e0 = e0/ControlValue; + if (SetPoint != 0.0) + e0 = e0 / SetPoint; + else + e0 = e0 / ControlValue; } // --- reset previous errors to 0 if controller gets stuck @@ -1146,11 +1735,14 @@ double getPIDSetting(struct TAction* a, double dt) // --- 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; + 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; + if (fabs(update) < tolerance) + update = 0.0; setting = Link[a->link].targetSetting + update; // --- update previous errors @@ -1158,75 +1750,74 @@ double getPIDSetting(struct TAction* a, double dt) a->e1 = e0; // --- check that new setting lies within feasible limits - if ( setting < 0.0 ) setting = 0.0; - if (Link[a->link].type != PUMP && setting > 1.0 ) setting = 1.0; + if (setting < 0.0) + setting = 0.0; + if (Link[a->link].type != PUMP && setting > 1.0) + setting = 1.0; return setting; } -//============================================================================= - -void updateActionList(struct TAction* a) -// -// Input: a = an action object -// Output: none -// Purpose: adds a new action to the list of actions to be taken. -// +/*! +* \brief Adds a new action to the list of actions to be taken. +* \param[in] a Action structure +*/ +void updateActionList(struct TAction *a) { - struct TActionList* listItem; - struct TAction* a1; + struct TActionList *listItem; + struct TAction *a1; double priority = Rules[a->rule].priority; // --- check if link referred to in action is already listed listItem = ActionList; - while ( listItem ) + while (listItem) { a1 = listItem->action; - if ( !a1 ) break; - if ( a1->link == a->link ) + if (!a1) + break; + if (a1->link == a->link) { // --- replace old action if new action has higher priority - if ( priority > Rules[a1->rule].priority ) listItem->action = a; + if (priority > Rules[a1->rule].priority) + listItem->action = a; return; } listItem = listItem->next; } // --- action not listed so add it to ActionList //5.2.1 - if ( !listItem ) + if (!listItem) { - listItem = (struct TActionList *) malloc(sizeof(struct TActionList)); + listItem = (struct TActionList *)malloc(sizeof(struct TActionList)); listItem->next = ActionList; ActionList = listItem; } listItem->action = a; } -//============================================================================= - +/*! +* \brief Executes all actions required by fired control rules. +* \param[in] currentTime Current simulation date/time +* \return Number of new actions taken +*/ int executeActionList(DateTime currentTime) -// -// Input: currentTime = current date/time of the simulation -// Output: returns number of new actions taken -// Purpose: executes all actions required by fired control rules. -// { - struct TActionList* listItem; - struct TActionList* nextItem; - struct TAction* a1; + struct TActionList *listItem; + struct TActionList *nextItem; + struct TAction *a1; int count = 0; listItem = ActionList; - while ( listItem ) + while (listItem) { a1 = listItem->action; - if ( !a1 ) break; - if ( a1->link >= 0 ) + if (!a1) + break; + if (a1->link >= 0) { - if ( Link[a1->link].targetSetting != a1->value ) + if (Link[a1->link].targetSetting != a1->value) { Link[a1->link].targetSetting = a1->value; - if ( RptFlags.controls && a1->curve < 0 - && a1->tseries < 0 && a1->attribute != r_PID ) + if (RptFlags.controls && a1->curve < 0 && a1->tseries < 0 && a1->attribute != r_PID) report_writeControlAction(currentTime, Link[a1->link].ID, a1->value, Rules[a1->rule].ID); count++; @@ -1238,249 +1829,288 @@ int executeActionList(DateTime currentTime) return count; } -//============================================================================= - -int evaluatePremise(struct TPremise* p, double tStep) -// -// Input: p = a control rule premise condition -// tStep = current time step (days) -// Output: returns TRUE if the condition is true or FALSE otherwise -// Purpose: evaluates the truth of a control rule premise condition. -// +/*! +* \brief Evaluates the truth of a control rule premise condition. +* \param[in] p Control rule premise condition +* \param[in] tStep Current time step (days) +* \return TRUE if the condition is true, FALSE otherwise +*/ +int evaluatePremise(struct TPremise *p, double tStep) { double lhsValue, rhsValue; - int result = FALSE; + int result = FALSE; // --- check if left hand side (lhs) of premise is an expression if (p->exprIndex >= 0) lhsValue = mathexpr_eval(Expression[p->exprIndex].expression, - getNamedVariableValue); + 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; + 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: case r_CLOCKTIME: - return compareTimes(lhsValue, p->relation, rhsValue, tStep/2.0); + return compareTimes(lhsValue, p->relation, rhsValue, tStep / 2.0); case r_TIMEOPEN: case r_TIMECLOSED: - result = compareTimes(lhsValue, p->relation, rhsValue, tStep/2.0); - ControlValue = lhsValue * 24.0; // convert time from days to hours + result = compareTimes(lhsValue, p->relation, rhsValue, tStep / 2.0); + ControlValue = lhsValue * 24.0; // convert time from days to hours return result; default: return compareValues(lhsValue, p->relation, rhsValue); } } -//============================================================================= - +/*! +* \brief Gets variable value +* \param[in] v Variable structure +* \return Variable value +*/ double getVariableValue(struct TVariable v) { - int i = -1; // a node index - int j = -1; // a link index + 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; + if (v.object == r_NODE) + i = v.index; + if (v.object == r_LINK) + j = v.index; - switch ( v.attribute ) + switch (v.attribute) { - case r_TIME: + case r_TIME: return ElapsedTime; - - case r_DATE: + + case r_DATE: return CurrentDate; - case r_CLOCKTIME: + case r_CLOCKTIME: return CurrentTime; - case r_DAY: + case r_DAY: return datetime_dayOfWeek(CurrentDate); - case r_MONTH: + case r_MONTH: return datetime_monthOfYear(CurrentDate); - case r_DAYOFYEAR: + case r_DAYOFYEAR: return datetime_dayOfYear(CurrentDate); - case r_STATUS: - if ( j < 0 || - (Link[j].type != CONDUIT && Link[j].type != PUMP) ) return MISSING; - else return Link[j].setting; - - case r_SETTING: - if ( j < 0 || (Link[j].type != PUMP && - Link[j].type != ORIFICE && - Link[j].type != WEIR) ) + case r_STATUS: + if (j < 0 || + (Link[j].type != CONDUIT && Link[j].type != PUMP)) + return MISSING; + else + return Link[j].setting; + + case r_SETTING: + if (j < 0 || (Link[j].type != PUMP && + Link[j].type != ORIFICE && + Link[j].type != WEIR)) + return MISSING; + else + return Link[j].setting; + + case r_FLOW: + 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; - else return Link[j].setting; - - case r_FLOW: - 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_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; + 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); - case r_VOLUME: - if ( i < 0 ) return MISSING; + case r_VOLUME: + if (i < 0) + return MISSING; return (Node[i].newVolume * UCF(VOLUME)); - case r_INFLOW: - if ( i < 0 ) return MISSING; - else return Node[i].newLatFlow*UCF(FLOW); + case r_INFLOW: + if (i < 0) + return MISSING; + else + return Node[i].newLatFlow * UCF(FLOW); - case r_TIMEOPEN: - if ( j < 0 ) return MISSING; - if ( Link[j].setting <= 0.0 ) return MISSING; - return CurrentDate + CurrentTime - Link[j].timeLastSet; + case r_TIMEOPEN: + if (j < 0) + return MISSING; + if (Link[j].setting <= 0.0) + return MISSING; + return CurrentDate + CurrentTime - Link[j].timeLastSet; - case r_TIMECLOSED: - if ( j < 0 ) return MISSING; - if ( Link[j].setting > 0.0 ) return MISSING; - return CurrentDate + CurrentTime - Link[j].timeLastSet; + case r_TIMECLOSED: + if (j < 0) + return MISSING; + if (Link[j].setting > 0.0) + return MISSING; + return CurrentDate + CurrentTime - Link[j].timeLastSet; - default: return MISSING; + default: + return MISSING; } } -//============================================================================= - +/*! +* \brief Retrieves the current or past rainfall amount for a rain gage. +* \param[in] v Rule premise variable for a rain gage +* \return Current or past rainfall amount +*/ 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; + 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); + else + return gage_getPastRain(v.index, v.attribute); } -//============================================================================= - +/*! +* \brief Evaluates the truth of a relation between two date/times. +* \param[in] lhsValue Date/time value on left hand side of relation +* \param[in] relation Relational operator code (see RuleRelation enumeration) +* \param[in] rhsValue Date/time value on right hand side of relation +* \param[in] halfStep 1/2 the current time step (days) +* \return TRUE if time relation is satisfied, FALSE otherwise +*/ int compareTimes(double lhsValue, int relation, double rhsValue, double halfStep) -// -// Input: lhsValue = date/time value on left hand side of relation -// relation = relational operator code (see RuleRelation enumeration) -// rhsValue = date/time value on right hand side of relation -// halfStep = 1/2 the current time step (days) -// Output: returns TRUE if time relation is satisfied -// Purpose: evaluates the truth of a relation between two date/times. -// { - if ( relation == EQ ) + if (relation == EQ) { - if ( lhsValue >= rhsValue - halfStep - && lhsValue < rhsValue + halfStep ) return TRUE; + if (lhsValue >= rhsValue - halfStep && lhsValue < rhsValue + halfStep) + return TRUE; return FALSE; } - else if ( relation == NE ) + else if (relation == NE) { - if ( lhsValue < rhsValue - halfStep - || lhsValue >= rhsValue + halfStep ) return TRUE; + if (lhsValue < rhsValue - halfStep || lhsValue >= rhsValue + halfStep) + return TRUE; return FALSE; } - else return compareValues(lhsValue, relation, rhsValue); + else + return compareValues(lhsValue, relation, rhsValue); } -//============================================================================= - +/*! +* \brief Evaluates the truth of a relation between two values. +* \param[in] lhsValue Value on left hand side of relation +* \param[in] relation Relational operator code (see RuleRelation enumeration) +* \param[in] rhsValue Value on right hand side of relation +* \return TRUE if relation is satisfied, FALSE otherwise +*/ int compareValues(double lhsValue, int relation, double rhsValue) -// Input: lhsValue = value on left hand side of relation -// relation = relational operator code (see RuleRelation enumeration) -// rhsValue = value on right hand side of relation -// Output: returns TRUE if relation is satisfied -// Purpose: evaluates the truth of a relation between two values. { SetPoint = rhsValue; ControlValue = lhsValue; switch (relation) { - case EQ: if ( lhsValue == rhsValue ) return TRUE; break; - case NE: if ( lhsValue != rhsValue ) return TRUE; break; - case LT: if ( lhsValue < rhsValue ) return TRUE; break; - case LE: if ( lhsValue <= rhsValue ) return TRUE; break; - case GT: if ( lhsValue > rhsValue ) return TRUE; break; - case GE: if ( lhsValue >= rhsValue ) return TRUE; break; + case EQ: + if (lhsValue == rhsValue) + return TRUE; + break; + case NE: + if (lhsValue != rhsValue) + return TRUE; + break; + case LT: + if (lhsValue < rhsValue) + return TRUE; + break; + case LE: + if (lhsValue <= rhsValue) + return TRUE; + break; + case GT: + if (lhsValue > rhsValue) + return TRUE; + break; + case GE: + if (lhsValue >= rhsValue) + return TRUE; + break; } return FALSE; } -//============================================================================= - +/*! +* \brief Clears the list of actions to be executed. +*/ void clearActionList(void) -// -// Input: none -// Output: none -// Purpose: clears the list of actions to be executed. -// { - struct TActionList* listItem; + struct TActionList *listItem; listItem = ActionList; - while ( listItem ) + while (listItem) { listItem->action = NULL; listItem = listItem->next; } } -//============================================================================= - -void deleteActionList(void) -// -// Input: none -// Output: none -// Purpose: frees the memory used to hold the list of actions to be executed. -// +/*! +* \brief Frees the memory used to hold the list of actions to be executed. +*/ +void deleteActionList(void) { - struct TActionList* listItem; - struct TActionList* nextItem; + struct TActionList *listItem; + struct TActionList *nextItem; listItem = ActionList; - while ( listItem ) + while (listItem) { nextItem = listItem->next; free(listItem); @@ -1489,65 +2119,58 @@ void deleteActionList(void) ActionList = NULL; } -//============================================================================= - -void deleteRules(void) -// -// Input: none -// Output: none -// Purpose: frees the memory used for all of the control rules. -// +/*! +* \brief Frees the memory used for all of the control rules. +*/ +void deleteRules(void) { - struct TPremise* p; - struct TPremise* pnext; - struct TAction* a; - struct TAction* anext; - int r; - for (r=0; rnext; - free(p); - p = pnext; - } - a = Rules[r].thenActions; - while (a ) - { - anext = a->next; - free(a); - a = anext; - } - a = Rules[r].elseActions; - while (a ) - { - anext = a->next; - free(a); - a = anext; - } - } - FREE(Rules); - RuleCount = 0; + struct TPremise *p; + struct TPremise *pnext; + struct TAction *a; + struct TAction *anext; + int r; + for (r = 0; r < RuleCount; r++) + { + p = Rules[r].firstPremise; + while (p) + { + pnext = p->next; + free(p); + p = pnext; + } + a = Rules[r].thenActions; + while (a) + { + anext = a->next; + free(a); + a = anext; + } + a = Rules[r].elseActions; + while (a) + { + anext = a->next; + free(a); + a = anext; + } + } + FREE(Rules); + RuleCount = 0; } -//============================================================================= - -int findExactMatch(char *s, char *keyword[]) -// -// Input: s = character string -// keyword = array of keyword strings -// Output: returns index of keyword which matches s or -1 if no match found -// Purpose: finds exact match between string and array of keyword strings. -// +/*! +* \brief Finds the exact match between a string and an array of keyword strings. +* \param[in] s Character string +* \param[in] keyword Array of keyword strings +* \return Index of keyword which matches s or -1 if no match found +*/ +int findExactMatch(char *s, char *keyword[]) { - int i = 0; - while (keyword[i] != NULL) - { - if ( strcomp(s, keyword[i]) ) return(i); - i++; - } - return(-1); + int i = 0; + while (keyword[i] != NULL) + { + if (strcomp(s, keyword[i])) + return (i); + i++; + } + return (-1); } - -//============================================================================= diff --git a/src/solver/datetime.h b/src/solver/datetime.h index 98b87b48e..b45ec112a 100644 --- a/src/solver/datetime.h +++ b/src/solver/datetime.h @@ -1,72 +1,221 @@ -//----------------------------------------------------------------------------- -// datetime.h -// -// Project: EPA SWMM5 -// 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 -// equivalent to a double floating point type. -// -// The integral part of a DateTime value is the number of days that have -// passed since 12/31/1899. The fractional part of a DateTime value is the -// fraction of a 24 hour day that has elapsed. -// -// Update History -// ============== -// Build 5.1.011: -// - New getTimeStamp function added. -//----------------------------------------------------------------------------- +/*! +* \file datetime.h +* \brief Header file for date and time functions. +* \author L. Rossman +* \date Created: 2021-11-01 +* \date Last updated: 2024-12-30 +* \version 5.3.0 +* \details +* The DateTime type is used to store date and time values. It is +* equivalent to a double floating point type. +* +* The integral part of a DateTime value is the number of days that have +* passed since 12/31/1899. The fractional part of a DateTime value is the +* fraction of a 24 hour day that has elapsed. +* +* Update History +* ============== +* Build 5.1.011: +* - New getTimeStamp function added. +*/ #ifndef DATETIME_H #define DATETIME_H - +/*! +* \typedef DateTime +* \brief Type for storing date and time values. +*/ typedef double DateTime; +/*! +* \def Y_M_D +* \brief Date format: year, month, day +*/ #define Y_M_D 0 + +/*! +* \def M_D_Y +* \brief Date format: month, day, year +*/ #define M_D_Y 1 + +/*! +* \def D_M_Y +* \brief Date format: day, month, year +*/ #define D_M_Y 2 + +/*! +* \def NO_DATE +* \brief No date value +*/ #define NO_DATE -693594 // 1/1/0001 + +/*! +* \def DATE_STR_SIZE +* \brief Size of a date string +*/ #define DATE_STR_SIZE 12 + +/*! +* \def TIME_STR_SIZE +* \brief Size of a time string +*/ #define TIME_STR_SIZE 9 + +/*! +* \def TIME_STAMP_SIZE +* \brief Size of a time stamp string +*/ #define TIME_STAMP_SIZE 21 -// Functions for encoding a date or time value to a DateTime value +/*! +* \brief Encodes a date values from year, month, and day to a DateTime value. +* \param[in] year Year +* \param[in] month Month +* \param[in] day Day +* \return DateTime value +*/ DateTime datetime_encodeDate(int year, int month, int day); + +/*! +* \brief Encodes a time value from hour, minute, and second to a DateTime value. +* \param[in] hour Hour +* \param[in] minute Minute +* \param[in] second Second +* \return DateTime value +*/ DateTime datetime_encodeTime(int hour, int minute, int second); -// Functions for decoding a DateTime value to a date and time +/*! +* \brief Decodes a DateTime value to year, month, and day. +* \param[in] date Date value +* \param[out] y Year +* \param[out] m Month +* \param[out] d Day +*/ void datetime_decodeDate(DateTime date, int* y, int* m, int* d); + +/*! +* \brief Decodes a DateTime value to hour, minute, and second. +* \param[in] time Time value +* \param[out] h Hour +* \param[out] m Minute +* \param[out] s Second +*/ void datetime_decodeTime(DateTime time, int* h, int* m, int* s); -// Function for finding day of week for a date (1 = Sunday) -// month of year, days per month, and hour of day +/*! +* \brief Finds the month of the year for a date. +* \param[in] date Date value +* \return Month of year +*/ int datetime_monthOfYear(DateTime date); + +/*! +* \brief Finds the day of the year for a date. +* \param[in] date Date value +* \return Day of year +*/ int datetime_dayOfYear(DateTime date); + +/*! +* \brief Finds the day of the week for a date. +* \param[in] date Date value +* \return Day of week (0 = Sunday, 1 = Monday, ..., 6 = Saturday) +*/ int datetime_dayOfWeek(DateTime date); + +/*! +* \brief Finds the hour of the day for a time. +* \param[in] time Time value +* \return Hour of day +*/ int datetime_hourOfDay(DateTime date); + +/*! +* \brief Finds the number of days in a month. +* \param[in] year Year +* \param[in] month Month +* \return Number of days in the month +*/ int datetime_daysPerMonth(int year, int month); -// Functions for converting a DateTime value to a string +/*! +* \brief Converts a DateTime value to a string. +* \param[in] date Date value +* \param[out] s String to store the date +*/ void datetime_dateToStr(DateTime date, char* s); + +/*! +* \brief Converts a DateTime value to a string. +* \param[in] time Time value +* \param[out] s String to store the time +*/ void datetime_timeToStr(DateTime time, char* s); -void datetime_getTimeStamp(int fmt, DateTime aDate, int stampSize, - char* timeStamp); -// Functions for converting a string date or time to a DateTime value +/*! +* \brief Converts a DateTime value to a string. +* \param[in] date Date value +* \param[in] time Time value +* \param[out] s String to store the date and time +*/ +void datetime_getTimeStamp(int fmt, DateTime aDate, int stampSize, char* timeStamp); + +/*! +* \brief Finds the month of the year for a date. +* \param[in] s String date +* \return Month of year +*/ int datetime_findMonth(char* s); + +/*! +* \brief Converts a string to a DateTime value. +* \param[in] s String date +* \param[out] d DateTime value +* \return 1 if successful, 0 if not +*/ int datetime_strToDate(char* s, DateTime* d); + +/*! +* \brief Converts a string to a DateTime value. +* \param[in] s String time +* \param[out] t DateTime value +* \return 1 if successful, 0 if not +*/ int datetime_strToTime(char* s, DateTime* t); -// Function for setting date format +/*! +* \brief Sets the date format. +* \param[in] fmt Date format +*/ void datetime_setDateFormat(int fmt); -// Functions for adding and subtracting dates +/*! +* \brief Adds seconds to a DateTime value. +* \param[in] date1 Date value +* \param[in] seconds Number of seconds to add +* \return New DateTime value +*/ DateTime datetime_addSeconds(DateTime date1, double seconds); + +/*! +* \brief Adds days to a DateTime value. +* \param[in] date1 Date value +* \param[in] days Number of days to add +* \return New DateTime value +*/ DateTime datetime_addDays(DateTime date1, DateTime date2); -long datetime_timeDiff(DateTime date1, DateTime date2); + +/*! +* \brief Finds the difference in seconds between two DateTime values. +* \param[in] date1 First date value +* \param[in] date2 Second date value +* \return Number of seconds +*/ +long datetime_timeDiff(DateTime date1, DateTime date2); #endif //DATETIME_H diff --git a/src/solver/dwflow.c b/src/solver/dwflow.c index 25b2b874a..6014be382 100644 --- a/src/solver/dwflow.c +++ b/src/solver/dwflow.c @@ -52,18 +52,15 @@ static double getHydRad(TXsect* xsect, double y); static double checkNormalFlow(int j, double q, double y1, double y2, double a1, double r1); -//============================================================================= - -void dwflow_findConduitFlow(int j, int steps, double omega, double dt) -// -// Input: j = link index -// steps = number of iteration steps taken -// omega = under-relaxation parameter -// dt = time step (sec) -// Output: returns new flow value (cfs) -// Purpose: updates flow in conduit link by solving finite difference -// form of continuity and momentum equations. -// +/*! +* \brief Updates flow in conduit link by solving finite difference +* form of continuity and momentum equations. +* \param[in] linkIndex Link index +* \param[in] steps Number of iteration steps taken +* \param[in] omega Under-relaxation parameter +* \param[in] dt Time step (sec) +*/ +void dwflow_findConduitFlow(int linkIndex, int steps, double omega, double dt) { int k; // index of conduit int n1, n2; // indexes of end nodes @@ -87,28 +84,28 @@ void dwflow_findConduitFlow(int j, int steps, double omega, double dt) double denom; // denominator of flow update formula double q; // new flow value (cfs) double barrels; // number of barrels in conduit - TXsect* xsect = &Link[j].xsect; // ptr. to conduit's cross section data + TXsect* xsect = &Link[linkIndex].xsect; // ptr. to conduit's cross section data char isFull = FALSE; // TRUE if conduit flowing full char isClosed = FALSE; // TRUE if conduit closed // --- adjust isClosed status by any control action - if ( Link[j].setting == 0 ) isClosed = TRUE; + if ( Link[linkIndex].setting == 0 ) isClosed = TRUE; // --- get flow from last time step & previous iteration - k = Link[j].subIndex; + k = Link[linkIndex].subIndex; barrels = Conduit[k].barrels; - qOld = Link[j].oldFlow / barrels; + qOld = Link[linkIndex].oldFlow / barrels; qLast = Conduit[k].q1; 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; - n2 = Link[j].node2; - z1 = Node[n1].invertElev + Link[j].offset1; - z2 = Node[n2].invertElev + Link[j].offset2; + n1 = Link[linkIndex].node1; + n2 = Link[linkIndex].node2; + z1 = Node[n1].invertElev + Link[linkIndex].offset1; + z2 = Node[n2].invertElev + Link[linkIndex].offset2; h1 = Node[n1].newDepth + Node[n1].invertElev; h2 = Node[n2].newDepth + Node[n2].invertElev; h1 = MAX(h1, z1); @@ -137,7 +134,7 @@ void dwflow_findConduitFlow(int j, int steps, double omega, double dt) // --- find surface area contributions to upstream and downstream nodes // based on previous iteration's flow estimate - findSurfArea(j, qLast, length, &h1, &h2, &y1, &y2); + findSurfArea(linkIndex, qLast, length, &h1, &h2, &y1, &y2); // --- compute area at each end of conduit & hyd. radius at upstream end wSlot = getSlotWidth(xsect, y1); @@ -162,20 +159,20 @@ void dwflow_findConduitFlow(int j, int steps, double omega, double dt) y2 >= xsect->yFull) isFull = TRUE; // --- set new flow to zero if conduit is dry or if flap gate is closed - if ( Link[j].flowClass == DRY || - Link[j].flowClass == UP_DRY || - Link[j].flowClass == DN_DRY || + if ( Link[linkIndex].flowClass == DRY || + Link[linkIndex].flowClass == UP_DRY || + Link[linkIndex].flowClass == DN_DRY || isClosed || aMid <= FUDGE ) { Conduit[k].a1 = 0.5 * (a1 + a2); Conduit[k].q1 = 0.0;; Conduit[k].q2 = 0.0; - Link[j].dqdh = GRAVITY * dt * aMid / length * barrels; - Link[j].froude = 0.0; - Link[j].newDepth = MIN(yMid, Link[j].xsect.yFull); - Link[j].newVolume = Conduit[k].a1 * link_getLength(j) * barrels; - Link[j].newFlow = 0.0; + Link[linkIndex].dqdh = GRAVITY * dt * aMid / length * barrels; + Link[linkIndex].froude = 0.0; + Link[linkIndex].newDepth = MIN(yMid, Link[linkIndex].xsect.yFull); + Link[linkIndex].newVolume = Conduit[k].a1 * link_getLength(linkIndex) * barrels; + Link[linkIndex].newFlow = 0.0; return; } @@ -184,14 +181,14 @@ void dwflow_findConduitFlow(int j, int steps, double omega, double dt) if ( fabs(v) > MAXVELOCITY ) v = MAXVELOCITY * SGN(qLast); // --- compute Froude No. - Link[j].froude = link_getFroude(j, v, yMid); - if ( Link[j].flowClass == SUBCRITICAL && - Link[j].froude > 1.0 ) Link[j].flowClass = SUPCRITICAL; + Link[linkIndex].froude = link_getFroude(linkIndex, v, yMid); + if ( Link[linkIndex].flowClass == SUBCRITICAL && + Link[linkIndex].froude > 1.0 ) Link[linkIndex].flowClass = SUPCRITICAL; // --- find inertial damping factor (sigma) - if ( Link[j].froude <= 0.5 ) sigma = 1.0; - else if ( Link[j].froude >= 1.0 ) sigma = 0.0; - else sigma = 2.0 * (1.0 - Link[j].froude); + if ( Link[linkIndex].froude <= 0.5 ) sigma = 1.0; + else if ( Link[linkIndex].froude >= 1.0 ) sigma = 0.0; + else sigma = 2.0 * (1.0 - Link[linkIndex].froude); // --- get upstream-weighted area & hyd. radius based on damping factor // (modified version of R. Dickinson's slope weighting) @@ -210,7 +207,7 @@ void dwflow_findConduitFlow(int j, int steps, double omega, double dt) // --- compute terms of momentum eqn.: // --- 1. friction slope term if ( xsect->type == FORCE_MAIN && isFull ) - dq1 = dt * forcemain_getFricSlope(j, fabs(v), rMid); + dq1 = dt * forcemain_getFricSlope(linkIndex, fabs(v), rMid); else dq1 = dt * Conduit[k].roughFactor / pow(rWtd, 1.33333) * fabs(v); // --- 2. energy slope term @@ -229,33 +226,33 @@ void dwflow_findConduitFlow(int j, int steps, double omega, double dt) dq5 = 0.0; if ( Conduit[k].hasLosses ) { - dq5 = findLocalLosses(j, a1, a2, aMid, qLast) / 2.0 / length * dt; + dq5 = findLocalLosses(linkIndex, a1, a2, aMid, qLast) / 2.0 / length * dt; } // --- 6. term for evap and seepage losses per unit length - dq6 = link_getLossRate(j, DW, qLast, dt) * 2.5 * dt * v / link_getLength(j); + dq6 = link_getLossRate(linkIndex, DW, qLast, dt) * 2.5 * dt * v / link_getLength(linkIndex); // --- combine terms to find new conduit flow denom = 1.0 + dq1 + dq5; 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; + Link[linkIndex].dqdh = 1.0 / denom * GRAVITY * dt * aWtd / length * barrels; // --- check if any flow limitation applies - Link[j].inletControl = FALSE; - Link[j].normalFlow = FALSE; + Link[linkIndex].inletControl = FALSE; + Link[linkIndex].normalFlow = FALSE; if ( q > 0.0 ) { // --- check for inlet controlled culvert flow if ( xsect->culvertCode > 0 && !isFull ) - q = culvert_getInflow(j, q, h1); + q = culvert_getInflow(linkIndex, q, h1); // --- check for normal flow limitation based on surface slope & Fr - else if (NormalFlowLtd != NEITHER && y1 < Link[j].xsect.yFull && - ( Link[j].flowClass == SUBCRITICAL || - Link[j].flowClass == SUPCRITICAL )) - q = checkNormalFlow(j, q, y1, y2, a1, r1); + else if (NormalFlowLtd != NEITHER && y1 < Link[linkIndex].xsect.yFull && + ( Link[linkIndex].flowClass == SUBCRITICAL || + Link[linkIndex].flowClass == SUPCRITICAL )) + q = checkNormalFlow(linkIndex, q, y1, y2, a1, r1); } // --- apply under-relaxation weighting between new & old flows; @@ -267,13 +264,13 @@ void dwflow_findConduitFlow(int j, int steps, double omega, double dt) } // --- check if user-supplied flow limit applies - if ( Link[j].qLimit > 0.0 ) + if ( Link[linkIndex].qLimit > 0.0 ) { - if ( fabs(q) > Link[j].qLimit ) q = SGN(q) * Link[j].qLimit; + if ( fabs(q) > Link[linkIndex].qLimit ) q = SGN(q) * Link[linkIndex].qLimit; } // --- check for reverse flow with closed flap gate - if ( link_setFlapGate(j, n1, n2, q) ) q = 0.0; + if ( link_setFlapGate(linkIndex, n1, n2, q) ) q = 0.0; // --- do not allow flow out of a dry node // (as suggested by R. Dickinson) @@ -284,12 +281,12 @@ void dwflow_findConduitFlow(int j, int steps, double omega, double dt) Conduit[k].a1 = aMid; Conduit[k].q1 = q; Conduit[k].q2 = q; - Link[j].newDepth = MIN(yMid, xsect->yFull); + Link[linkIndex].newDepth = MIN(yMid, xsect->yFull); aMid = (a1 + a2) / 2.0; // 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; + Link[linkIndex].newVolume = aMid * link_getLength(linkIndex) * barrels; + Link[linkIndex].newFlow = q * barrels; } //============================================================================= diff --git a/src/solver/dynwave.c b/src/solver/dynwave.c index a59fdbd2f..b4bd7696e 100644 --- a/src/solver/dynwave.c +++ b/src/solver/dynwave.c @@ -114,14 +114,10 @@ static double getVariableStep(double maxStep); static double getLinkStep(double tMin, int *minLink); static double getNodeStep(double tMin, int *minNode); -//============================================================================= - +/*! +* \brief Initializes dynamic wave routing method. +*/ void dynwave_init() -// -// Input: none -// Output: none -// Purpose: initializes dynamic wave routing method. -// { int i, j; double z; @@ -174,14 +170,10 @@ void dynwave_close() FREE(Xnode); } -//============================================================================= - +/*! +* \brief Adjusts dynamic wave routing options. +*/ void dynwave_validate() -// -// Input: none -// Output: none -// Purpose: adjusts dynamic wave routing options. -// { if ( MinRouteStep > RouteStep ) MinRouteStep = RouteStep; if ( MinRouteStep < MINTIMESTEP ) MinRouteStep = MINTIMESTEP; @@ -192,14 +184,12 @@ void dynwave_validate() if ( MaxTrials == 0 ) MaxTrials = DEFAULT_MAXTRIALS; } -//============================================================================= - +/*! +* \brief Computes variable routing time step if applicable. +* \param[in] fixedStep User-supplied fixed time step (sec) +* \return Returns routing time step (sec) +*/ double dynwave_getRoutingStep(double fixedStep) -// -// Input: fixedStep = user-supplied fixed time step (sec) -// Output: returns routing time step (sec) -// Purpose: computes variable routing time step if applicable. -// { // --- use user-supplied fixed step if variable step option turned off // or if its smaller than the min. allowable variable time step @@ -221,15 +211,11 @@ double dynwave_getRoutingStep(double fixedStep) return VariableStep; } -//============================================================================= - +/*! +* \brief Routes flows through drainage network over current time step. +* \param[in] tStep Time step (sec) +*/ int dynwave_execute(double tStep) -// -// Input: links = array of topo sorted links indexes -// tStep = time step (sec) -// Output: returns number of iterations used -// Purpose: routes flows through drainage network over current time step. -// { int converged; diff --git a/src/solver/enums.h b/src/solver/enums.h index 853df790e..d4c65fba1 100644 --- a/src/solver/enums.h +++ b/src/solver/enums.h @@ -1,500 +1,1232 @@ -//----------------------------------------------------------------------------- -// enums.h -// -// Project: EPA SWMM5 -// Version: 5.2 -// Date: 06/01/22 (Build 5.2.1) -// Author: L. Rossman -// -// 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. -// Build 5.2.0: -// - Support added for Streets and Inlets. -// - Support added for variable speed pumps. -// - Support added for analytical storage shapes. -// Build 5.2.1: -// - Adds a NEITHER option to the NormalFlowType enumeration. -//----------------------------------------------------------------------------- +/*! + * \file enums.h + * \brief Header file for enumerated constants. + * \author L. Rossman + * \date Created: 2022-06-01 + * \date Last updated: 2024-12-30 + * \version 5.3.0 + * \details + * Enumerated constants used in the SWMM model. + * + * 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. + * Build 5.2.0: + * - Support added for Streets and Inlets. + * - Support added for variable speed pumps. + * - Support added for analytical storage shapes. + * Build 5.2.1: + * - Adds a NEITHER option to the NormalFlowType enumeration. + */ #ifndef ENUMS_H #define ENUMS_H +/*! +* \addtogroup SWMM_Enumerations SWMM Enumerations +* \brief Enumerated constants used in the SWMM model. +* \ingroup SWMM_Constants +* \{ +*/ -//------------------------------------- -// Names of major object types -//------------------------------------- - enum ObjectType { - GAGE, // rain gage - SUBCATCH, // subcatchment - NODE, // conveyance system node - LINK, // conveyance system link - POLLUT, // pollutant - LANDUSE, // land use category - TIMEPATTERN, // dry weather flow time pattern - CURVE, // generic table of values - TSERIES, // generic time series of values - CONTROL, // conveyance system control rules - TRANSECT, // irregular channel cross-section - AQUIFER, // groundwater aquifer - UNITHYD, // RDII unit hydrograph - 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 5 - enum NodeType { - JUNCTION, - OUTFALL, - STORAGE, - DIVIDER}; - -//------------------------------------- -// Names of Link sub-types -//------------------------------------- - #define MAX_LINK_TYPES 5 - enum LinkType { - CONDUIT, - PUMP, - ORIFICE, - WEIR, - OUTLET}; - -//------------------------------------- -// File types -//------------------------------------- - enum FileType { - RAINFALL_FILE, // rainfall file - RUNOFF_FILE, // runoff file - HOTSTART_FILE, // hotstart file - RDII_FILE, // RDII file - INFLOWS_FILE, // inflows interface file - OUTFLOWS_FILE}; // outflows interface file - -//------------------------------------- -// File usage types -//------------------------------------- - enum FileUsageType { - NO_FILE, // no file usage - SCRATCH_FILE, // use temporary scratch file - USE_FILE, // use previously saved file - SAVE_FILE}; // save file currently in use - -//------------------------------------- -// Rain gage data types -//------------------------------------- - enum GageDataType { - RAIN_TSERIES, // rainfall from user-supplied time series - RAIN_FILE}; // rainfall from external file - -//------------------------------------- -// Cross section shape types -//------------------------------------- - enum XsectType { - DUMMY, // 0 - CIRCULAR, // 1 closed - FILLED_CIRCULAR, // 2 closed - RECT_CLOSED, // 3 closed - RECT_OPEN, // 4 - TRAPEZOIDAL, // 5 - TRIANGULAR, // 6 - PARABOLIC, // 7 - POWERFUNC, // 8 - RECT_TRIANG, // 9 - RECT_ROUND, // 10 - MOD_BASKET, // 11 - HORIZ_ELLIPSE, // 12 closed - VERT_ELLIPSE, // 13 closed - ARCH, // 14 closed - EGGSHAPED, // 15 closed - HORSESHOE, // 16 closed - GOTHIC, // 17 closed - CATENARY, // 18 closed - SEMIELLIPTICAL, // 19 closed - BASKETHANDLE, // 20 closed - SEMICIRCULAR, // 21 closed - IRREGULAR, // 22 - CUSTOM, // 23 closed - FORCE_MAIN, // 24 closed - STREET_XSECT}; // 25 - -//------------------------------------- -// Measurement units types -//------------------------------------- - enum UnitsType { - US, // US units - SI}; // SI (metric) units - - enum FlowUnitsType { - CFS, // cubic feet per second - GPM, // gallons per minute - MGD, // million gallons per day - CMS, // cubic meters per second - LPS, // liters per second - MLD}; // million liters per day - - enum ConcUnitsType { - MG, // Milligrams / L - UG, // Micrograms / L - COUNT}; // Counts / L - -//-------------------------------------- -// Quantities requiring unit conversions -//-------------------------------------- - enum ConversionType { - RAINFALL, - RAINDEPTH, - EVAPRATE, - LENGTH, - LANDAREA, - VOLUME, - WINDSPEED, - TEMPERATURE, - MASS, - GWFLOW, - FLOW}; // Flow must always be listed last - -//------------------------------------- -// Computed subcatchment quantities -//------------------------------------- - #define MAX_SUBCATCH_RESULTS 9 - enum SubcatchResultType { - SUBCATCH_RAINFALL, // rainfall intensity - SUBCATCH_SNOWDEPTH, // snow depth - SUBCATCH_EVAP, // evap loss - SUBCATCH_INFIL, // infil loss - SUBCATCH_RUNOFF, // runoff flow rate - SUBCATCH_GW_FLOW, // groundwater flow rate to node - SUBCATCH_GW_ELEV, // elevation of saturated gw table - SUBCATCH_SOIL_MOIST, // soil moisture - SUBCATCH_WASHOFF}; // pollutant washoff concentration - -//------------------------------------- -// Computed node quantities -//------------------------------------- - #define MAX_NODE_RESULTS 7 - enum NodeResultType { - NODE_DEPTH, // water depth above invert - NODE_HEAD, // hydraulic head - NODE_VOLUME, // volume stored & ponded - NODE_LATFLOW, // lateral inflow rate - NODE_INFLOW, // total inflow rate - NODE_OVERFLOW, // overflow rate - NODE_QUAL}; // concentration of each pollutant - -//------------------------------------- -// Computed link quantities -//------------------------------------- - #define MAX_LINK_RESULTS 6 - enum LinkResultType { - LINK_FLOW, // flow rate - LINK_DEPTH, // flow depth - LINK_VELOCITY, // flow velocity - LINK_VOLUME, // link volume - LINK_CAPACITY, // ratio of area to full area - LINK_QUAL}; // concentration of each pollutant - -//------------------------------------- -// System-wide quantities -//------------------------------------- +/*! + * \enum ObjectType + * \brief Enumeration of object types used in SWMM5 + */ +enum ObjectType +{ + /*! \brief Rain gage */ + GAGE, // rain gage + /*! \brief Subcatchment */ + SUBCATCH, + /*! \brief Node */ + NODE, + /*! \brief Link */ + LINK, + /*! \brief Pollutant */ + POLLUT, + /*! \brief Land use category */ + LANDUSE, + /*! \brief Dry weather flow time pattern */ + TIMEPATTERN, + /*! \brief Generic table of values */ + CURVE, + /*! \brief Time series of values */ + TSERIES, + /*! \brief Convyance system control rules */ + CONTROL, + /*! \brief Irregular channel cross-section */ + TRANSECT, + /*! \brief Groundwater aquifer */ + AQUIFER, + /*! \brief RDII unit hydrograph */ + UNITHYD, + /*! \brief Snowmelt parameter set */ + SNOWMELT, + /*! \brief Custom conduit shape */ + SHAPE, + /*! \brief LID treatment units */ + LID, + /*! \brief Street */ + STREET, + /*! \brief Inlet */ + INLET, + /*! \brief Maximum number of object types */ + MAX_OBJ_TYPES +}; + +/*! + * \def MAX_NODE_TYPES + * \brief Maximum number of node types + */ +#define MAX_NODE_TYPES 5 + +/*! + * \enum NodeType + * \brief Enumeration of node sub-types used in SWMM5 + */ +enum NodeType +{ + /*! \brief Junction node */ + JUNCTION, + /*! \brief Outfall node */ + OUTFALL, + /*! \brief Storage node */ + STORAGE, + /*! \brief Divider node */ + DIVIDER +}; + +/*! + * \def MAX_LINK_TYPES + * \brief Maximum number of link types + */ +#define MAX_LINK_TYPES 5 + +/*! + * \enum LinkType + * \brief Enumeration of link sub-types used in SWMM5 + */ +enum LinkType +{ + /*! \brief Conduit link */ + CONDUIT, + /*! \brief Pump link */ + PUMP, + /*! \brief Orifice link */ + ORIFICE, + /*! \brief Weir link */ + WEIR, + /*! \brief Outlet link */ + OUTLET +}; + +/*! + * \enum FileType + * \brief Enumeration of file types used in SWMM5 + */ +enum FileType +{ + /*! \brief Rainfall file */ + RAINFALL_FILE, + /*! \brief Runoff file */ + RUNOFF_FILE, + /*! \brief Hotstart file */ + HOTSTART_FILE, + /*! \brief RDII file */ + RDII_FILE, + /*! \brief Infows interface file */ + INFLOWS_FILE, + /*! \brief Outflows interface file */ + OUTFLOWS_FILE +}; + + +/*! + * \enum FileUsageType + * \brief Enumeration of file usage types used in SWMM5 + */ +enum FileUsageType +{ + /*! \brief No file usage */ + NO_FILE, + /*! \brief Temporary scratch file */ + SCRATCH_FILE, + /*! \brief Use previously saved file */ + USE_FILE, + /*! \brief Save file currently in use */ + SAVE_FILE +}; + + +/*! + * \enum GageDataType + * \brief Enumeration of rain gage data types used in SWMM5 + */ +enum GageDataType +{ + /*! \brief Rainfall from user-supplied time series */ + RAIN_TSERIES, + /*! \brief Rainfall from external file */ + RAIN_FILE +}; + +/*! + * \enum XsectType + * \brief Enumeration of cross section shape types used in SWMM5 + */ +enum XsectType +{ + /*! \brief Dummy cross section */ + DUMMY, // 0 + /*! \brief Circular cross section */ + CIRCULAR, // 1 closed + /*! \brief Filled circular cross section */ + FILLED_CIRCULAR, // 2 closed + /*! \brief Rectangular cross section */ + RECT_CLOSED, // 3 closed + /*! \brief Rectangular open cross section */ + RECT_OPEN, // 4 + /*! \brief Trapezoidal cross section */ + TRAPEZOIDAL, // 5 + /*! \brief Triangular cross section */ + TRIANGULAR, // 6 + /*! \brief Parabolic cross section */ + PARABOLIC, // 7 + /*! \brief Power function cross section */ + POWERFUNC, // 8 + /*! \brief Rectangular triangular cross section */ + RECT_TRIANG, // + /*! \brief Rectangular round cross section */ + RECT_ROUND, // 10 + /*! \brief Modified Baskethandle cross section */ + MOD_BASKET, // 11 + /*! \brief Horizontal elliptical cross section */ + HORIZ_ELLIPSE, // 12 closed + /*! \brief Vertical elliptical cross section */ + VERT_ELLIPSE, // 13 closed + /*! \brief Arch cross section */ + ARCH, // 14 closed + /*! \brief Egg-shaped cross section */ + EGGSHAPED, // 15 closed + /*! \brief Horseshoe cross section */ + HORSESHOE, // 16 closed + /*! \brief Gothic cross section */ + GOTHIC, // 17 closed + /*! \brief Catenary cross section */ + CATENARY, // 18 closed + /*! \brief Semi-elliptical cross section */ + SEMIELLIPTICAL, // 19 closed + /*! \brief Baskethandle cross section */ + BASKETHANDLE, // 20 closed + /*! \brief Semi-circular cross section */ + SEMICIRCULAR, // 21 closed + /*! \brief Irregular cross section */ + IRREGULAR, // 22 + /*! \brief Custom cross section */ + CUSTOM, // 23 closed + /*! \brief Force main cross section */ + FORCE_MAIN, // 24 closed + /*! \brief Street cross section */ + STREET_XSECT // 25 +}; + +/*! + * \enum UnitsType + * \brief Enumeration of measurement units types used in SWMM5 + */ +enum UnitsType +{ + /*! \brief US units */ + US, // US units + /*! \brief SI units */ + SI, // SI (metric) units +}; + +/*! + * \enum FlowUnitsType + * \brief Enumeration of flow units types used in SWMM5 + */ +enum FlowUnitsType +{ + /*! \brief Cubic feet per second */ + CFS, + /*! \brief Gallons per minute */ + GPM, + /*! \brief Million gallons per day */ + MGD, + /*! \brief Cubic meters per second */ + CMS, + /*! \brief Liters per second */ + LPS, + /*! \brief Million liters per day */ + MLD, +}; + +enum ConcUnitsType +{ + /*! \brief Milligrams per liter */ + MG, + /*! \brief Micrograms per liter */ + UG, + /*! \brief Counts per liter */ + COUNT, +}; + +/*! +* \enum ConversionType +* \brief Quanties requiring unit conversions in SWMM5 +*/ +enum ConversionType +{ + /*! \brief Rainfall */ + RAINFALL, + /*! \brief Rain depth */ + RAINDEPTH, + /*! \brief Evaporation rate */ + EVAPRATE, + /*! \brief Length */ + LENGTH, + /*! \brief Land area */ + LANDAREA, + /*! \brief Volume */ + VOLUME, + /*! \brief Wind speed */ + WINDSPEED, + /*! \brief Temperature */ + TEMPERATURE, + /*! \brief Mass */ + MASS, + /*! \brief Groundwater flow */ + GWFLOW, + /*! \brief Flow rate */ + FLOW +}; + +/*! +* \def MAX_SUBCATCH_RESULTS +* \brief Maximum number of subcatchment results +*/ +#define MAX_SUBCATCH_RESULTS 9 + +/*! +* \enum SubcatchResultType +* \brief Enumeration of computed subcatchment quantities +*/ +enum SubcatchResultType +{ + /*! \brief Rainfall intensity */ + SUBCATCH_RAINFALL, + /*! \brief Snow depth */ + SUBCATCH_SNOWDEPTH, + /*! \brief Evaporation loss */ + SUBCATCH_EVAP, + /*! \brief Infiltration loss */ + SUBCATCH_INFIL, + /*! \brief Runoff flow rate */ + SUBCATCH_RUNOFF, + /*! \brief Groundwater flow rate */ + SUBCATCH_GW_FLOW, + /*! \brief Groundwater elevation of saturated table */ + SUBCATCH_GW_ELEV, + /*! \brief Soil moisture content */ + SUBCATCH_SOIL_MOIST, + /*! \brief Pollutant washoff concentration */ + SUBCATCH_WASHOFF, +}; + +/*! +* \def MAX_NODE_RESULTS +* \brief Maximum number of node results +*/ +#define MAX_NODE_RESULTS 7 + +/*! +* \enum NodeResultType +* \brief Enumeration of computed node quantities +*/ +enum NodeResultType +{ + /*! \brief Depth of water above node invert */ + NODE_DEPTH, + /*! \brief Hydraulic head at node */ + NODE_HEAD, + /*! \brief Volume of stored & ponded water */ + NODE_VOLUME, + /*! \brief Lateral inflow rate */ + NODE_LATFLOW, + /*! \brief Total inflow rate */ + NODE_INFLOW, + /*! \brief Overflow rate */ + NODE_OVERFLOW, + /*! \brief Concentration of each pollutant */ + NODE_QUAL, +}; + +/*! +* \def MAX_LINK_RESULTS +* \brief Maximum number of link results +*/ +#define MAX_LINK_RESULTS 6 + +/*! +* \enum LinkResultType +* \brief Enumeration of computed link quantities +*/ +enum LinkResultType +{ + /*! \brief Flow rate */ + LINK_FLOW, + /*! \brief Flow depth */ + LINK_DEPTH, + /*! \brief Flow velocity */ + LINK_VELOCITY, + /*! \brief Flow volume */ + LINK_VOLUME, + /*! \brief Ratio of area to full area */ + LINK_CAPACITY, + /*! \brief Concentration of each pollutant */ + LINK_QUAL, +}; + +/*! +* \def MAX_SYS_RESULTS +* \brief Maximum number of system results +*/ #define MAX_SYS_RESULTS 15 -enum SysFlowType { - SYS_TEMPERATURE, // air temperature - SYS_RAINFALL, // rainfall intensity - SYS_SNOWDEPTH, // snow depth - SYS_INFIL, // infil - SYS_RUNOFF, // runoff flow - SYS_DWFLOW, // dry weather inflow - SYS_GWFLOW, // ground water inflow - SYS_IIFLOW, // RDII inflow - SYS_EXFLOW, // external inflow - SYS_INFLOW, // total lateral inflow - SYS_FLOODING, // flooding outflow - SYS_OUTFLOW, // outfall outflow - SYS_STORAGE, // storage volume - SYS_EVAP, // evaporation - SYS_PET}; // potential ET - -//------------------------------------- -// Conduit flow classifications -//------------------------------------- - enum FlowClassType { - DRY, // dry conduit - UP_DRY, // upstream end is dry - DN_DRY, // downstream end is dry - SUBCRITICAL, // sub-critical flow - SUPCRITICAL, // super-critical flow - UP_CRITICAL, // free-fall at upstream end - DN_CRITICAL, // free-fall at downstream end - MAX_FLOW_CLASSES, // number of distinct flow classes - UP_FULL, // upstream end is full - DN_FULL, // downstream end is full - ALL_FULL}; // completely full - -//------------------------ -// Runoff flow categories -//------------------------ -enum RunoffFlowType { - RUNOFF_RAINFALL, // rainfall - RUNOFF_EVAP, // evaporation - RUNOFF_INFIL, // infiltration - RUNOFF_RUNOFF, // runoff - RUNOFF_DRAINS, // LID drain flow - RUNOFF_RUNON}; // runon from outfalls - -//------------------------------------- -// Surface pollutant loading categories -//------------------------------------- - enum LoadingType { - BUILDUP_LOAD, // pollutant buildup load - DEPOSITION_LOAD, // rainfall deposition load - SWEEPING_LOAD, // load removed by sweeping - BMP_REMOVAL_LOAD, // load removed by BMPs - INFIL_LOAD, // runon load removed by infiltration - RUNOFF_LOAD, // load removed by runoff - FINAL_LOAD}; // load remaining on surface - -//------------------------------------- -// Input data options -//------------------------------------- - enum RainfallType { - RAINFALL_INTENSITY, // rainfall expressed as intensity - RAINFALL_VOLUME, // rainfall expressed as volume - CUMULATIVE_RAINFALL}; // rainfall expressed as cumulative volume - - enum TempType { - NO_TEMP, // no temperature data supplied - TSERIES_TEMP, // temperatures come from time series - FILE_TEMP}; // temperatures come from file - -enum WindType { - MONTHLY_WIND, // wind speed varies by month - FILE_WIND}; // wind speed comes from file - - enum EvapType { - CONSTANT_EVAP, // constant evaporation rate - MONTHLY_EVAP, // evaporation rate varies by month - TIMESERIES_EVAP, // evaporation supplied by time series - TEMPERATURE_EVAP, // evaporation from daily temperature - FILE_EVAP, // evaporation comes from file - RECOVERY, // soil recovery pattern - DRYONLY}; // evap. allowed only in dry periods - - enum NormalizerType { - PER_AREA, // buildup is per unit of area - PER_CURB}; // buildup is per unit of curb length - - enum BuildupType { - NO_BUILDUP, // no buildup - POWER_BUILDUP, // power function buildup equation - EXPON_BUILDUP, // exponential function buildup equation - SATUR_BUILDUP, // saturation function buildup equation - EXTERNAL_BUILDUP}; // external time series buildup - - enum WashoffType { - NO_WASHOFF, // no washoff - EXPON_WASHOFF, // exponential washoff equation - RATING_WASHOFF, // rating curve washoff equation - EMC_WASHOFF}; // event mean concentration washoff - -enum SubAreaType { - IMPERV0, // impervious w/o depression storage - IMPERV1, // impervious w/ depression storage - PERV}; // pervious - - enum RunoffRoutingType { - TO_OUTLET, // perv & imperv runoff goes to outlet - TO_IMPERV, // perv runoff goes to imperv area - TO_PERV}; // imperv runoff goes to perv subarea - - enum RouteModelType { - NO_ROUTING, // no routing - SF, // steady flow model - KW, // kinematic wave model - EKW, // extended kin. wave model - DW}; // dynamic wave model - - enum ForceMainType { - H_W, // Hazen-Williams eqn. - D_W}; // Darcy-Weisbach eqn. - - enum OffsetType { - DEPTH_OFFSET, // offset measured as depth - ELEV_OFFSET}; // offset measured as elevation - - enum KinWaveMethodType { - NORMAL, // normal method - MODIFIED}; // modified method - -enum CompatibilityType { - SWMM5, // SWMM 5 weighting for area & hyd. radius - SWMM3, // SWMM 3 weighting - SWMM4}; // SWMM 4 weighting - - enum NormalFlowType { - SLOPE, // based on slope only - FROUDE, // based on Fr only - BOTH, // based on slope & Fr - NEITHER}; - - enum InertialDampingType { - NO_DAMPING, // no inertial damping - PARTIAL_DAMPING, // partial damping - FULL_DAMPING}; // full damping - - enum SurchargeMethodType { - EXTRAN, // original EXTRAN method - SLOT}; // Preissmann slot method - - enum InflowType { - EXTERNAL_INFLOW, // user-supplied external inflow - DRY_WEATHER_INFLOW, // user-supplied dry weather inflow - WET_WEATHER_INFLOW, // computed runoff inflow - GROUNDWATER_INFLOW, // computed groundwater inflow - RDII_INFLOW, // computed I&I inflow - FLOW_INFLOW, // inflow parameter is flow - CONCEN_INFLOW, // inflow parameter is pollutant concen. - MASS_INFLOW}; // inflow parameter is pollutant mass - - enum PatternType { - MONTHLY_PATTERN, // DWF multipliers for each month - DAILY_PATTERN, // DWF multipliers for each day of week - HOURLY_PATTERN, // DWF multipliers for each hour of day - WEEKEND_PATTERN}; // hourly multipliers for week end days - - enum OutfallType { - FREE_OUTFALL, // critical depth outfall condition - NORMAL_OUTFALL, // normal flow depth outfall condition - FIXED_OUTFALL, // fixed depth outfall condition - TIDAL_OUTFALL, // variable tidal stage outfall condition - TIMESERIES_OUTFALL}; // variable time series outfall depth - - enum StorageType { - TABULAR, // area v. depth from table - 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 - PLUG}; // plug flow reactor - - enum TreatmentType { - REMOVAL, // treatment stated as a removal - CONCEN}; // treatment stated as effluent concen. - - enum DividerType { - CUTOFF_DIVIDER, // diverted flow is excess of cutoff flow - TABULAR_DIVIDER, // table of diverted flow v. inflow - WEIR_DIVIDER, // diverted flow proportional to excess flow - OVERFLOW_DIVIDER}; // diverted flow is flow > full conduit flow - - enum PumpCurveType { - TYPE1_PUMP, // flow varies stepwise with wet well volume - 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 { - SIDE_ORIFICE, // side orifice - BOTTOM_ORIFICE}; // bottom orifice - - enum WeirType { - TRANSVERSE_WEIR, // transverse weir - SIDEFLOW_WEIR, // side flow weir - VNOTCH_WEIR, // V-notch (triangular) weir - TRAPEZOIDAL_WEIR, // trapezoidal weir - ROADWAY_WEIR}; // FHWA HDS-5 roadway weir - - enum CurveType { - STORAGE_CURVE, // surf. area v. depth for storage node - DIVERSION_CURVE, // diverted flow v. inflow for divider node - TIDAL_CURVE, // water elev. v. hour of day for outfall - 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 - 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) - PUMP5_CURVE}; // variable speed version of TYPE3 pump - - enum NodeInletType { + +/*! +* \enum SysResultType +* \brief Enumeration of computed system quantities +*/ +enum SysFlowType +{ + /*! \brief Air temperature */ + SYS_TEMPERATURE, + /*! \brief Rainfall intensity */ + SYS_RAINFALL, + /*! \brief Snow depth */ + SYS_SNOWDEPTH, + /*! \brief Infiltration loss */ + SYS_INFIL, + /*! \brief Runoff flow */ + SYS_RUNOFF, + /*! \brief Dry weather inflow */ + SYS_DWFLOW, + /*! \brief Groundwater inflow */ + SYS_GWFLOW, + /*! \brief RDII inflow */ + SYS_IIFLOW, + /*! \brief External inflow */ + SYS_EXFLOW, + /*! \brief Total lateral inflow */ + SYS_INFLOW, + /*! \brief Flooding outflow */ + SYS_FLOODING, + /*! \brief Outfall flow */ + SYS_OUTFLOW, + /*! \brief Storage volume */ + SYS_STORAGE, + /*! \brief Evaporation */ + SYS_EVAP, + /*! \brief Potential evapotranspiration */ + SYS_PET, +}; + +/*! +* \enum FlowClassType +* \brief Enumeration of conduit flow classifications +*/ +enum FlowClassType +{ + /*! \brief Dry conduit */ + DRY, + /*! \brief Upstream end is dry */ + UP_DRY, + /*! \brief Downstream end is dry */ + DN_DRY, + /*! \brief Subcritical flow */ + SUBCRITICAL, + /*! \brief Supercritical flow */ + SUPCRITICAL, + /*! \brief Free-fall at the upstream end */ + UP_CRITICAL, + /*! \brief Free-fall at the downstream end */ + DN_CRITICAL, + /*! \brief Number of distinct flow classes */ + MAX_FLOW_CLASSES, + /*! \brief Upstream end is full */ + UP_FULL, + /*! \brief Downstream end is full */ + DN_FULL, + /*! \brief Completely full conduit */ + ALL_FULL, +}; + + +/*! +* \enum RunoffFlowType +* \brief Enumeration of runoff flow categories +*/ +enum RunoffFlowType +{ + /*! \brief Rainfall */ + RUNOFF_RAINFALL, + /*! \brief Evaporation */ + RUNOFF_EVAP, + /*! \brief Infiltration */ + RUNOFF_INFIL, + /*! \brief Runoff */ + RUNOFF_RUNOFF, + /*! \brief LID drain flow */ + RUNOFF_DRAINS, + /*! \brief Runon from outfalls */ + RUNOFF_RUNON, +}; + +/*! +* \enum LoadingType +* \brief Enumeration of surface pollutant loading categories +*/ +enum LoadingType +{ + /*! \brief Pollutant buildup load */ + BUILDUP_LOAD, + /*! \brief Pollutant deposition load */ + DEPOSITION_LOAD, + /*! \brief Pollutant load removed by sweeping */ + SWEEPING_LOAD, + /*! \brief Pollutant load removed by BMPs */ + BMP_REMOVAL_LOAD, + /*! \brief Runon load removed by infiltration */ + INFIL_LOAD, + /*! \brief Pollutant load removed by runoff */ + RUNOFF_LOAD, + /*! \brief Pollutant load remaining on surface */ + FINAL_LOAD, +}; + +/*! +* \enum RainfallType +* \brief Enumeration of rainfall input data type options +*/ +enum RainfallType +{ + /*! \brief Rainfall expressed as intensity */ + RAINFALL_INTENSITY, + /*! \brief Rainfall expressed as volume */ + RAINFALL_VOLUME, + /*! \brief Rainfall expressed as cumulative volume */ + CUMULATIVE_RAINFALL, +}; + +/*! +* \enum TempType +* \brief Enumeration of temperature data type options +*/ +enum TempType +{ + /*! \brief No temperature data supplied */ + NO_TEMP, + /*! \brief Temperature data from time series */ + TSERIES_TEMP, + /*! \brief Temperature data from climate file */ + FILE_TEMP, +}; + +/*! +* \enum WindType +* \brief Enumeration of wind speed data type options +*/ +enum WindType +{ + /*! \brief Wind speed varies by month */ + MONTHLY_WIND, + /*! \brief Wind speed from climate file */ + FILE_WIND, +}; + +/*! +* \enum EvapType +* \brief Enumeration of evaporation data type options +*/ +enum EvapType +{ + /*! \brief Constant evaporation rate */ + CONSTANT_EVAP, + /*! \brief Evaporation rate varies by month */ + MONTHLY_EVAP, + /*! \brief Evaporation rate from time series */ + TIMESERIES_EVAP, + /*! \brief Evaporation rate from daily temperature */ + TEMPERATURE_EVAP, + /*! \brief Evaporation rate from climate file */ + FILE_EVAP, + /*! \brief Soil moisture recovery pattern */ + RECOVERY, + /*! \brief Evaporation allowed only in dry periods */ + DRYONLY, +}; + +/*! +* \enum NormalizerType +* \brief Enumeration of buildup normalization options +*/ +enum NormalizerType +{ + /*! \brief Normalized per unit of area */ + PER_AREA, + /*! \brief Normalized per unit of curb length */ + PER_CURB, +}; + +/*! +* \enum BuildupType +* \brief Enumeration of pollutant buildup function types +*/ +enum BuildupType +{ + /*! \brief No buildup */ + NO_BUILDUP, + /*! \brief Power function buildup */ + POWER_BUILDUP, + /*! \brief Exponential function buildup */ + EXPON_BUILDUP, + /*! \brief Saturation function buildup */ + SATUR_BUILDUP, + /*! \brief External time series buildup */ + EXTERNAL_BUILDUP, +}; + +/*! +* \enum WashoffType +* \brief Enumeration of pollutant washoff function types +*/ +enum WashoffType +{ + /*! \brief No washoff */ + NO_WASHOFF, + /*! \brief Exponential washoff equation */ + EXPON_WASHOFF, + /*! \brief Rating curve washoff equation */ + RATING_WASHOFF, + /*! \brief Event mean concentration washoff */ + EMC_WASHOFF, +}; + +/*! +* \enum SubAreaType +* \brief Enumeration of subcatchment area types +*/ +enum SubAreaType +{ + /*! \brief Impervious area with no depression storage */ + IMPERV0, + /*! \brief Impervious area with depression storage */ + IMPERV1, + /*! \brief Pervious area */ + PERV, +}; + +/*! +* \enum RunoffRoutingType +* \brief Enumeration of runoff routing types +*/ +enum RunoffRoutingType +{ + /*! \brief Pervious and impervious runoff goes to outlet */ + TO_OUTLET, + /*! \brief Pervious runoff goes to impervious area */ + TO_IMPERV, + /*! \brief Impervious runoff goes to pervious area */ + TO_PERV, +}; + +/*! +* \enum RouteModelType +* \brief Enumeration of routing model types +*/ +enum RouteModelType +{ + /*! \brief No routing */ + NO_ROUTING, + /*! \brief Steady flow routing */ + SF, + /*! \brief Kinematic wave routing */ + KW, + /*! \brief Extended kinematic wave routing */ + EKW, + /*! \brief Dynamic wave routing */ + DW, +}; + +/*! +* \enum ForceMainType +* \brief Enumeration of force main friction loss models +*/ +enum ForceMainType +{ + /*! \brief Hazen-Williams equation */ + H_W, + /*! \brief Darcy-Weisbach equation */ + D_W, +}; + +/*! +* \enum OffsetType +* \brief Enumeration of node offset types +*/ +enum OffsetType +{ + /*! \brief Offset measured as depth */ + DEPTH_OFFSET, + /*! \brief Offset measured as elevation */ + ELEV_OFFSET, +}; + +/*! +* \enum KinWaveMethodType +* \brief Enumeration of kinematic wave routing methods +*/ +enum KinWaveMethodType +{ + /*! \brief Normal method */ + NORMAL, + /*! \brief Modified method */ + MODIFIED, +}; + +/*! +* \enum CompatibilityType +* \brief Enumeration of SWMM5 compatibility types +*/ +enum CompatibilityType +{ + /*! \brief SWMM 5.0 weighting for area & hyd. radius */ + SWMM5, + /*! \brief SWMM 3 weighting */ + SWMM3, + /*! \brief SWMM 4 weighting */ + SWMM4, +}; + +/*! +* \enum NormalFlowType +* \brief Enumeration of normal flow computation methods +*/ +enum NormalFlowType +{ + /*! \brief Based on slope only */ + SLOPE, + /*! \brief Based on Froude number only */ + FROUDE, + /*! \brief Based on both slope & Froude number */ + BOTH, + /*! \brief Neither slope nor Froude number */ + NEITHER, +}; + +/*! +* \enum InertialDampingType +* \brief Enumeration of inertial damping options +*/ +enum InertialDampingType +{ + /*! \brief No inertial damping */ + NO_DAMPING, + /*! \brief Partial inertial damping */ + PARTIAL_DAMPING, + /*! \brief Full inertial damping */ + FULL_DAMPING, +}; + +/*! +* \enum SurchargeMethodType +* \brief Enumeration of surcharge method types +*/ +enum SurchargeMethodType +{ + /*! \brief Original EXTAN method */ + EXTRAN, + /*! \brief Preissmann slot method */ + SLOT, +}; + +/*! +* \enum InflowType +* \brief Enumeration of inflow method types +*/ +enum InflowType +{ + /*! \brief User supplied external inflow */ + EXTERNAL_INFLOW, + /*! \brief User supplied dry weather inflow */ + DRY_WEATHER_INFLOW, + /*! \brief Computed runoff inflow */ + WET_WEATHER_INFLOW, + /*! \brief Computed groundwater inflow */ + GROUNDWATER_INFLOW, + /*! \brief Computed I&I inflow */ + RDII_INFLOW, + /*! \brief Inflow parameter is flow */ + FLOW_INFLOW, + /*! \brief Inflow parameter is pollutant concen. */ + CONCEN_INFLOW, + /*! \brief Inflow parameter is pollutant mass */ + MASS_INFLOW, +}; + +/*! +* \enum PatternType +* \brief Enumeration of time pattern types +*/ +enum PatternType +{ + /*! \brief Dry weather flow multipliers for each month */ + MONTHLY_PATTERN, + /*! \brief Dry weather flow multipliers for each day of week */ + DAILY_PATTERN, + /*! \brief Dry weather flow multipliers for each hour of day */ + HOURLY_PATTERN, + /*! \brief Dry weather flow multipliers for weekend days */ + WEEKEND_PATTERN, +}; + +/*! +* \enum OutfallType +* \brief Enumeration of outfall types +*/ +enum OutfallType +{ + /*! \brief Critical depth outfall condition */ + FREE_OUTFALL, + /*! \brief Normal flow depth outfall condition */ + NORMAL_OUTFALL, + /*! \brief Fixed depth outfall condition */ + FIXED_OUTFALL, + /*! \brief Tidal stage outfall condition */ + TIDAL_OUTFALL, + /*! \brief Variable time series outfall depth */ + TIMESERIES_OUTFALL, +}; + +/*! +* \enum StorageType +* \brief Enumeration of storage node types +*/ +enum StorageType +{ + /*! \brief Area versus depth storage from table */ + TABULAR, + /*! \brief Area versus depth storage from function */ + FUNCTIONAL, + /*! \brief Area versus depth storage from elliptical cylinder */ + CYLINDRICAL, + /*! \brief Area versus depth storage from elliptical cone */ + CONICAL, + /*! \brief Area versus depth storage from elliptical paraboloid */ + PARABOLOID, + /*! \brief Area versus depth storage from rectangular pyramid */ + PYRAMIDAL, +}; + +/*! +* \enum ReactorType +* \brief Enumeration of reactor types +*/ +enum ReactorType +{ + /*! \brief Completely mixed reactor */ + CSTR, + /*! \brief Plug flow reactor */ + PLUG, +}; + +/*! +* \enum TreatmentType +* \brief Enumeration of pollutant treatment types +*/ +enum TreatmentType +{ + /*! \brief Treatment stated as a removal */ + REMOVAL, + /*! \brief Treatment stated as effluent concen. */ + CONCEN, +}; + +/*! +* \enum DividerType +* \brief Enumeration of flow divider types +*/ +enum DividerType +{ + /*! \brief Diverted flow is excess of cutoff flow */ + CUTOFF_DIVIDER, + /*! \brief Table of diverted flow versus inflow */ + TABULAR_DIVIDER, + /*! \brief Diverted flow proportional to excess flow */ + WEIR_DIVIDER, + /*! \brief Diverted flow if flow > full conduit flow */ + OVERFLOW_DIVIDER, +}; + +/*! +* \enum PumpCurveType +* \brief Enumeration of pump curve types +*/ +enum PumpCurveType +{ + /*! \brief Flow varies stepwise with wet well volume */ + TYPE1_PUMP, + /*! \brief Flow varies stepwise with inlet depth */ + TYPE2_PUMP, + /*! \brief Flow varies with head delivered */ + TYPE3_PUMP, + /*! \brief Flow varies with inlet depth */ + TYPE4_PUMP, + /*! \brief Variable speed version of TYPE3 pump */ + TYPE5_PUMP, + /*! \brief Outflow varies with inflow */ + IDEAL_PUMP, +}; + +/*! +* \enum OrificeType +* \brief Enumeration of orifice types +*/ +enum OrificeType +{ + /*! \brief Side orifice */ + SIDE_ORIFICE, + /*! \brief Bottom orifice */ + BOTTOM_ORIFICE, +}; + +/*! +* \enum WeirType +* \brief Enumeration of weir types +*/ +enum WeirType +{ + /*! \brief Transverse weir */ + TRANSVERSE_WEIR, + /*! \brief Side flow weir */ + SIDEFLOW_WEIR, + /*! \brief V-notch (triangular) weir */ + VNOTCH_WEIR, + /*! \brief Trapezoidal weir */ + TRAPEZOIDAL_WEIR, + /*! \brief FHWA HDS-5 roadway weir */ + ROADWAY_WEIR, +}; + +/*! +* \enum CurveType +* \brief Enumeration of curve types +*/ +enum CurveType +{ + /*! \brief Surface area v. depth for storage node */ + STORAGE_CURVE, + /*! \brief Diverted flow v. inflow for divider node */ + DIVERSION_CURVE, + /*! \brief Water elev. v. hour of day for outfall */ + TIDAL_CURVE, + /*! \brief Flow rate v. head for outlet link */ + RATING_CURVE, + /*! \brief Control setting v. controller variable */ + CONTROL_CURVE, + /*! \brief Width v. depth for custom x-section */ + SHAPE_CURVE, + /*! \brief Discharge coeff. v. head for weir */ + WEIR_CURVE, + /*! \brief Flow v. wet well volume for pump */ + PUMP1_CURVE, + /*! \brief Flow v. depth for pump (discrete) */ + PUMP2_CURVE, + /*! \brief Flow v. head for pump (continuous) */ + PUMP3_CURVE, + /*! \brief Flow v. depth for pump (continuous) */ + PUMP4_CURVE, + /*! \brief Variable speed version of TYPE3 pump */ + PUMP5_CURVE, +}; + +/*! +* \enum NodeInletType +* \brief Enumeration of node inlet types +*/ +enum NodeInletType +{ + /*! \brief No inlet */ NO_INLET, + /*! \brief Bypass flow inlet */ BYPASS, - CAPTURE - }; - - enum InputSectionType { - s_TITLE, s_OPTION, s_FILE, s_RAINGAGE, - s_TEMP, s_EVAP, s_SUBCATCH, s_SUBAREA, - s_INFIL, s_AQUIFER, s_GROUNDWATER, s_SNOWMELT, - s_JUNCTION, s_OUTFALL, s_STORAGE, s_DIVIDER, - s_CONDUIT, s_PUMP, s_ORIFICE, s_WEIR, - s_OUTLET, s_XSECTION, s_TRANSECT, s_LOSSES, - s_CONTROL, s_POLLUTANT, s_LANDUSE, s_BUILDUP, - s_WASHOFF, s_COVERAGE, s_INFLOW, s_DWF, - s_PATTERN, s_RDII, s_UNITHYD, s_LOADING, - s_TREATMENT, s_CURVE, s_TIMESERIES, s_REPORT, - 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_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, - REPORT_STEP, ALLOW_PONDING, INERT_DAMPING, - SLOPE_WEIGHTING, VARIABLE_STEP, NORMAL_FLOW_LTD, - LENGTHENING_STEP, MIN_SURFAREA, COMPATIBILITY, - SKIP_STEADY_STATE, TEMPDIR, IGNORE_RAINFALL, - FORCE_MAIN_EQN, LINK_OFFSETS, MIN_SLOPE, - 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}; - -enum NoYesType { - NO, - YES}; - -enum NoneAllType { - NONE, - ALL, - SOME}; - - -#endif //ENUMS_H + /*! \brief Capture flow inlet */ + CAPTURE, +}; + +/*! +* \enum InputSectionType +* \brief Enumeration of input file section types +*/ +enum InputSectionType +{ + /*! \brief Title section */ + s_TITLE, + /*! \brief Options section */ + s_OPTION, + /*! \brief Input files section */ + s_FILE, + /*! \brief Raingage section */ + s_RAINGAGE, + /*! \brief Temperature data section */ + s_TEMP, + /*! \brief Evaporation data section */ + s_EVAP, + /*! \brief Subcatchments data section */ + s_SUBCATCH, + /*! \brief Subareas data section */ + s_SUBAREA, + /*! \brief Infiltration data section */ + s_INFIL, + /*! \brief Aquifers data section */ + s_AQUIFER, + /*! \brief Groundwater units data section */ + s_GROUNDWATER, + /*! \brief Snowmelt data section */ + s_SNOWMELT, + /*! \brief Junctions data section */ + s_JUNCTION, + /*! \brief Outfalls data section */ + s_OUTFALL, + /*! \brief Storage units data section */ + s_STORAGE, + /*! \brief Dividers unit data section */ + s_DIVIDER, + /*! \brief Conduits data section */ + s_CONDUIT, + /*! \brief Pumps data section */ + s_PUMP, + /*! \brief Orifices data section */ + s_ORIFICE, + /*! \brief Weirs data section */ + s_WEIR, + /*! \brief Outlets data section */ + s_OUTLET, + /*! \brief Xsections data section */ + s_XSECTION, + /*! \brief Transects data section */ + s_TRANSECT, + /*! \brief Loss methods data section */ + s_LOSSES, + /*! \brief Control rules data section */ + s_CONTROL, + /*! \brief Pollutants data section */ + s_POLLUTANT, + /*! \brief Land uses data section */ + s_LANDUSE, + /*! \brief Pollutant buildup data section */ + s_BUILDUP, + /*! \brief Pollutant washoff data section */ + s_WASHOFF, + /*! \brief Coverages data section */ + s_COVERAGE, + /*! \brief Inflows data section */ + s_INFLOW, + /*! \brief DWF patterns data section */ + s_DWF, + /*! \brief Time patterns data section */ + s_PATTERN, + /*! \brief RDII data section */ + s_RDII, + /*! \brief Unit hydrographs data section */ + s_UNITHYD, + /*! \brief Pollutant loading data section */ + s_LOADING, + /*! \brief Treatment functions data section */ + s_TREATMENT, + /*! \brief Curve data section */ + s_CURVE, + /*! \brief Time series data section */ + s_TIMESERIES, + /*! \brief Reporting options data section */ + s_REPORT, + /*! \brief Coordinates data section */ + s_COORDINATE, + /*! \brief Vertices data section */ + s_VERTICES, + /*! \brief Polygons data section */ + s_POLYGON, + /*! \brief Labels data section */ + s_LABEL, + /*! \brief Symbols data section */ + s_SYMBOL, + /*! \brief Backdrop data section */ + s_BACKDROP, + /*! \brief Tags data section */ + s_TAG, + /*! \brief Profiles data section */ + s_PROFILE, + /*! \brief Map data section */ + s_MAP, + /*! \brief LID control data section */ + s_LID_CONTROL, + /*! \brief LID usage data section */ + s_LID_USAGE, + /*! \brief GWF data section */ + s_GWF, + /*! \brief Adjustments data section */ + s_ADJUST, + /*! \brief Event data section */ + s_EVENT, + /*! \brief Streets data section */ + s_STREET, + /*! \brief Inlets usage data section */ + s_INLET_USAGE, + /*! \brief Inlets data section */ + s_INLET, +}; + +/*! +* \enum InputOptionType +* \brief Enumeration of input file option types +*/ +enum InputOptionType +{ + /*! \brief Flow units */ + FLOW_UNITS, + /*! \brief Infiltration modeling method */ + INFIL_MODEL, + /*! \brief Flow routing method */ + ROUTE_MODEL, + /*! \brief Starting date of simulation */ + START_DATE, + /*! \brief Starting time of simulation */ + START_TIME, + /*! \brief Ending date of simulation */ + END_DATE, + /*! \brief Ending time of simulation */ + END_TIME, + /*! \brief Reporting start date */ + REPORT_START_DATE, + /*! \brief Reporting start time */ + REPORT_START_TIME, + /*! \brief Sweep start date */ + SWEEP_START, + /*! \brief Sweep end date */ + SWEEP_END, + /*! \brief Start dry days */ + START_DRY_DAYS, + /*! \brief Wet weather flow routing time step */ + WET_STEP, + /*! \brief Dry weather flow routing time step */ + DRY_STEP, + /*! \brief Flow routing time step */ + ROUTE_STEP, + /*! \brief Control rule time step */ + RULE_STEP, + /*! \brief Reporting time step */ + REPORT_STEP, + /*! \brief Allow ponding */ + ALLOW_PONDING, + /*! \brief Inertial dampening option */ + INERT_DAMPING, + /*! \brief Slope weighting for dynamic wave routing */ + SLOPE_WEIGHTING, + /*! \brief Variable step method */ + VARIABLE_STEP, + /*! \brief Normal flow limitation */ + NORMAL_FLOW_LTD, + /*! \brief Lengthening step for dynamic wave routing */ + LENGTHENING_STEP, + /*! \brief Minimum surface area for dynamic wave routing */ + MIN_SURFAREA, + /*! \brief Compatibility option */ + COMPATIBILITY, + /*! \brief Skip steady state option */ + SKIP_STEADY_STATE, + /*! \brief Temporary directory */ + TEMPDIR, + /*! \brief Ignore rainfall */ + IGNORE_RAINFALL, + /*! \brief Force main equation */ + FORCE_MAIN_EQN, + /*! \brief Link offsets */ + LINK_OFFSETS, + /*! \brief Minimum conduit slope */ + MIN_SLOPE, + /*! \brief Ignore snowmelt */ + IGNORE_SNOWMELT, + /*! \brief Ignore groundwater */ + IGNORE_GWATER, + /*! \brief Ignore routing */ + IGNORE_ROUTING, + /*! \brief Ignore quality */ + IGNORE_QUALITY, + /*! \brief Maximum number of iterations trials for dynamic wave routing solver */ + MAX_TRIALS, + /*! \brief Head convergence tolerance for dynamic wave routing solver */ + HEAD_TOL, + /*! \brief System flow tolerance for dynamic wave routing solver */ + SYS_FLOW_TOL, + /*! \brief Lateral inflow tolerance for dynamic wave routing solver */ + LAT_FLOW_TOL, + /*! \brief Ignore RDII */ + IGNORE_RDII, + /*! \brief Minimum route step */ + MIN_ROUTE_STEP, + /*! \brief Number of threads */ + NUM_THREADS, + /*! \brief Surcharge method */ + SURCHARGE_METHOD, +}; + +/*! +* \enum NoYesType +* \brief Enumeration of no/yes options +*/ +enum NoYesType +{ + /*! \brief No option */ + NO, + /*! \brief Yes option */ + YES, +}; + +/*! +* \enum NoneAllType +* \brief Enumeration of none/all/some options +*/ +enum NoneAllType +{ + /*! \brief None option */ + NONE, + /*! \brief All option */ + ALL, + /*! \brief Some option */ + SOME, +}; + +/*! +* \} +*/ + + +#endif // ENUMS_H diff --git a/src/solver/error.h b/src/solver/error.h index 595380daf..2bcea19dd 100644 --- a/src/solver/error.h +++ b/src/solver/error.h @@ -1,176 +1,306 @@ -//----------------------------------------------------------------------------- -// error.h -// -// Project: EPA SWMM5 -// Version: 5.2 -// Date: 11/01/21 (Build 5.2.0) -// Author: L. Rossman -// -// Error codes -// -// Update History: -// ============== -// Build 5.3.0: -// - Moved API error codes to swmm.h so that they can be accessed for -// interpretation -//----------------------------------------------------------------------------- +/*! +* \file error.h +* \brief Header file for error codes. +* \author L. Rossman +* \date Created: 2021-11-01 +* \date Last updated: 2024-12-30 +* \version 5.3.0 +* \details +* Error codes used in the SWMM model. +* +* Update history +* ============== +* Build 5.3.0 +* - Moved API error codes to swmm.h so that they can be accessed for interpretation +*/ #ifndef ERROR_H #define ERROR_H +/*! +* \addtogroup SWMM_Error_Constants SWMM Error Constants +* \brief General error constants used in the SWMM model. +* \ingroup SWMM_Constants +* \{ +*/ + + +/*! +* \enum ErrorType +* \brief Enumeration of error codes used in SWMM5 +*/ enum ErrorType { // ... Runtime Errors + /*! \brief No error */ ERR_NONE = 0, + /*! \brief Memory allocation error */ ERR_MEMORY = 101, + /*! \brief Kinematic wave routing error */ ERR_KINWAVE = 103, + /*! \brief ODE solver error */ ERR_ODE_SOLVER = 105, + /*! \brief Time step error */ ERR_TIMESTEP = 107, // ... Subcatchment/Aquifer Errors + /*! \brief Subcatchment has no outlet */ ERR_SUBCATCH_OUTLET = 108, + /*! \brief Aquifer parameter error */ ERR_AQUIFER_PARAMS = 109, + /*! \brief Ground elevation error */ ERR_GROUND_ELEV = 110, // ... Conduit/Pump Errors + /*! \brief Conduit length error */ ERR_LENGTH = 111, + /*! \brief Conduit elevation drop error */ ERR_ELEV_DROP = 112, + /*! \brief Conduit roughness error */ ERR_ROUGHNESS = 113, + /*! \brief Conduit barrel error */ ERR_BARRELS = 114, + /*! \brief Conduit slope error */ ERR_SLOPE = 115, + /*! \brief Conduit no cross section */ ERR_NO_XSECT = 117, + /*! \brief Conduit xsection error */ ERR_XSECT = 119, + /*! \brief No curve specified error */ ERR_NO_CURVE = 121, + /*! \brief Pump limits error */ ERR_PUMP_LIMITS = 122, // ... Topology Errors + /*! \brief Network loop error */ ERR_LOOP = 131, + /*! \brief Network multiple outlet error */ ERR_MULTI_OUTLET = 133, + /*! \brief Network dummy links error */ ERR_DUMMY_LINK = 134, // ... Node Errors + /*! \brief Error divider error */ ERR_DIVIDER = 135, + /*! \brief Divider link error */ ERR_DIVIDER_LINK = 136, + /*! \brief Weir divider error */ ERR_WEIR_DIVIDER = 137, + /*! \brief Node depth error */ ERR_NODE_DEPTH = 138, + /*! \brief Regulator error */ ERR_REGULATOR = 139, + /*! \brief Storage volume error */ ERR_STORAGE_VOLUME = 140, + /*! \brief Outfall error */ ERR_OUTFALL = 141, + /*! \brief Regulator shape error */ ERR_REGULATOR_SHAPE = 143, + /*! \brief No outlets error */ ERR_NO_OUTLETS = 145, // ... RDII Errors + /*! \brief RDII unit hydrograph time error */ ERR_UNITHYD_TIMES = 151, + /*! \brief RDII unit hydrograph ratios error */ ERR_UNITHYD_RATIOS = 153, + /*! \brief RDII area error */ ERR_RDII_AREA = 155, // ... Rain Gage Errors + /*! \brief Rain file conflict error */ ERR_RAIN_FILE_CONFLICT = 156, + /*! \brief Rain gage format error */ ERR_RAIN_GAGE_FORMAT = 157, + /*! \brief Rain gage time series error */ ERR_RAIN_GAGE_TSERIES = 158, + /*! \brief Rain gage interval error */ ERR_RAIN_GAGE_INTERVAL = 159, // ... Treatment Function Error + /*! \brief Cyclic treatment error */ ERR_CYCLIC_TREATMENT = 161, // ... Curve/Time Series Errors + /*! \brief Curve sequence error */ ERR_CURVE_SEQUENCE = 171, + /*! \brief Time series sequence error */ ERR_TIMESERIES_SEQUENCE = 173, // ... Snowmelt Errors + /*! \brief Snowmelt parameters error */ ERR_SNOWMELT_PARAMS = 181, + /*! \brief Snowpack parameters error */ ERR_SNOWPACK_PARAMS = 182, // ... LID Errors + /*! \brief LID type error */ ERR_LID_TYPE = 183, + /*! \brief LID layer error */ ERR_LID_LAYER = 184, + /*! \brief LID parameters error */ ERR_LID_PARAMS = 185, + /*! \brief LID areas error */ ERR_LID_AREAS = 187, + /*! \brief LID capture area error */ ERR_LID_CAPTURE_AREA = 188, // ... Simulation Date/Time Errors + /*! \brief Start date error */ ERR_START_DATE = 191, + /*! \brief Report start date error */ ERR_REPORT_DATE = 193, + /*! \brief Report time step error */ ERR_REPORT_STEP = 195, // ... Input Parser Errors + /*! \brief Input error */ ERR_INPUT = 200, + /*! \brief Line length error */ ERR_LINE_LENGTH = 201, + /*! \brief Error items error */ ERR_ITEMS = 203, + /*! \brief Keyword error */ ERR_KEYWORD = 205, + /*! \brief Duplicate ID error */ ERR_DUP_NAME = 207, + /*! \brief Name error */ ERR_NAME = 209, + /*! \brief Data value error */ ERR_NUMBER = 211, + /*! \brief Datetime error */ ERR_DATETIME = 213, + /*! \brief Rule error */ ERR_RULE = 217, + /*! \brief Transect unknown error */ ERR_TRANSECT_UNKNOWN = 219, + /*! \brief Transect sequence error */ ERR_TRANSECT_SEQUENCE = 221, + /*! \brief Transect too few error */ ERR_TRANSECT_TOO_FEW = 223, + /*! \brief Transect too many error */ ERR_TRANSECT_TOO_MANY = 225, + /*! \brief Transect Manning error */ ERR_TRANSECT_MANNING = 227, + /*! \brief Transect overbank error */ ERR_TRANSECT_OVERBANK = 229, + /*! \brief Transect no depth error */ ERR_TRANSECT_NO_DEPTH = 231, + /*! \brief Math expression error */ ERR_MATH_EXPR = 233, + /*! \brief Infiltration parameters error */ ERR_INFIL_PARAMS = 235, // ... File Name/Opening Errors + /*! \brief File name error */ ERR_FILE_NAME = 301, + /*! \brief Input file error */ ERR_INP_FILE = 303, + /*! \brief Report file error */ ERR_RPT_FILE = 305, + /*! \brief Output file error */ ERR_OUT_FILE = 307, + /*! \brief Output size error */ ERR_OUT_SIZE = 308, + /*! \brief Output write error */ ERR_OUT_WRITE = 309, + /*! \brief Output read error */ ERR_OUT_READ = 311, // ... Rain File Errors + /*! \brief Rain file scratch error */ ERR_RAIN_FILE_SCRATCH = 313, + /*! \brief Rain file open error */ ERR_RAIN_FILE_OPEN = 315, + /*! \brief Rain file data error */ ERR_RAIN_FILE_DATA = 317, + /*! \brief Rain file sequence error */ ERR_RAIN_FILE_SEQUENCE = 318, + /*! \brief Rain file format error */ ERR_RAIN_FILE_FORMAT = 319, + /*! \brief Rain interface format error */ ERR_RAIN_IFACE_FORMAT = 320, + /*! \brief Rain file gage error */ ERR_RAIN_FILE_GAGE = 321, // ... Runoff File Errors + /*! \brief Runoff file scratch error */ ERR_RUNOFF_FILE_OPEN = 323, + /*! \brief Runoff file format error */ ERR_RUNOFF_FILE_FORMAT = 325, + /*! \brief Runoff file end error */ ERR_RUNOFF_FILE_END = 327, + /*! \brief Runoff file read error */ ERR_RUNOFF_FILE_READ = 329, // ... Hotstart File Errors + /*! \brief Hotstart file scratch error */ ERR_HOTSTART_FILE_OPEN = 331, + /*! \brief Hotstart file format error */ ERR_HOTSTART_FILE_FORMAT = 333, + /*! \brief Hotstart file read error */ ERR_HOTSTART_FILE_READ = 335, // ... Climate File Errors + /*! \brief Climate file error */ ERR_NO_CLIMATE_FILE = 336, + /*! \brief Climate file open error */ ERR_CLIMATE_FILE_OPEN = 337, + /*! \brief Climate file read error */ ERR_CLIMATE_FILE_READ = 338, + /*! \brief Climate file end error */ ERR_CLIMATE_END_OF_FILE = 339, // ... RDII File Errors + /*! \brief RDII file scratch error */ ERR_RDII_FILE_SCRATCH = 341, + /*! \brief RDII file open error */ ERR_RDII_FILE_OPEN = 343, + /*! \brief RDII file format error */ ERR_RDII_FILE_FORMAT = 345, // ... Routing File Errors + /*! \brief Routing file open error */ ERR_ROUTING_FILE_OPEN = 351, + /*! \brief Routing file format error */ ERR_ROUTING_FILE_FORMAT = 353, + /*! \brief Routing file no match error */ ERR_ROUTING_FILE_NOMATCH = 355, + /*! \brief Routing file names error */ ERR_ROUTING_FILE_NAMES = 357, // ... Time Series File Errors + /*! \brief Table file open error */ ERR_TABLE_FILE_OPEN = 361, + /*! \brief Table file read error */ ERR_TABLE_FILE_READ = 363, // ... Runtime Errors + /*! \brief System error */ ERR_SYSTEM = 500, // ... Additional Errors + /*! \brief Maximum error message length */ MAXERRMSG = 1000, }; - +/*! +* \} +*/ + +/*! +* \brief Get the error message for a given error code. +* \param[in] i Error code +* \param[out] msg Error message +* \return Error message +*/ char* error_getMsg(int i, char* msg); + +/*! +* \brief Set an error code in the error manager. +* \param[in] errcode Error code +* \param[in] s Error message +* \return Error code +*/ int error_setInpError(int errcode, char* s); #endif //ERROR_H diff --git a/src/solver/exfil.h b/src/solver/exfil.h index 8da4da302..2cef6830d 100644 --- a/src/solver/exfil.h +++ b/src/solver/exfil.h @@ -1,35 +1,90 @@ -//----------------------------------------------------------------------------- -// exfil.h -// -// Project: EPA SWMM5 -// Version: 5.2 -// Date: 11/01/21 (Build 5.2.0) -// Author: L. Rossman -// -// Public interface for exfiltration functions. -//----------------------------------------------------------------------------- - +/*! +* \file exfil.h +* \brief Exfiltration object header file. +* \author L. Rossman +* \date Created: 2021-11-01 +* \date Last updated: 2024-12-30 +* \version 5.3.0 +* \details Include file for exfiltration functions. +*/ #ifndef EXFIL_H #define EXFIL_H -//---------------------------- -// EXFILTRATION OBJECT -//---------------------------- +/*! + * \defgroup Exfil_Enums_Structs_Variables_Functions Exfil File Constants, Enumerations, Structs, Shared Variables, and Functions + * \brief Exfile file constants, enumerations, structs, shared variables, and functions + * \{ + */ + +/*! + * \addtogroup Exfil_Structs Exfil File Structures + * \brief Exfil structures. + * \ingroup Exfil_Enums_Structs_Variables_Functions + * \{ + */ + +/*! +* \struct TExfil +* \brief Exfiltration object. +*/ typedef struct { + /*! \brief Green-Ampt infiltration parameters for bottom of exfiltration layer */ TGrnAmpt* btmExfil; + /*! \brief Green-Ampt infiltration parameters for bank of exfiltration layer */ TGrnAmpt* bankExfil; + /*! \brief Area of exfiltration layer */ double btmArea; + /*! \brief Bank minimum depth of exfiltration layer */ double bankMinDepth; + /*! \brief Bank maximum depth of exfiltration layer */ double bankMaxDepth; + /*! \brief Bank maximum area of exfiltration layer */ double bankMaxArea; } TExfil; -//----------------------------------------------------------------------------- -// Exfiltration Methods -//----------------------------------------------------------------------------- -int exfil_readStorageParams(int k, char* tok[], int ntoks, int n); -void exfil_initState(int k); +/*! + * \} + */ + +/*! + * \addtogroup Exfil_Functions Exfil File Functions + * \brief Exfil file functions. + * \ingroup Exfil_Enums_Structs_Variables_Functions + * \{ + */ + +/*! +* \brief Reads exfiltration parameters from a line of input data. +* \param k Index of line's keyword in project's keyword list +* \param tok Array of string tokens +* \param ntoks Number of tokens +* \param n Number of exfiltration parameters +* \return Error code +*/ +int exfil_readStorageParams(int k, char* tok[], int ntoks, int n); + +/*! +* \brief Initializes exfiltration parameters. +* \param k Index of exfiltration object in project's object list +*/ +void exfil_initState(int k); + +/*! +* \brief Gets exfiltration loss rate. +* \param exfil Pointer to exfiltration object +* \param tStep Time step +* \param depth Depth of water above exfiltration layer +* \param area Area of exfiltration layer +* \return Exfiltration loss rate +*/ double exfil_getLoss(TExfil* exfil, double tStep, double depth, double area); +/*! + * \} + */ + +/*! + * \} + */ #endif diff --git a/src/solver/findroot.c b/src/solver/findroot.c index d6056f658..53681521c 100644 --- a/src/solver/findroot.c +++ b/src/solver/findroot.c @@ -17,9 +17,7 @@ #define MAXIT 60 -int findroot_Newton(double x1, double x2, double* rts, double xacc, - void (*func) (double x, double* f, double* df, void* p), - void* p) +int findroot_Newton(double x1, double x2, double* rts, double xacc, newton_callback func, void* p) // // Using a combination of Newton-Raphson and bisection, find the root of a // function func bracketed between x1 and x2. The root, returned in rts, @@ -87,9 +85,7 @@ int findroot_Newton(double x1, double x2, double* rts, double xacc, else return 0; }; - -double findroot_Ridder(double x1, double x2, double xacc, - double (*func)(double, void* p), void* p) +double findroot_Ridder(double x1, double x2, double xacc, ridder_callback func, void* p) { int j; double ans, fhi, flo, fm, fnew, s, xhi, xlo, xm, xnew; diff --git a/src/solver/findroot.h b/src/solver/findroot.h index 3b54c6456..71fce197c 100644 --- a/src/solver/findroot.h +++ b/src/solver/findroot.h @@ -1,18 +1,53 @@ -//----------------------------------------------------------------------------- -// findroot.h -// -// Header file for root finding method contained in findroot.c -// -// Last modified on 11/19/13. -//----------------------------------------------------------------------------- +/*! +* \file findroot.h +* \brief Header file for root finding method. +* \author L. Rossman +* \date Created: 2013-11-19 +* \date Last updated: 2025-01-02 +*/ #ifndef FINDROOT_H #define FINDROOT_H -int findroot_Newton(double x1, double x2, double* rts, double xacc, - void (*func) (double x, double* f, double* df, void* p), - void* p); -double findroot_Ridder(double x1, double x2, double xacc, - double (*func)(double, void* p), void* p); +/*! +* \typedef newton_callback +* \brief Callback function for Newton-Raphson root finder. +* \param x Value of the independent variable. +* \param f Value of the function at x. +* \param df Value of the derivative of the function at x. +* \param p Pointer to additional data. +*/ +typedef void (*newton_callback) (double x, double* f, double* df, void* p); + +/*! +* \typedef ridder_callback +* \brief Callback function for Ridder's root finder. +* \param x Value of the independent variable. +* \param p Pointer to additional data. +*/ +typedef double (*ridder_callback) (double x, void* p); + +/*! +* \brief Finds the root of a function using Newton's method. +* \param x1 Lower bound of the bracketing interval. +* \param x2 Upper bound of the bracketing interval. +* \param rts Pointer to the root. +* \param xacc Desired accuracy of the root. +* \param func Callback function for the function and its derivative. +* \param p Pointer to additional data. +* \return Number of function evaluations used or 0 if the maximum allowed iterations were exceeded. +*/ +int findroot_Newton(double x1, double x2, double* rts, double xacc, newton_callback func, void* p); + +/*! +* \brief Finds the root of a function using Ridder's method. +* \param x1 Lower bound of the bracketing interval. +* \param x2 Upper bound of the bracketing interval. +* \param xacc Desired accuracy of the root. +* \param func Callback function for the function. +* \param p Pointer to additional data. +* \return The root of the function. +*/ +double findroot_Ridder(double x1, double x2, double xacc, ridder_callback func, void* p); #endif //FINDROOT_H diff --git a/src/solver/flowrout.c b/src/solver/flowrout.c index 934d22827..b37021976 100644 --- a/src/solver/flowrout.c +++ b/src/solver/flowrout.c @@ -102,8 +102,10 @@ void flowrout_init(int routingModel) initLinks(routingModel); } -//============================================================================= - +/*! +* \brief Closes down flow routing system. +* \param[in] routingModel Routing model code +*/ void flowrout_close(int routingModel) // // Input: routingModel = routing method code @@ -114,8 +116,12 @@ void flowrout_close(int routingModel) if ( routingModel == DW ) dynwave_close(); } -//============================================================================= - +/*! +* \brief Finds variable time step for dynamic wave routing. +* \param[in] routingModel Type of routing model +* \param[in] fixedStep User-supplied time step (sec) +* \return Returns adjusted value of routing time step (sec) +*/ double flowrout_getRoutingStep(int routingModel, double fixedStep) // // Input: routingModel = type of routing method used diff --git a/src/solver/funcs.h b/src/solver/funcs.h index ed7fa8b94..0084592aa 100644 --- a/src/solver/funcs.h +++ b/src/solver/funcs.h @@ -1,556 +1,2080 @@ -//----------------------------------------------------------------------------- -// funcs.h -// -// Project: EPA SWMM5 -// Version: 5.2 -// Date: 06/12/23 (Build 5.2.4) -// 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. -// Build 5.2.4: -// - Additional arguments added to function link_getLossRate. -// Build 5.3.0: -// - Modified code to allow saving multiple hotstart files at different times. -//----------------------------------------------------------------------------- +/*! + * \file funcs.h + * \brief Header file for global interfacing functions. + * \author L. Rossman + * \author M. Tryby (EPA) + * \date Created: 2023-06-12 + * \date Last updated: 2025-01-02 + * \version 5.3.0 + * \details + * Update History + * ================= + * Global interfacing functions used in the SWMM model. + * 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. + * Build 5.2.4: + * - Additional arguments added to function link_getLossRate. + * Build 5.3.0: + * - Modified code to allow saving multiple hotstart files at different times. + */ #ifndef FUNCS_H #define FUNCS_H - - -//----------------------------------------------------------------------------- -// Project Methods -//----------------------------------------------------------------------------- -void project_open(const char *f1, const char *f2, const char *f3); -void project_close(void); - -void project_readInput(void); -int project_readOption(char* s1, char* s2); -void project_validate(void); -int project_init(void); - -int project_addObject(int type, char* id, int n); -int project_findObject(int type, const char* id); -char* project_findID(int type, char* id); - -double** project_createMatrix(int nrows, int ncols); -void project_freeMatrix(double** m); - -//----------------------------------------------------------------------------- -// Input Reader Methods -//----------------------------------------------------------------------------- -int input_countObjects(void); -int input_readData(void); - -//----------------------------------------------------------------------------- -// Report Writer Methods -//----------------------------------------------------------------------------- -int report_readOptions(char* tok[], int ntoks); - -void report_writeLine(const char* line); -void report_writeSysTime(void); -void report_writeLogo(void); -void report_writeTitle(void); -void report_writeOptions(void); -void report_writeReport(void); - -void report_writeRainStats(int gage, TRainStats* rainStats); -void report_writeRdiiStats(double totalRain, double totalRdii); - -void report_writeControlActionsHeading(void); -void report_writeControlAction(DateTime aDate, char* linkID, double value, - char* ruleID); - -void report_writeRunoffError(TRunoffTotals* totals, double area); -void report_writeLoadingError(TLoadingTotals* totals); -void report_writeGwaterError(TGwaterTotals* totals, double area); -void report_writeFlowError(TRoutingTotals* totals); -void report_writeQualError(TRoutingTotals* totals); - -void report_writeMaxStats(TMaxStats massBalErrs[], TMaxStats CourantCrit[], - int nMaxStats); -void report_writeMaxFlowTurns(TMaxStats flowTurns[], int nMaxStats); -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_writeTseriesErrorMsg(int code, TTable *tseries); - -void inputrpt_writeInput(void); -void statsrpt_writeReport(void); - -//----------------------------------------------------------------------------- -// Temperature/Evaporation Methods -//----------------------------------------------------------------------------- -int climate_readParams(char* tok[], int ntoks); -int climate_readEvapParams(char* tok[], int ntoks); -int climate_readAdjustments(char* tok[], int ntoks); -void climate_validate(void); -void climate_openFile(void); -void climate_initState(void); -void climate_setState(DateTime aDate); +/*! + * \defgroup Global_Interfacing_Functions SWMM Global Interfacing Functions + * \brief SWMM global interfacing functions + * \{ + */ + +/*! + * \addtogroup Project_Methods Global Project Methods + * \brief Global methods associated with a SWMM project + * \ingroup Global_Interfacing_Functions + * \{ + */ + +/*! + * \brief Opens a new SWMM project + * \param[in] inp_file SWMM input file + * \param[in] rpt_file SWMM report file + * \param[in] out_file SWMM output file + */ +void project_open(const char *inp_file, const char *rpt_file, const char *out_file); + +/*! + * \brief Close project + */ +void project_close(void); + +/*! + * \brief Read project inputs + */ +void project_readInput(void); + +/*! + * \brief Reads a project option from a pair of string tokens + * \param[in] s1 Option keyword + * \param[in] s2 String representation of option's value + * \return Error code + * \note All project options have default values assigned in setDefaults(). + */ +int project_readOption(char *s1, char *s2); + +/*! + * \brief Checks validity of project data + */ +void project_validate(void); + +/*! + * \brief Initializes the internal state of the project + */ +int project_init(void); + +/*! + * \brief Adds an object Id to a hash table + * \param[in] type Object type + * \param[in] id Object ID string + * \param[in] n Object index + * \return 0 if object already added, 1 if not, -1 if hashing fails + */ +int project_addObject(int type, char *id, int n); + +/*! + * \brief Uses hash table to find index of an object with a given ID + * \param[in] type Object type + * \param[in] id Object ID string + * \return Returns index of object with given ID, or -1 if not found + */ +int project_findObject(int type, const char *id); + +/*! + * \brief Uses hash table to find address of a given string entry + * \param[in] type Object type + * \param[in] id ID name being sought + * \return Pointer to location where object's ID string is stored + */ +char *project_findID(int type, char *id); + +/*! + * \brief Allocates memory for a matrix of doubles + * \param[in] nrows Number of rows (0-based) + * \param[in] ncols Number of columns (0-based) + * \return Pointer to a matrix of doubles + */ +double **project_createMatrix(int nrows, int ncols); + +/*! + * \brief Frees memory allocated for a matrix of doubles + */ +void project_freeMatrix(double **m); +/*! + * \} + */ + +/*! + * \addtogroup Input_Reader_Methods Global Input Reader Methods + * \brief Global input reader methods + * \ingroup Global_Interfacing_Functions + * \{ + */ + +/*! + * \brief Reads input file to determine number of system objects + * \return Error code + */ +int input_countObjects(void); + +/*! + * \brief Reads input file to determine number of system objects + * \return Error code + */ +int input_readData(void); +/*! + * \} + */ + +/*! + * \addtogroup Report_Writer_Methods Global Report Writer Methods + * \brief Global report writer methods + * \ingroup Global_Interfacing_Functions + * \{ + */ + +/*! + * \brief Reads reporting options from a line of input data + * \param[in] tok Array of string tokens + * \param[in] ntoks Number of tokens + * \return Error code + */ +int report_readOptions(char *tok[], int ntoks); + +/*! + * \brief Writes a line of text to the report file + * \param[in] line Line of text + */ +void report_writeLine(const char *line); + +/*! + * \brief Writes starting/ending processing times to the report file + */ +void report_writeSysTime(void); + +/*! + * \brief Writes report header lines to report file + */ +void report_writeLogo(void); + +/*! + * \brief Writes project title to report file + */ +void report_writeTitle(void); + +/*! + * \brief Writes analysis options in use to report file + */ +void report_writeOptions(void); + +/*! + * \brief Writes project title to report file + */ +void report_writeReport(void); + +/*! + * \brief Writes summary of rain data read from input file to report file + * \param[in] gage Rain gage index + * \param[in] rainStats Rain file summary statistics + */ +void report_writeRainStats(int gage, TRainStats *rainStats); + +/*! + * \brief Writes summary of RDII inflow to report file + * \param[in] totalRain Total rainfall volume over sewershed + * \param[in] totalRdii Total RDII volume produced + */ +void report_writeRdiiStats(double totalRain, double totalRdii); + +/*! + * \brief Writes summary control actions heading to report file + */ +void report_writeControlActionsHeading(void); + +/*! + * \brief Writes control action to report file + * \param[in] aDate Date/time of rule action + * \param[in] linkID ID of link being controlled + * \param[in] value New status value of link + * \param[in] ruleID ID of rule implementing the action + */ +void report_writeControlAction(DateTime aDate, char *linkID, double value, + char *ruleID); + +/*! + * \brief Writes runoff continuity error to report file + * \param[in] totals Accumulated runoff totals + * \param[in] totalArea Total area of all subcatchments + */ +void report_writeRunoffError(TRunoffTotals *totals, double area); + +/*! + * \brief Writes runoff loading continuity error to report file + * \param[in] totals Accumulated pollutant loading totals + */ +void report_writeLoadingError(TLoadingTotals *totals); + +/*! + * \brief Writes groundwater continuity error to report file + * \param[in] totals Accumulated groundwater totals + * \param[in] area Total area of all subcatchments with groundwater + */ +void report_writeGwaterError(TGwaterTotals *totals, double area); + +/*! + * \brief Writes flow routing continuity error to report file + * \param[in] totals Accumulated flow routing totals + */ +void report_writeFlowError(TRoutingTotals *totals); + +/*! + * \brief Writes quality routing continuity error to report file + * \param[in] QualTotals Accumulated quality routing totals for each pollutant + */ +void report_writeQualError(TRoutingTotals *totals); + +/*! + * \brief Lists nodes and links with highest mass balance errors and courant time + * step violations to report file + * \param[in] massBalErrs Nodes with highest mass balance errors + * \param[in] CourantCrit Nodes most often Courant time step critical + * \param[in] nMaxStats Number of most critical nodes/links saved + */ +void report_writeMaxStats(TMaxStats massBalErrs[], TMaxStats CourantCrit[], + int nMaxStats); + +/*! + * \brief Lists links with highest number of flow turns to report (i.e., fraction + * of time periods where the flow is higher (or lower) than the + * flows in the previous and following periods) + * \param[in] flowTurns Links with highest number of flow turns + * \param[in] nMaxStats Number of links in flowTurns[] + */ +void report_writeMaxFlowTurns(TMaxStats flowTurns[], int nMaxStats); + +/*! + * \brief Writes non-converging nodes to report file + * \param[in] maxNonconverged Nodes most often Courant time step critical + * \param[in] nMaxStats Number of most critical nodes/links saved + */ +void report_writeNonconvergedStats(TMaxStats maxNonconverged[], + int nMaxStats); + +/*! + * \brief Writes routing time step statistics to report file + * \param[in] timeStepStats Routing time step statistics + */ +void report_writeTimeStepStats(TTimeStepStats *timeStepStats); + +/*! + * \brief Writes error message to report file + * \param[in] code Error code + * \param[in] msg Error message text + */ +void report_writeErrorMsg(int code, char *msg); + +/*! + * \brief Writes error message to report file + */ +void report_writeErrorCode(void); + +/*! + * \brief Writes input error message to report file + * \param[in] k Error code + * \param[in] sect Number of input data section where error occurred + * \param[in] line Line of data containing the error + * \param[in] lineCount Line number of data file containing the error + */ +void report_writeInputErrorMsg(int k, int sect, char *line, long lineCount); + +/*! + * \brief Writes a warning message to report file + * \param[in] msg Text of warning message + * \param[in] id ID name of object that message refers to + */ +void report_writeWarningMsg(char *msg, char *id); + +/*! + * \brief Writes the date where a time series' data is out of order + * \param[in] code Error code + * \param[in] tseries Pointer to a time series + */ +void report_writeTseriesErrorMsg(int code, TTable *tseries); + +/*! + * \brief Writes summary of input data to report file + */ +void inputrpt_writeInput(void); + +/*! + * \brief Reports simulation summary statistics + */ +void statsrpt_writeReport(void); +/*! + * \} + */ + +/*! + * \addtogroup Climate_Methods Global Temperature/Evaporation Methods + * \brief Global temperature/evaporation methods + * \ingroup Global_Interfacing_Functions + * \{ + */ +/*! + * \brief Reads climate/temperature parameters from input line of data + * \param[in] tok Array of string tokens + * \param[in] ntoks Number of tokens + * \return Error code + * \details + * Format of data can be: + * - TIMESERIES name + * - FILE name (start) (units) + * - WINDSPEED MONTHLY v1 v2 ... v12 + * - WINDSPEED FILE + * - SNOWMELT v1 v2 ... v6 + * - ADC IMPERV/PERV v1 v2 ... v10 + */ +int climate_readParams(char *tok[], int ntoks); + +/*! + * \brief Reads evaporation parameters from input line of data. + * \param[in] tok Array of string tokens + * \param[in] ntoks Number of tokens + * \return Error code + * \details + * Data formats are: + * - CONSTANT value + * - MONTHLY v1 ... v12 + * - TIMESERIES name + * - TEMPERATURE + * - FILE (v1 ... v12) + * - RECOVERY name + * - DRY_ONLY YES/NO + */ +int climate_readEvapParams(char *tok[], int ntoks); + +/*! + * \brief Reads adjustments to monthly evaporation or rainfall from input line of data. + * \param[in] tok Array of string tokens + * \param[in] ntoks Number of tokens + * \return Error code + * \details + * Data formats are: + * - TEMPERATURE v1 ... v12 + * - EVAPORATION v1 ... v12 + * - RAINFALL v1 ... v12 + * - CONDUCTIVITY v1 ... v12 + * - N-PERV subcatchID patternID + * - DSTORE subcatchID patternID + * - INFIL subcatchID patternID + */ +int climate_readAdjustments(char *tok[], int ntoks); + +/*! + * \brief Validates climatological variables. + */ +void climate_validate(void); + +/*! + * \brief Opens a climate file and reads in first set of values. + */ +void climate_openFile(void); + +/*! + * \brief Initializes climate state variables. + */ +void climate_initState(void); + +/*! + * \brief Sets climate variables for current date. + * \param[in] theDate Simulation date + */ +void climate_setState(DateTime theDate); + +/*! + * \brief Gets the next date when evaporation rate changes. + * \return The current value of NextEvapDate + */ DateTime climate_getNextEvapDate(void); +/*! + * \} + */ + +/*! + * \addtogroup Rainfall_Methods Global Rainfall Processing Methods + * \brief Global rainfall processing methods + * \ingroup Global_Interfacing_Functions + * \{ + */ + +/*! + * \brief Opens binary rainfall interface file and RDII processor. + */ +void rain_open(void); + +/*! + * \brief Closes rain interface file and RDII processor. + */ +void rain_close(void); +/*! + * \} + */ + +/*! + * \addtogroup Snowmelt_Methods Global Snowmelt Processing Methods + * \brief Global snowmelt processing methods + * \ingroup Global_Interfacing_Functions + * \{ + */ + +/*! + * \brief Reads snowmelt parameters from a tokenized line of input data. + * \param[in] tok Array of string tokens + * \param[in] ntoks Number of tokens + * \return Error code + * \details + * Format of data are: + * - Name SubArea Cmin Cmax Tbase FWF SD0 FW0 SNN0/SD100 + * - Name REMOVAL SDplow Fout Fimperv Fperv Fimelt Fsubcatch (Subcatch) + */ +int snow_readMeltParams(char *tok[], int ntoks); + +/*! + * \brief Creates a snowpack object for a subcatchment. + * \param[in] subcatchIndex Subcatchment index + * \param[in] snowIndex Snowmelt parameter set index + * \return TRUE if successful, FALSE if not + */ +int snow_createSnowpack(int subcatchIndex, int snowIndex); + +/*! + * \brief Checks for valid values in a snowmelt parameter set. + * \param[in] snowIndex Snowmelt parameter set index + */ +void snow_validateSnowmelt(int snowIndex); + +/*! + * \brief Initializes state of a subcatchment's snowpack. + * \param[in] subcatchIndex Subcatchment index + */ +void snow_initSnowpack(int subcatchIndex); + +/*! + * \brief Initializes values in a snowmelt parameter set. + * \param[in] snowIndex Snowmelt parameter set index + */ +void snow_initSnowmelt(int snowIndex); + +/*! + * \brief Retrieves the current state of a snowpack object. + * \param[in] subcatchIndex Subcatchment index + * \param[in] subAreaIndex Snowpack sub-area index + * \param[out] x Array of snowpack state variables + */ +void snow_getState(int subcatchIndex, int subAreaIndex, double x[]); + +/*! + * \brief Sets the current state of a snowpack object. + * \param[in] subcatchIndex Subcatchment index + * \param[in] subAreaIndex Snowpack sub-area index + * \param[in] x Array of snowpack state variables + */ +void snow_setState(int subcatchIndex, int subAreaIndex, double x[]); + +/*! + * \brief Sets values of snowmelt coefficients for a particular time of year. + * \param[in] snowIndex Snowmelt parameter set index + * \param[in] season Season of the year + */ +void snow_setMeltCoeffs(int snowIndex, double season); + +/*! + * \brief Adds new snow to subcatchment and plows it between sub-areas. + * \param[in] subcatchIndex Subcatchment index + * \param[in] tStep Time step (sec) + */ +void snow_plowSnow(int subcatchIndex, double tStep); + +/*! + * \brief Modifies rainfall input to subcatchment's subareas based on possible + * snow melt and updates snow depth over entire subcatchment. + * \param[in] subcatchIndex Subcatchment index + * \param[in] rainfall Rainfall (ft/sec) + * \param[in] snowfall Snowfall (ft/sec) + * \param[in] tStep Time step (sec) + * \param[out] netPrecip Rainfall + snowmelt on each runoff sub-area (ft/sec) + * \return New snow depth over subcatchment + */ +double snow_getSnowMelt(int subcatchIndex, double rainfall, double snowfall, + double tStep, double netPrecip[]); + +/*! + * \brief Computes volume of snow on a subcatchment. + * \param[in] subcatchIndex Subcatchment index + * \return Volume of snow cover (ft3) + */ +double snow_getSnowCover(int subcatchIndex); +/*! + * \} + */ + +/*! + * \addtogroup Runoff_Analyzer_Methods Global Runoff Analyzer Methods + * \brief Global runoff analyzer methods + * \ingroup Global_Interfacing_Functions + * \{ + */ +/*! + * \brief Opens the runoff analyzer. + * \return Error code + */ +int runoff_open(void); + +/*! + * \brief Computes runoff for each subcatchment at the current runoff time. + */ +void runoff_execute(void); + +/*! + * \brief Closes the runoff analyzer. + */ +void runoff_close(void); +/*! + * \} + */ + +/*! + * \addtogroup Runoff_Analyzer_Methods Global Conveyance System Routing Methods + * \brief Global conveyance system routing methods + * \ingroup Global_Interfacing_Functions + * \{ + */ + +/*! + * \brief Initializes the routing analyzer. + * \return Error code + */ +int routing_open(void); + +/*! + * \brief Determines time step used for flow routing at current time period. + * \param[in] routingModel Routing method code + * \param[in] fixedStep User-supplied time step (sec) + * \return Time step used for flow routing (sec) + */ +double routing_getRoutingStep(int routingModel, double fixedStep); + +/*! + * \brief Executes the routing process at the current time period. + * \param[in] routingModel Routing method code + * \param[in] routingStep Routing time step (sec) + */ +void routing_execute(int routingModel, double routingStep); + +/*! + * \brief Closes down the routing analyzer. + * \param[in] routingModel Routing method code + */ +void routing_close(int routingModel); +/*! + * \} + */ + +/*! + * \addtogroup Output_File_Methods Global Output File Methods + * \brief Global output file methods + * \ingroup Global_Interfacing_Functions + * \{ + */ + +/*! + * \brief Writes basic project data to binary output file. + */ +int output_open(void); + +/*! + * \brief Writes closing records to binary file. + */ +void output_end(void); + +/*! + * \brief Frees memory used for accessing the binary file. + */ +void output_close(void); + +/*! + * \brief Writes computed results for current report time to binary file. + * \param[in] reportTime Elapsed simulation time (millisec) + */ +void output_saveResults(double reportTime); + +/*! + * \brief Updates average results for current time period. + */ +void output_updateAvgResults(void); + +/*! + * \brief Retrieves the date/time for a specific reporting period + * from the binary output file. + * \param[in] period Index of reporting time period + * \param[out] days Date/time value + */ +void output_readDateTime(long period, DateTime *days); + +/*! + * \brief Reads computed results for a subcatchment at a specific time + * period. + * \param[in] period Index of reporting time period + * \param[in] index Subcatchment index in binary output file + */ +void output_readSubcatchResults(long period, int index); + +/*! + * \brief Reads computed results for a node at a specific time period. + * \param[in] period Index of reporting time period + * \param[in] index Node index in binary output file + */ +void output_readNodeResults(long period, int index); + +/*! + * \brief Reads computed results for a link at a specific time period. + * \param[in] period Index of reporting time period + * \param[in] index Link index in binary output file + */ +void output_readLinkResults(long period, int index); +/*! + * \} + */ + +/*! + * \addtogroup Groundwater_File_Methods Global Groundwater Methods + * \brief Global groundwater methods + * \ingroup Global_Interfacing_Functions + * \{ + */ + +/*! + * \brief Reads aquifer parameter values from line of input data + * \param[in] aquiferIndex Index of aquifer + * \param[in] tok Array of string tokens + * \param[in] ntoks Number of tokens + * \return Error code + * \details Data line contains following parameters: + * ID, porosity, wiltingPoint, fieldCapacity, conductivity, + * conductSlope, tensionSlope, upperEvapFraction, lowerEvapDepth, + * gwRecession, bottomElev, waterTableElev, upperMoisture + * (evapPattern) + */ +int gwater_readAquiferParams(int aquiferIndex, char *tok[], int ntoks); + +/*! + * \brief Reads groundwater inflow parameters for a subcatchment from + * a line of input data. + * \param[in] tok Array of string tokens + * \param[in] ntoks Number of tokens + * \return Error code + * \details Data format is: + * subcatch aquifer node surfElev a1 b1 a2 b2 a3 fixedDepth + + * (nodeElev bottomElev waterTableElev upperMoisture ) + */ +int gwater_readGroundwaterParams(char *tok[], int ntoks); + +/*! + * \brief Reads mathematical expression for lateral or deep groundwater + * flow for a subcatchment from a line of input data. + * \param[in] tok Array of string tokens + * \param[in] ntoks Number of tokens + * \return Error code + * \details Format is: subcatch LATERAL/DEEP + * where subcatch is the ID of the subcatchment, LATERAL is for lateral + * GW flow, DEEP is for deep GW flow and is any well-formed math + * expression. + */ +int gwater_readFlowExpression(char *tok[], int ntoks); + +/*! + * \brief Veletes a subcatchment's custom groundwater flow expressions. + * \param[in] subcatchIndex Subcatchment index + */ +void gwater_deleteFlowExpression(int subcatchIndex); + +/*! + * \brief Validates groundwater aquifer properties. + * \param[in] aquiferIndex Index of aquifer + */ +void gwater_validateAquifer(int aquiferIndex); + +/*! + * \brief Validates groundwater parameters for a subcatchment. + * \param[in] subcatchIndex Subcatchment index + */ +void gwater_validate(int subcatchIndex); + +/*! + * \brief Initializes state of subcatchment's groundwater. + * \param[in] subcatchIndex Subcatchment index + */ +void gwater_initState(int subcatchIndex); + +/*! + * \brief Retrieves state of subcatchment's groundwater. + * \param[in] subcatchIndex Subcatchment index + * \param[out] x Array of groundwater state variables + */ +void gwater_getState(int subcatchIndex, double x[]); + +/*! + * \brief Assigns values to a subcatchment's groundwater state. + * \param[in] subcatchIndex Subcatchment index + * \param[in] x Array of groundwater state variables + */ +void gwater_setState(int subcatchIndex, double x[]); + +/*! + * \brief Computes groundwater flow for a subcatchment. + * \param[in] subcatchIndex Subcatchment index + * \param[in] evap Pervious surface evaporation volume consumed (ft3) + * \param[in] infil Surface infiltration volume (ft3) + * \param[in] tStep Time step (sec) + */ +void gwater_getGroundwater(int subcatchIndex, double evap, double infil, + double tStep); + +/*! + * \brief Finds volume of groundwater stored in upper & lower zones. + * \param[in] subcatchIndex Subcatchment index + * \return Total volume of groundwater in ft/ft2 + */ +double gwater_getVolume(int subcatchIndex); +/*! + * \} + */ + +/*! + * \addtogroup RDII_File_Methods Global RDII Methods + * \brief Global RDII methods + * \ingroup Global_Interfacing_Functions + * \{ + */ + +/*! + * \brief Reads properties of an RDII inflow from a line of input. + * \param[in] tok Array of string tokens + * \param[in] ntoks Number of tokens + * \return Error code + */ +int rdii_readRdiiInflow(char *tok[], int ntoks); + +/*! + * \brief Deletes RDII inflow data for a node. + * \param[in] nodeIndex Node index + */ +void rdii_deleteRdiiInflow(int nodeIndex); + +/*! + * \brief Initializes properties of a unit hydrograph group. + * \param[in] unitHydIndex Unit hydrograph group index + */ +void rdii_initUnitHyd(int unitHydIndex); + +/*! + * \brief Reads parameters of an RDII unit hydrograph from a line of input. + * \param[in] tok Array of string tokens + * \param[in] ntoks Number of tokens + * \return Error code + */ +int rdii_readUnitHydParams(char *tok[], int ntoks); + +/*! + * \brief Opens an exisiting RDII interface file or creates a new one. + */ +void rdii_openRdii(void); + +/*! + * \brief Closes the RDII interface file. + */ +void rdii_closeRdii(void); + +/*! + * \brief Finds number of RDII inflows at a specified date. + * \param[in] aDate Current date/time + * \return Returns 0 if no RDII flow or number of nodes with RDII inflows + */ +int rdii_getNumRdiiFlows(DateTime aDate); + +/*! + * \brief Finds index and current RDII inflow for an RDII node. + * \param[in] index RDII node index + * \param[out] nodeIndex Node index + * \param[out] q RDII flow rate + */ +void rdii_getRdiiFlow(int index, int *nodeIndex, double *q); +/*! + * \} + */ + +/*! + * \addtogroup LandUse_File_Methods Global Land Use Methods + * \brief Global RDII methods + * \ingroup Global_Interfacing_Functions + * \{ + */ + +/*! +* \brief Reads landuse parameters from a tokenized line of input. +* \param[in] landuseIndex Land use index +* \param[in] tok Array of string tokens +* \param[in] ntoks Number of tokens +* \return Error code +* \details Data format is: +* landuseID (sweepInterval sweepRemoval sweepDays0) +*/ +int landuse_readParams(int landuseIndex, char *tok[], int ntoks); + +/*! +* \brief Reads pollutant parameters from a tokenized line of input. +* \param[in] pollutIndex Pollutant index +* \param[in] tok Array of string tokens +* \param[in] ntoks Number of tokens +* \return Error code +* \details Data format is: +* ID Units cRain cGW cRDII kDecay (snowOnly coPollut coFrac cDWF cInit) +*/ +int landuse_readPollutParams(int pollutIndex, char *tok[], int ntoks); + +/*! +* \brief Reads pollutant buildup parameters from a tokenized line of input. +* \param[in] tok Array of string tokens +* \param[in] ntoks Number of tokens +* \return Error code +* \details Data format is: +* landuseID pollutID buildupType c1 c2 c3 normalizerType +*/ +int landuse_readBuildupParams(char *tok[], int ntoks); + +/*! +* \brief Reads pollutant washoff parameters from a tokenized line of input. +* \param[in] tok Array of string tokens +* \param[in] ntoks Number of tokens +* \return Error code +* \details Data format is: +* landuseID pollutID washoffType c1 c2 sweepEffic bmpRemoval +*/ +int landuse_readWashoffParams(char *tok[], int ntoks); + +/*! +* \brief Determines the initial buildup of each pollutant on +* each land use for a given subcatchment. +* \param[in] landFactor Land use factor +* \param[out] initBuildup Initial buildup of each pollutant +* \param[in] area Area of subcatchment (ft2) +* \param[in] curb Subcatchment's curb length (users units) +* \details Contributions from co-pollutants to initial buildup are not +* included since the co-pollutant mechanism only applies to washoff. +*/ +void landuse_getInitBuildup(TLandFactor *landFactor, double *initBuildup, + double area, double curb); + +/*! +* \brief Computes new pollutant buildup on a landuse after a time increment. +* \param[in] landuseIndex Land use index +* \param[in] pollutIndex Pollutant index +* \param[in] area Area of subcatchment (ac or ha) +* \param[in] curb Land use curb length (users units) +* \param[in] buildup Current pollutant buildup (lbs or kg) +* \param[in] tStep Time increment for buildup (sec) +* \return Returns new pollutant buildup (lbs or kg) +*/ +double landuse_getBuildup(int landuseIndex, int pollutIndex, double area, double curb, + double buildup, double tStep); + +/*! +* \brief Computes pollutant load generated by a land use over a time step. +* \param[in] landuseIndex Land use index +* \param[in] pollutIndex Pollutant index +* \param[in] area Area of subcatchment (ft2) +* \param[in] landFactor Array of land use data for subcatchment +* \param[in] runoff Runoff flow generated by subcatchment (ft/sec) +* \param[in] vOutflow Runoff volume leaving subcatchment (ft3) +* \return Returns pollutant load generated by a land use over a time step. +*/ +double landuse_getWashoffLoad(int landuse, int p, double area, + TLandFactor landFactor[], double runoff, double vOutflow); + +/*! +* \brief Finds the overall average BMP removal achieved for pollutant pollutIndex +* treated in subcatchment j. +* \param[in] subcatchIndex Subcatchment index +* \param[in] pollutIndex Pollutant index +* \return Returns a BMP removal fraction for a pollutant pollutIndex +*/ +double landuse_getAvgBmpEffic(int subcatchIndex, int pollutIndex); + +/*! +* \brief Finds washoff mass added by a co-pollutant of a given pollutant. +* \param[in] p Pollutant index +* \param[in] washoff Array of washoff mass for each pollutant +* \return Returns washoff mass added by co-pollutant relation (mass) +*/ +double landuse_getCoPollutLoad(int p, double washoff[]); +/*! + * \} + */ + +/*! + * \addtogroup FlowQuality_Routing_File_Methods Global Flow/Quality Routing Methods + * \brief Global flow/quality routing methods + * \ingroup Global_Interfacing_Functions + * \{ + */ + +/*! +* \brief Initializes flow routing system. +* \param[in] routingModel Routing model code +*/ +void flowrout_init(int routingModel); + +/*! +* \brief Closes down flow routing system. +* \param[in] routingModel Routing model code +*/ +void flowrout_close(int routingModel); + +/*! +* \brief Finds variable time step for dynamic wave routing. +* \param[in] routingModel Type of routing model +* \param[in] fixedStep User-supplied time step (sec) +* \return Returns adjusted value of routing time step (sec) +*/ +double flowrout_getRoutingStep(int routingModel, double fixedStep); + +/*! +* \brief Routes flow through conveyance network over current time step. +* \param[in] links Array of link indices +* \param[in] routingModel Routing model code +* \param[in] tStep Time step (sec) +* \return Returns number of computational steps taken +*/ +int flowrout_execute(int links[], int routingModel, double tStep); + +/*! +* \brief Sorts links from upstream to downstream. +* \param[in, out] sortedLinks Array of link indexes in sorted order +*/ +void toposort_sortLinks(int sortedLinks[]); + +/*! +* \brief Finds outflow over time step tStep given flow entering a +* conduit using Kinematic Wave flow routing. +* \param[in] linkIndex Link index +* \param[in] qinflow Inflow at current time (cfs) +* \param[out] qoutflow Outflow at current time (cfs) +* \param[in] tStep Time step (sec) +* \return Returns number of iterations used +* \details +* Orientation of link and variables: +* q1, a1, q2, a2, q3, a3, x, t +* ^ q3 +* t | +* | qin, ain |-------------------| qout, aout +* | | Flow ---> | +* |----> x q1, a1 |-------------------| q2, a2 +*/ +int kinwave_execute(int linkIndex, double *qin, double *qout, double tStep); + +/*! +* \brief Adjusts dynamic wave routing options. +*/ +void dynwave_validate(void); + +/*! +* \brief Initializes dynamic wave routing method. +*/ +void dynwave_init(void); + +/*! +* \brief Frees memory allocated for dynamic wave routing method. +*/ +void dynwave_close(void); + +/*! +* \brief Computes variable routing time step if applicable. +* \param[in] fixedStep User-supplied fixed time step (sec) +* \return Returns routing time step (sec) +*/ +double dynwave_getRoutingStep(double fixedStep); + +/*! +* \brief Routes flows through drainage network over current time step. +* \param[in] tStep Time step (sec) +*/ +int dynwave_execute(double tStep); + +/*! +* \brief Updates flow in conduit link by solving finite difference +* form of continuity and momentum equations. +* \param[in] linkIndex Link index +* \param[in] steps Number of iteration steps taken +* \param[in] omega Under-relaxation parameter +* \param[in] dt Time step (sec) +*/ +void dwflow_findConduitFlow(int linkIndex, int steps, double omega, double dt); + +/*! +* \brief Initializes water quality concentrations in all nodes and links. +*/ +void qualrout_init(void); + +/*! +* \brief Routes water quality constituents through the drainage +* network over the current time step. +* \param[in] tStep Routing time step (sec) +*/ +void qualrout_execute(double tStep); +/*! + * \} + */ + +/*! + * \addtogroup Treatment_File_Methods Global Treatment Methods + * \brief Global treatment methods + * \ingroup Global_Interfacing_Functions + * \{ + */ + +/*! +* \brief Allocates memory for computing pollutant removals by treatment. +* \return Returns True if successful, False if not +*/ +int treatmnt_open(void); + +/*! +* \brief Frees memory used for computing pollutant removals by treatment. +*/ +void treatmnt_close(void); + +/*! +* \brief Reads a treatment expression from a tokenized line of input. +* \param[in] tok Array of string tokens +* \param[in] ntoks Number of tokens +* \return Error code +*/ +int treatmnt_readExpression(char *tok[], int ntoks); + +/*! +* \brief Deletes the treatment objects for each pollutant at a node. +* \param[in] nodeIndex Node index +*/ +void treatmnt_delete(int nodeIndex); + +/*! +* \brief Updates pollutant concentrations at a node after treatment. +* \param[in] nodeIndex Node index +* \param[in] q Inflow to node (cfs) +* \param[in] v Volume of node (ft3) +* \param[in] tStep Routing time step (sec) +*/ +void treatmnt_treat(int nodeIndex, double q, double v, double tStep); + +/*! +* \brief Computes and saves array of inflow concentrations to a node. +* \param[in] qIn Flow inflow rate (cfs) +* \param[in] wIn Pollutant mass inflow rates (mass/sec) +*/ +void treatmnt_setInflow(double qIn, double wIn[]); +/*! + * \} + */ + +/*! + * \addtogroup Mass_Balance_File_Methods Global Mass Balance Methods + * \brief Global mass balance methods + * \ingroup Global_Interfacing_Functions + * \{ + */ + +/*! +* \brief Opens and initializes mass balance continuity checking. +* \return Error code +*/ +int massbal_open(void); + +/*! +* \brief Frees memory used by mass balance system. +*/ +void massbal_close(void); + +/*! +* \brief Reports mass balance results. +*/ +void massbal_report(void); + +/*! +* \brief Updates runoff totals after current time step. +* \param[in] flowType Type of flow (RUNOFF_RAINFALL, RUNOFF_EVAP, RUNOFF_RUNOFF, etc.) +* \param[in] v Volume of flow (ft3) +*/ +void massbal_updateRunoffTotals(int flowType, double v); + +/*! +* \brief Adds inflow mass loading to loading totals for current time step. +* \param[in] type Type of inflow +* \param[in] pollutIndex Pollutant index +* \param[in] w Mass loading +*/ +void massbal_updateLoadingTotals(int type, int pollutIndex, double w); + +/*! +* \brief Updates groundwater totals after current time step. +* \param[in] vInfil Volume depth of infiltration (ft) +* \param[in] vUpperEvap Volume depth of upper zone evaporation (ft) +* \param[in] vLowerEvap Volume depth of lower zone evaporation (ft) +* \param[in] vLowerPerc Volume depth of percolation to deep GW (ft) +* \param[in] vGwater Volume depth of groundwater outflow (ft) +*/ +void massbal_updateGwaterTotals(double vInfil, double vUpperEvap, + double vLowerEvap, double vLowerPerc, double vGwater); + +/*! +* \brief Updates overall routing totals with totals from current time step. +* \param[in] tStep Time step (sec) +*/ +void massbal_updateRoutingTotals(double tStep); + +/*! +* \brief Initializes routing totals for current time step. +*/ +void massbal_initTimeStepTotals(void); + +/*! +* \brief Adds flow inflow to routing totals for current time step. +* \param[in] type Type of inflow +* \param[in] q Inflow rate (cfs) +*/ +void massbal_addInflowFlow(int type, double q); + +/*! +* \brief Adds quality inflow to routing totals for current time step. +* \param[in] type Type of inflow +* \param[in] pollutIndex Pollutant index +* \param[in] w Mass flow rate (mass/sec) +*/ +void massbal_addInflowQual(int type, int pollutIndex, double w); + +/*! +* \brief Adds flow outflow over current time step to routing totals. +* \param[in] q Outflow rate (cfs) +* \param[in] isFlooded TRUE if outflow represents internal flooding +*/ +void massbal_addOutflowFlow(double q, int isFlooded); + +/*! +* \brief Adds pollutant outflow over current time step to routing totals. +* \param[in] pollutIndex Pollutant index +* \param[in] mass Mass outflow rate (mass/sec) +* \param[in] isFlooded TRUE if outflow represents internal flooding +*/ +void massbal_addOutflowQual(int pollutIndex, double mass, int isFlooded); + +/*! +* \brief Adds node losses over current time step to routing totals. +* \param[in] evapLoss Evaporation loss from all nodes (ft3/sec) +* \param[in] infilLoss Seepage loss from all nodes (ft3/sec) +*/ +void massbal_addNodeLosses(double evapLoss, double infilLoss); + +/*! +* \brief Adds link losses over current time step to routing totals. +* \param[in] evapLoss Evaporation loss from all links (ft3/sec) +* \param[in] infilLoss Infiltration loss from all links (ft3/sec) +*/ +void massbal_addLinkLosses(double evapLoss, double infilLoss); + +/*! +* \brief Adds mass reacted during current time step to routing totals. +* \param[in] pollutIndex Pollutant index +* \param[in] mass Rate of mass reacted (mass/sec) +*/ +void massbal_addReactedMass(int pollutIndex, double mass); + +/*! +* \brief Adds mass lost to seepage during current time step to routing totals. +* \param[in] pollutIndex Pollutant index +* \param[in] seepLoss Mass seepage rate (mass/sec) +*/ +void massbal_addSeepageLoss(int pollutIndex, double seepLoss); + +/*! +* \brief Adds mass remaining on dry surface to routing totals. +* \param[in] pollutIndex Pollutant index +* \param[in] mass Pollutant mass +*/ +void massbal_addToFinalStorage(int pollutIndex, double mass); + +/*! +* \brief Computes flow routing mass balance error at current time step. +* \return Returns fractional difference between total inflow and outflow. +*/ +double massbal_getStepFlowError(void); + +/*! +* \brief Computes runoff mass balance error. +* \return Returns percent runoff mass balance error. +*/ +double massbal_getRunoffError(void); + +/*! +* \brief Computes flow routing mass balance error. +* \return Returns percent flow routing mass balance error. +*/ +double massbal_getFlowError(void); +/*! + * \} + */ + +/*! + * \addtogroup Simulation_Statistics_File_Methods Global Simulation Statistics Methods + * \brief Global simulation statistics methods + * \ingroup Global_Interfacing_Functions + * \{ + */ +/*! +* \brief Opens the simulation statistics system. +* \return Error code +*/ +int stats_open(void); + +/*! +* \brief Closes the simulation statistics system. +*/ +void stats_close(void); + +/*! +* \brief Reports simulation statistics. +*/ +void stats_report(void); + +/*! +* \brief Updates count of times a node or link was time step-critical. +* \param[in] nodeIndex Node index +* \param[in] linkIndex Link index +*/ +void stats_updateCriticalTimeCount(int nodeIndex, int linkIndex); + +/*! +* \brief Updates various flow routing statistics at current time period. +* \param[in] tStep Routing time step (sec) +* \param[in] aDate Current date/time +*/ +void stats_updateFlowStats(double tStep, DateTime aDate); + +/*! +* \brief Updates flow routing time step statistics. +* \param[in] tStep Current flow routing time step (sec) +* \param[in] trialsCount Number of trials used to solve routing +* \param[in] steadyState TRUE if steady flow conditions exist +*/ +void stats_updateTimeStepStats(double tStep, int trialsCount, int steadyState); + +/*! +* \brief Updates totals of runoff components for a specific subcatchment. +* \param[in] subcatchIndex Subcatchment index +* \param[in] rainVol Rainfall + snowfall volume (ft3) +* \param[in] runonVol Runon volume from other subcatchments (ft3) +* \param[in] evapVol Evaporation volume (ft3) +* \param[in] infilVol Infiltration volume (ft3) +* \param[in] impervVol Impervious runoff volume (ft3) +* \param[in] pervVol Pervious runoff volume (ft3) +* \param[in] runoffVol Total runoff volume (ft3) +* \param[in] runoff Runoff rate (cfs) +*/ +void stats_updateSubcatchStats(int subcatchIndex, double rainVol, + double runonVol, double evapVol, double infilVol, + double impervVol, double pervVol, double runoffVol, double runoff); + +/*! +* \brief Updates groundwater statistics for a specific subcatchment. +* \param[in] subcatchIndex Subcatchment index +* \param[in] infil Infiltration volume (ft3) +* \param[in] evap Evaporation volume (ft3) +* \param[in] latFlow Lateral flow volume (ft3) +* \param[in] deepFlow Deep flow volume (ft3) +* \param[in] theta Soil moisture content +* \param[in] waterTable Depth to water table (ft) +* \param[in] tStep Time step (sec) +*/ +void stats_updateGwaterStats(int subcatchIndex, double infil, double evap, + double latFlow, double deepFlow, double theta, double waterTable, + double tStep); + +/*! +* \brief Updates value of maximum system runoff rate. +* \note Updates global variable MaxRunoffFlow +*/ +void stats_updateMaxRunoff(void); + +/*! +* \brief Updates a node's maximum depth recorded at reporting times. +* \param[in] nodeIndex Node index +* \param[in] depth Water depth at node at current reporting time (ft) +*/ +void stats_updateMaxNodeDepth(int nodeIndex, double depth); + +/*! +* \brief Updates convergence statistics node +* \param[in] nodeIndex Node index +* \param[in] converged TRUE if node has converged +*/ +void stats_updateConvergenceStats(int nodeIndex, int converged); +/*! + * \} + */ + +/*! + * \addtogroup Raingage_File_Methods Global Raingage Methods + * \brief Global raingage methods + * \ingroup Global_Interfacing_Functions + * \{ + */ +/*! +* \brief Reads rain gage parameters from a line of input data +* \param[in] gageIndex Gage index +* \param[in] tok Array of string tokens +* \param[in] ntoks Number of tokens +* \return Error code +* \details Data formats are: +* Name RainType RecdFreq SCF TIMESERIES SeriesName +* Name RainType RecdFreq SCF FILE FileName Station Units StartDate +*/ +int gage_readParams(int gageIndex, char *tok[], int ntoks); + +/*! +* \brief Checks for valid rain gage parameters +* \param[in] gageIndex Gage index +* \note Assumes that any time series used by the gage has been previously validated +*/ +void gage_validate(int gageIndex); + +/*! +* \brief Initializes state of a rain gage +* \param[in] gageIndex Rain gage index +*/ +void gage_initState(int gageIndex); + +/*! +* \brief Updates state of rain gage for specified date. +* \param[in] gageIndex Rain gage index +* \param[in] aDate A calendar date/time +*/ +void gage_setState(int gageIndex, DateTime aDate); + +/*! +* \brief Determines whether gage's recorded rainfall is rain or snow. +* \param[in] gageIndex Rain gage index +* \param[out] rainfall Rainfall rate (ft/sec) +* \param[out] snowfall Snowfall rate (ft/sec) +* \return Returns Total precipitation (ft/sec) +*/ +double gage_getPrecip(int gageIndex, double *rainfall, double *snowfall); + +/*! +* \brief Sets the rainfall value reported at the current reporting time. +* \param[in] gageIndex Rain gage index +* \param[in] aDate Current date/time +*/ +void gage_setReportRainfall(int gageIndex, DateTime aDate); + +/*! +* \brief Finds the next date from specified date when rainfall occurs. +* \param[in] gageIndex Rain gage index +* \param[in] aDate Current date/time +* \return Returns next date when rainfall occurs +*/ +DateTime gage_getNextRainDate(int gageIndex, DateTime aDate); + +/*! +* \brief Updates past MAXPASTRAIN hourly rain totals. +* \param[in] gageIndex Rain gage index +* \param[in] tStep Time step (sec) +* \details +* 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. +*/ +void gage_updatePastRain(int gageIndex, int tStep); + +/*! +* \brief Retrieves rainfall total over some previous number of hours. +* \param[in] gageIndex Rain gage index +* \param[in] hrs Number of hours +* \return Returns cumulative rain volume (inches or mm) in last n hours +*/ +double gage_getPastRain(int gageIndex, int hrs); +/*! + * \} + */ + +/*! + * \addtogroup Subcatchment_File_Methods Global Subcatchments Methods + * \brief Global subcatchment methods + * \ingroup Global_Interfacing_Functions + * \{ + */ +/*! +* \brief Reads subcatchment parameters from a tokenized line of input data. +* \param[in] subcatchIndex Subcatchment index +* \param[in] tok Array of string tokens +* \param[in] ntoks Number of tokens +* \return Error code +* \details Data has format: +* Name RainGage Outlet Area %Imperv Width Slope CurbLength Snowpack +*/ +int subcatch_readParams(int subcatchIndex, char *tok[], int ntoks); + +/*! +* \brief Rreads subcatchment's subarea parameters from a tokenized +* line of input data. +* \param[in] tok Array of string tokens +* \param[in] ntoks Number of tokens +* \return Error code +* \details Data has format: +* Subcatch Imperv_N Perv_N Imperv_S Perv_S PctZero RouteTo (PctRouted) +*/ +int subcatch_readSubareaParams(char *tok[], int ntoks); + +/*! +* \brief Reads assignment of landuses to subcatchment from a tokenized +* line of input data. +* \param[in] tok Array of string tokens +* \param[in] ntoks Number of tokens +* \return Error code +* \details Data has format: +* Subcatch landuse percent .... landuse percent +*/ +int subcatch_readLanduseParams(char *tok[], int ntoks); + +/*! +* \brief Reads initial pollutant buildup on subcatchment from +* tokenized line of input data. +* \param[in] tok Array of string tokens +* \param[in] ntoks Number of tokens +* \return Error code +* \details Data has format: +* Subcatch pollut initLoad .... pollut initLoad +*/ +int subcatch_readInitBuildup(char *tok[], int ntoks); + +/*! +* \brief Checks for valid subcatchment input parameters. +* \param[in] subcatchIndex Subcatchment index +*/ +void subcatch_validate(int subcatchIndex); + +/*! +* \brief Initializes state of a subcatchment. +* \param[in] subcatchIndex Subcatchment index +*/ +void subcatch_initState(int subcatchIndex); + +/*! +* \brief Replaces old state of subcatchment with new state. +* \param[in] subcatchIndex Subcatchment index +*/ +void subcatch_setOldState(int subcatchIndex); + +/*! +* \brief Determines what fraction of subcatchment area, including any LID +* area, is pervious. +* \param[in] subcatchIndex Subcatchment index +* \return Returns fraction of subcatchment area that is pervious cover +*/ +double subcatch_getFracPerv(int subcatchIndex); + +/*! +* \brief Finds total volume of water stored on a subcatchment's surface +* and its LIDs at the current time. +* \param[in] subcatchIndex Subcatchment index +* \return Returns total volume of water stored on subcatchment's surface (ft3) +*/ +double subcatch_getStorage(int subcatchIndex); + +/*! +* \brief Finds average depth of water over the non-LID portion of a +* subcatchment +* \param[in] subcatchIndex Subcatchment index +* \return Returns average depth of ponded water (ft) +*/ +double subcatch_getDepth(int subcatchIndex); + +/*! +* \brief Routes runoff from a subcatchment to its outlet subcatchment +* or between its subareas. +* \param[in] subcatchIndex Subcatchment index +*/ +void subcatch_getRunon(int subcatchIndex); + +/*! +* \brief Updates the total runon flow (ft/s) seen by a subcatchment that +* receives runon flow from an upstream subcatchment. +* \param[in] subcatchIndex Subcatchment index +* \param[in] flow Runon flow rate (cfs) to subcatchment subcatchIndex +*/ +void subcatch_addRunonFlow(int subcatchIndex, double flow); + +/*! +* \brief Computes runoff & new storage depth for subcatchment. +* \param[in] subcatchIndex Subcatchment index +* \param[in] tStep Time step (sec) +* \details +* The 'runoff' value returned by this function is the total runoff +* generated (in ft/sec) by the subcatchment before any internal +* re-routing is applied. It is used to compute pollutant washoff. +* +* The 'outflow' value computed here (in cfs) is the surface runoff +* that actually leaves the subcatchment after any LID controls are +* applied and is saved to Subcatch[j].newRunoff. +*/ +double subcatch_getRunoff(int subcatchIndex, double tStep); + +/*! +* \brief Computes weighted combination of old and new subcatchment runoff. +* \param[in] subcatchIndex Subcatchment index +* \param[in] wt Weighting factor +* \return Returns weighted runoff value (ft/sec) +*/ +double subcatch_getWtdOutflow(int subcatchIndex, double wt); + +/*! +* \brief Computes wtd. combination of old and new subcatchment results. +* \param[in] subcatchIndex Subcatchment index +* \param[in] wt Weighting factor +* \param[out] x Array of subcatchment results +*/ +void subcatch_getResults(int subcatchIndex, double wt, float x[]); +/*! + * \} + */ + +/*! + * \addtogroup Surface_Pollutant_File_Methods Global Surface Pollutant Buildup/Washoff Methods + * \brief Global surface pollutant buildup/washoff methods + * \ingroup Global_Interfacing_Functions + * \{ + */ +/*! +* \brief Initializes pollutant buildup, ponded mass, and washoff. +* \param[in] subcatchIndex Subcatchment index +*/ +void surfqual_initState(int subcatchIndex); + +/*! +* \brief Computes new runoff quality for a subcatchment. +* \param[in] subcatchIndex Subcatchment index +* \param[in] runoff Total subcatchment runoff before internal re-routing or +* LID controls (ft/sec) +* \param[in] tStep Time step (sec) +* \details +* Considers three pollutant generating streams that are combined together: +* 1. washoff of pollutant buildup as described by the project's land use +* washoff functions, +* 2. complete mix mass balance of pollutants in surface ponding on +* non-LID area due to runon, wet deposition, infiltration, & evaporation, +* 3. wet deposition and runon over LID areas. +*/ +void surfqual_getWashoff(int subcatchIndex, double runoff, double tStep); + +/*! +* \brief Adds to pollutant buildup on subcatchment surface. +* \param[in] subcatchIndex Subcatchment index +* \param[in] tStep Time step (sec) +*/ +void surfqual_getBuildup(int subcatchIndex, double tStep); + +/*! +* \brief Reduces pollutant buildup over a subcatchment if sweeping occurs. +* \param[in] subcatchIndex Subcatchment index +* \param[in] aDate Current date/time +*/ +void surfqual_sweepBuildup(int subcatchIndex, DateTime aDate); + +/*! +* \brief Finds wtd. combination of old and new washoff for a pollutant. +* \param[in] subcatchIndex Subcatchment index +* \param[in] pollutIndex Pollutant index +* \param[in] wt Weighting factor +* \return Returns pollutant washoff value +*/ +double surfqual_getWtdWashoff(int subcatchIndex, int pollutIndex, double wt); +/*! + * \} + */ + +/*! + * \addtogroup Conveyance_System_Node_File_Methods Global Conveyance System Node Methods + * \brief Global conveyance system node methods + * \ingroup Global_Interfacing_Functions + * \{ + */ + +/*! +* \brief Reads node properties from a tokenized line of input. +* \param[in] nodeIndex Node index +* \param[in] type Node type code +* \param[in] subIndex Node sub-type code +* \param[in] tok Array of string tokens +* \param[in] ntoks Number of tokens +* \return Error code +*/ +int node_readParams(int nodeIndex, int type, int subIndex, char *tok[], int ntoks); + +/*! +* \brief Validates a node's properties. +* \param[in] nodeIndex Node index +*/ +void node_validate(int nodeIndex); + +/*! +* \brief Initializes a node's state variables at start of simulation. +* \param[in] nodeIndex Node index +*/ +void node_initState(int nodeIndex); + +/*! +* \brief Initializes a node's inflow/outflow/overflow at start of time step. +* \param[in] nodeIndex Node index +* \param[in] tStep Time step (sec) +*/ +void node_initFlows(int nodeIndex, double tStep); + +/*! +* \brief Replaces a node's old hydraulic state values with new ones. +* \param[in] nodeIndex Node index +*/ +void node_setOldHydState(int nodeIndex); + +/*! +* \brief Replaces a node's old water quality state values with new ones. +* \param[in] nodeIndex Node index +*/ +void node_setOldQualState(int nodeIndex); + +/*! +* \brief Sets water depth at a node that serves as an outlet point. +* \param[in] nodeIndex Node index +* \param[in] yNorm Normal flow depth (ft) +* \param[in] yCrit Critical flow depth (ft) +* \param[in] z Offset of connecting outfall link from node invert (ft) +*/ +void node_setOutletDepth(int nodeIndex, double yNorm, double yCrit, double z); + +/*! +* \brief Computes surface area of water stored at a node from water depth. +* \param[in] nodeIndex Node index +* \param[in] depth Water depth (ft) +* \return Returns surface area of water stored at node (ft2) +*/ +double node_getSurfArea(int nodeIndex, double depth); + +/*! +* \brief Computes a node's water depth from its volume. +* \param[in] nodeIndex Node index +* \param[in] volume Volume of water stored at node (ft3) +* \return Returns water depth at node (ft) +*/ +double node_getDepth(int nodeIndex, double volume); + +/*! +* \brief Computes volume stored at a node from its water depth. +* \param[in] nodeIndex Node index +* \param[in] depth Water depth at node (ft) +* \return Returns volume of water stored at node (ft3) +*/ +double node_getVolume(int nodeIndex, double depth); + +/*! +* \brief Computes surface area of water at a node based on depth. +* \param[in] nodeIndex Node index +* \param[in] depth Water depth at node (ft) +* \return Returns surface area of water at node (ft2) +*/ +double node_getPondedArea(int nodeIndex, double depth); + +/*! +* \brief Computes outflow from node available for inflow into a link. +* \param[in] nodeIndex Node index +* \param[in] linkIndex Link index +* \return Returns flow rate leaving node (cfs) +*/ +double node_getOutflow(int nodeIndex, int linkIndex); + +/*! +* \brief Computes the rates of evaporation and infiltration over a given +* time step for a node. +* \param[in] nodeIndex Node index +* \param[in] tStep Time step (sec) +* \return Returns water loss rate at node (ft3) +*/ +double node_getLosses(int nodeIndex, double tStep); + +/*! +* \brief Limits outflow rate from a node with storage volume. +* \param[in] nodeIndex Node index +* \param[in] q Flow rate leaving node (cfs) +* \param[in] tStep Time step (sec) +* \return Returns modified flow rate (cfs) +*/ +double node_getMaxOutflow(int nodeIndex, double q, double tStep); + +/*! +* \brief Computes flow rate at outfalls and flooded nodes. +* \param[in] nodeIndex Node index +* \param[out] isFlooded TRUE if node is flooded +* \return Returns flow rate lost from system (cfs) +*/ +double node_getSystemOutflow(int nodeIndex, int *isFlooded); + +/*! +* \brief Computes weighted average of old and new results at a node. +* \param[in] nodeIndex Node index +* \param[in] wt Weighting factor +* \param[out] x Array of node results +*/ +void node_getResults(int nodeIndex, double wt, float x[]); +/*! + * \} + */ + +/*! + * \addtogroup Conveyance_System_Inflow_File_Methods Global Conveyance System Inflow Methods + * \brief Global conveyance system inflow methods + * \ingroup Global_Interfacing_Functions + * \{ + */ +/*! +* \brief Reads parameters of a direct external inflow from a line of input. +* \param[in] tok Array of string tokens +* \param[in] ntoks Number of tokens +* \return Error code +* \details Data formats of data line are: +* nodeID FLOW tSeriesID (FLOW 1.0 scaleFactor baseline basePat) +* nodeID pollutID tSeriesID (CONCEN/MASS unitsFactor scaleFactor baseline basePat) +*/ +int inflow_readExtInflow(char *tok[], int ntoks); + +/*! +* \brief Reads dry weather inflow parameters from line of input data. +* \param[in] tok Array of string tokens +* \param[in] ntoks Number of tokens +* \return Error code +* \details Format of data line is: +* nodeID FLOW/pollutID avgValue (pattern1 pattern2 ... pattern4) +*/ +int inflow_readDwfInflow(char *tok[], int ntoks); + +/*! +* \brief Reads values of a time pattern from a line of input data. +* \param[in] tok Array of string tokens +* \param[in] ntoks Number of tokens +* \return Error code +* \details Format of data line is: +* patternID patternType value(1) value(2) ... +* patternID value(n) value(n+1) .... (for continuation lines) +*/ +int inflow_readDwfPattern(char *tok[], int ntoks); + +/*! +* \brief This function assigns property values to the inflow object. +* \param[in] nodeIndex Node index +* \param[in] param Parameter code +* \param[in] type Inflow type code +* \param[in] tSeries Time series index +* \param[in] basePat Base pattern index +* \param[in] cf Conversion factor +* \param[in] baseline Baseline value +* \param[in] sf Scale factor +* \return Error code +*/ +int inflow_setExtInflow(int nodeIndex, int param, int type, int tSeries, + int basePat, double cf, double baseline, double sf); + +/*! +* \brief Initialzes a dry weather inflow by ordering its time patterns. +* \param[in] inflow Dry weather inflow object +* \details This function sorts the user-supplied time patterns for a dry weather +* inflow in the order of the PatternType enumeration (monthly, daily, +* weekday hourly, weekend hourly) to help speed up pattern processing. +*/ +void inflow_initDwfInflow(TDwfInflow *inflow); + +/*! +* \brief Initialzes a dry weather inflow time pattern. +* \param[in] patternIndex Time pattern index +*/ +void inflow_initDwfPattern(int patternIndex); + +/*! +* \brief Retrieves the value of an external inflow at a specific +* date and time. +* \param[in] inflow External inflow data structure +* \param[in] aDate Current simulation date/time +* \return Returns current value of external inflow parameter +*/ +double inflow_getExtInflow(TExtInflow *inflow, DateTime aDate); + +/*! +* \brief Computes dry weather inflow value at a specific point in time. +* \param[in] inflow Dry weather inflow data structure +* \param[in] m Current month of year for the simulation +* \param[in] d Current day of month for the simulation +* \param[in] h Current hour of day for the simulation +* \return Returns dry weather inflow value +*/ +double inflow_getDwfInflow(TDwfInflow *inflow, int m, int d, int h); + +/*! +* \brief Deletes all time series inflow data for a node. +* \param[in] nodeIndex Node index +*/ +void inflow_deleteExtInflows(int nodeIndex); + +/*! +* \brief Deletes all dry weather inflow data for a node. +* \param[in] nodeIndex Node index +*/ +void inflow_deleteDwfInflows(int nodeIndex); +/*! + * \} + */ //----------------------------------------------------------------------------- -// Rainfall Processing Methods -//----------------------------------------------------------------------------- -void rain_open(void); -void rain_close(void); - -//----------------------------------------------------------------------------- -// Snowmelt Processing Methods -//----------------------------------------------------------------------------- -int snow_readMeltParams(char* tok[], int ntoks); -int snow_createSnowpack(int subcacth, int snowIndex); - -void snow_validateSnowmelt(int snowIndex); -void snow_initSnowpack(int subcatch); -void snow_initSnowmelt(int snowIndex); - -void snow_getState(int subcatch, int subArea, double x[]); -void snow_setState(int subcatch, int subArea, double x[]); - -void snow_setMeltCoeffs(int snowIndex, double season); -void snow_plowSnow(int subcatch, double tStep); -double snow_getSnowMelt(int subcatch, double rainfall, double snowfall, - double tStep, double netPrecip[]); -double snow_getSnowCover(int subcatch); - -//----------------------------------------------------------------------------- -// Runoff Analyzer Methods -//----------------------------------------------------------------------------- -int runoff_open(void); -void runoff_execute(void); -void runoff_close(void); - -//----------------------------------------------------------------------------- -// Conveyance System Routing Methods -//----------------------------------------------------------------------------- -int routing_open(void); -double routing_getRoutingStep(int routingModel, double fixedStep); -void routing_execute(int routingModel, double routingStep); -void routing_close(int routingModel); - -//----------------------------------------------------------------------------- -// Output Filer Methods -//----------------------------------------------------------------------------- -int output_open(void); -void output_end(void); -void output_close(void); -void output_saveResults(double reportTime); -void output_updateAvgResults(void); -void output_readDateTime(long period, DateTime *aDate); -void output_readSubcatchResults(long period, int index); -void output_readNodeResults(long period, int index); -void output_readLinkResults(long period, int index); - -//----------------------------------------------------------------------------- -// Groundwater Methods -//----------------------------------------------------------------------------- -int gwater_readAquiferParams(int aquifer, char* tok[], int ntoks); -int gwater_readGroundwaterParams(char* tok[], int ntoks); -int gwater_readFlowExpression(char* tok[], int ntoks); -void gwater_deleteFlowExpression(int subcatch); - -void gwater_validateAquifer(int aquifer); -void gwater_validate(int subcatch); - -void gwater_initState(int subcatch); -void gwater_getState(int subcatch, double x[]); -void gwater_setState(int subcatch, double x[]); - -void gwater_getGroundwater(int subcatch, double evap, double infil, - double tStep); -double gwater_getVolume(int subcatch); - -//----------------------------------------------------------------------------- -// RDII Methods -//----------------------------------------------------------------------------- -int rdii_readRdiiInflow(char* tok[], int ntoks); -void rdii_deleteRdiiInflow(int node); -void rdii_initUnitHyd(int unitHyd); -int rdii_readUnitHydParams(char* tok[], int ntoks); -void rdii_openRdii(void); -void rdii_closeRdii(void); -int rdii_getNumRdiiFlows(DateTime aDate); -void rdii_getRdiiFlow(int index, int* node, double* q); - -//----------------------------------------------------------------------------- -// Landuse Methods -//----------------------------------------------------------------------------- -int landuse_readParams(int landuse, char* tok[], int ntoks); -int landuse_readPollutParams(int pollut, char* tok[], int ntoks); -int landuse_readBuildupParams(char* tok[], int ntoks); -int landuse_readWashoffParams(char* tok[], int ntoks); - -void landuse_getInitBuildup(TLandFactor* landFactor, double* initBuildup, - double area, double curb); -double landuse_getBuildup(int landuse, int pollut, double area, double curb, - double buildup, double tStep); - -double landuse_getWashoffLoad(int landuse, int p, double area, - TLandFactor landFactor[], double runoff, double vOutflow); -double landuse_getAvgBmpEffic(int j, int p); -double landuse_getCoPollutLoad(int p, double washoff[]); - -//----------------------------------------------------------------------------- -// Flow/Quality Routing Methods -//----------------------------------------------------------------------------- -void flowrout_init(int routingModel); -void flowrout_close(int routingModel); -double flowrout_getRoutingStep(int routingModel, double fixedStep); -int flowrout_execute(int links[], int routingModel, double tStep); - -void toposort_sortLinks(int links[]); -int kinwave_execute(int link, double* qin, double* qout, double tStep); - -void dynwave_validate(void); -void dynwave_init(void); -void dynwave_close(void); -double dynwave_getRoutingStep(double fixedStep); -int dynwave_execute(double tStep); -void dwflow_findConduitFlow(int j, int steps, double omega, double dt); - -void qualrout_init(void); -void qualrout_execute(double tStep); - -//----------------------------------------------------------------------------- -// Treatment Methods -//----------------------------------------------------------------------------- -int treatmnt_open(void); -void treatmnt_close(void); -int treatmnt_readExpression(char* tok[], int ntoks); -void treatmnt_delete(int node); -void treatmnt_treat(int node, double q, double v, double tStep); -void treatmnt_setInflow(double qIn, double wIn[]); - -//----------------------------------------------------------------------------- -// Mass Balance Methods -//----------------------------------------------------------------------------- -int massbal_open(void); -void massbal_close(void); -void massbal_report(void); - -void massbal_updateRunoffTotals(int type, double v); -void massbal_updateLoadingTotals(int type, int pollut, double w); -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); -void massbal_addOutflowFlow(double q, int isFlooded); -void massbal_addOutflowQual(int pollut, double mass, int isFlooded); -void massbal_addNodeLosses(double evapLoss, double infilLoss); -void massbal_addLinkLosses(double evapLoss, double infilLoss); -void massbal_addReactedMass(int pollut, double mass); -void massbal_addSeepageLoss(int pollut, double seepLoss); -void massbal_addToFinalStorage(int pollut, double mass); -double massbal_getStepFlowError(void); -double massbal_getRunoffError(void); -double massbal_getFlowError(void); - -//----------------------------------------------------------------------------- -// Simulation Statistics Methods -//----------------------------------------------------------------------------- -int stats_open(void); -void stats_close(void); -void stats_report(void); - -void stats_updateCriticalTimeCount(int node, int link); -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); -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 -//----------------------------------------------------------------------------- -int gage_readParams(int gage, char* tok[], int ntoks); -void gage_validate(int gage); -void gage_initState(int gage); -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 -//----------------------------------------------------------------------------- -int subcatch_readParams(int subcatch, char* tok[], int ntoks); -int subcatch_readSubareaParams(char* tok[], int ntoks); -int subcatch_readLanduseParams(char* tok[], int ntoks); -int subcatch_readInitBuildup(char* tok[], int ntoks); - -void subcatch_validate(int subcatch); -void subcatch_initState(int subcatch); -void subcatch_setOldState(int subcatch); - -double subcatch_getFracPerv(int subcatch); -double subcatch_getStorage(int subcatch); -double subcatch_getDepth(int subcatch); - -void subcatch_getRunon(int subcatch); -void subcatch_addRunonFlow(int subcatch, double flow); -double subcatch_getRunoff(int subcatch, double tStep); - -double subcatch_getWtdOutflow(int subcatch, double wt); -void subcatch_getResults(int subcatch, double wt, float x[]); - -//----------------------------------------------------------------------------- -// Surface Pollutant Buildup/Washoff Methods +// Routing Interface File Methods //----------------------------------------------------------------------------- -void surfqual_initState(int subcatch); -void surfqual_getWashoff(int subcatch, double runoff, double tStep); -void surfqual_getBuildup(int subcatch, double tStep); -void surfqual_sweepBuildup(int subcatch, DateTime aDate); -double surfqual_getWtdWashoff(int subcatch, int pollut, double wt); +int iface_readFileParams(char *tok[], int ntoks); +void iface_openRoutingFiles(void); +void iface_closeRoutingFiles(void); +int iface_getNumIfaceNodes(DateTime aDate); +int iface_getIfaceNode(int index); +double iface_getIfaceFlow(int index); +double iface_getIfaceQual(int index, int pollut); +void iface_saveOutletResults(DateTime reportDate, FILE *file); +/*! + * \} + */ //----------------------------------------------------------------------------- -// Conveyance System Node Methods +// Hot Start File Methods //----------------------------------------------------------------------------- -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_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); - -double node_getSurfArea(int node, double depth); -double node_getDepth(int node, double volume); -double node_getVolume(int node, double depth); -double node_getPondedArea(int node, double depth); - -double node_getOutflow(int node, int link); -double node_getLosses(int node, double tStep); -double node_getMaxOutflow(int node, double q, double tStep); -double node_getSystemOutflow(int node, int *isFlooded); -void node_getResults(int node, double wt, float x[]); +int hotstart_open(void); +void hotstart_save(void); +int hotstart_save_to_file(const char *hotstartFile); +int hotstart_is_valid(const char *hotstartFile, int *inputFileVersion); +void hotstart_close(void); +/*! + * \} + */ //----------------------------------------------------------------------------- -// Conveyance System Inflow Methods +// Conveyance System Link Methods //----------------------------------------------------------------------------- -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); - -void inflow_initDwfInflow(TDwfInflow* inflow); -void inflow_initDwfPattern(int pattern); +int link_readParams(int link, int type, int subIndex, char *tok[], int ntoks); +int link_readXsectParams(char *tok[], int ntoks); +int link_readLossParams(char *tok[], int ntoks); -double inflow_getExtInflow(TExtInflow* inflow, DateTime aDate); -double inflow_getDwfInflow(TDwfInflow* inflow, int m, int d, int h); +void link_validate(int link); +void link_initState(int link); +void link_setOldHydState(int link); +void link_setOldQualState(int link); -void inflow_deleteExtInflows(int node); -void inflow_deleteDwfInflows(int node); +void link_setTargetSetting(int j); +void link_setSetting(int j, double tstep); +int link_setFlapGate(int link, int n1, int n2, double q); -//----------------------------------------------------------------------------- -// Routing Interface File Methods -//----------------------------------------------------------------------------- -int iface_readFileParams(char* tok[], int ntoks); -void iface_openRoutingFiles(void); -void iface_closeRoutingFiles(void); -int iface_getNumIfaceNodes(DateTime aDate); -int iface_getIfaceNode(int index); -double iface_getIfaceFlow(int index); -double iface_getIfaceQual(int index, int pollut); -void iface_saveOutletResults(DateTime reportDate, FILE* file); +double link_getInflow(int link); +void link_setOutfallDepth(int link); +double link_getLength(int link); +double link_getYcrit(int link, double q); +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, int routeModel, double q, double tstep); +char link_getFullState(double a1, double a2, double aFull); -//----------------------------------------------------------------------------- -// Hot Start File Methods -//----------------------------------------------------------------------------- -int hotstart_open(void); -void hotstart_save(void); -int hotstart_save_to_file(const char* hotstartFile); -int hotstart_is_valid(const char* hotstartFile, int *inputFileVersion); -void hotstart_close(void); - -//----------------------------------------------------------------------------- -// Conveyance System Link Methods -//----------------------------------------------------------------------------- -int link_readParams(int link, int type, int subIndex, char* tok[], int ntoks); -int link_readXsectParams(char* tok[], int ntoks); -int link_readLossParams(char* tok[], int ntoks); - -void link_validate(int link); -void link_initState(int link); -void link_setOldHydState(int link); -void link_setOldQualState(int link); - -void link_setTargetSetting(int j); -void link_setSetting(int j, double tstep); -int link_setFlapGate(int link, int n1, int n2, double q); - -double link_getInflow(int link); -void link_setOutfallDepth(int link); -double link_getLength(int link); -double link_getYcrit(int link, double q); -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, int routeModel, double q, double tstep); -char link_getFullState(double a1, double a2, double aFull); - -void link_getResults(int link, double wt, float x[]); +void link_getResults(int link, double wt, float x[]); +/*! + * \} + */ //----------------------------------------------------------------------------- // Link Cross-Section Methods //----------------------------------------------------------------------------- -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); -double xsect_getYofA(TXsect* xsect, double area); -double xsect_getRofA(TXsect* xsect, double area); -double xsect_getAofS(TXsect* xsect, double sFactor); -double xsect_getdSdA(TXsect* xsect, double area); -double xsect_getAofY(TXsect* xsect, double y); -double xsect_getRofY(TXsect* xsect, double y); -double xsect_getWofY(TXsect* xsect, double y); -double xsect_getYcrit(TXsect* xsect, double q); +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); +double xsect_getYofA(TXsect *xsect, double area); +double xsect_getRofA(TXsect *xsect, double area); +double xsect_getAofS(TXsect *xsect, double sFactor); +double xsect_getdSdA(TXsect *xsect, double area); +double xsect_getAofY(TXsect *xsect, double y); +double xsect_getRofY(TXsect *xsect, double y); +double xsect_getWofY(TXsect *xsect, double y); +double xsect_getYcrit(TXsect *xsect, double q); +/*! + * \} + */ //----------------------------------------------------------------------------- // Culvert/Roadway Methods //----------------------------------------------------------------------------- -double culvert_getInflow(int link, double q, double h); -double roadway_getInflow(int link, double dir, double hcrest, double h1, - double h2); +double culvert_getInflow(int link, double q, double h); +double roadway_getInflow(int link, double dir, double hcrest, double h1, + double h2); +/*! + * \} + */ //----------------------------------------------------------------------------- // Force Main Methods //----------------------------------------------------------------------------- -double forcemain_getEquivN(int j, int k); -double forcemain_getRoughFactor(int j, double lengthFactor); -double forcemain_getFricSlope(int j, double v, double hrad); +double forcemain_getEquivN(int j, int k); +double forcemain_getRoughFactor(int j, double lengthFactor); +double forcemain_getFricSlope(int j, double v, double hrad); +/*! + * \} + */ //----------------------------------------------------------------------------- // Cross-Section Transect Methods //----------------------------------------------------------------------------- -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); +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); +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 //----------------------------------------------------------------------------- -int shape_validate(TShape *shape, TTable *curve); +int shape_validate(TShape *shape, TTable *curve); +/*! + * \} + */ //----------------------------------------------------------------------------- // Control Rule Methods //----------------------------------------------------------------------------- -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, - double tStep); +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, + double tStep); +/*! + * \} + */ //----------------------------------------------------------------------------- // Table & Time Series Methods //----------------------------------------------------------------------------- -int table_readCurve(char* tok[], int ntoks); -int table_readTimeseries(char* tok[], int ntoks); +int table_readCurve(char *tok[], int ntoks); +int table_readTimeseries(char *tok[], int ntoks); -int table_addEntry(TTable* table, double x, double y); -int table_getFirstEntry(TTable* table, double* x, double* y); -int table_getNextEntry(TTable* table, double* x, double* y); -void table_deleteEntries(TTable* table); +int table_addEntry(TTable *table, double x, double y); +int table_getFirstEntry(TTable *table, double *x, double *y); +int table_getNextEntry(TTable *table, double *x, double *y); +void table_deleteEntries(TTable *table); -void table_init(TTable* table); -int table_validate(TTable* table); +void table_init(TTable *table); +int table_validate(TTable *table); -double table_lookup(TTable* table, double x); -double table_lookupEx(TTable* table, double x); -double table_intervalLookup(TTable* table, double x); -double table_inverseLookup(TTable* table, double y); +double table_lookup(TTable *table, double x); +double table_lookupEx(TTable *table, double x); +double table_intervalLookup(TTable *table, double x); +double table_inverseLookup(TTable *table, double y); -double table_getSlope(TTable *table, double x); -double table_getMaxY(TTable *table, double x); -double table_getStorageVolume(TTable* table, double x); -double table_getStorageDepth(TTable* table, double v); +double table_getSlope(TTable *table, double x); +double table_getMaxY(TTable *table, double x); +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); +void table_tseriesInit(TTable *table); +double table_tseriesLookup(TTable *table, double t, char extend); +/*! + * \} + */ //----------------------------------------------------------------------------- // Utility Methods //----------------------------------------------------------------------------- -double UCF(int quantity); // units conversion factor -int getInt(char *s, int *y); // get integer from string -int getFloat(char *s, float *y); // get float from string -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(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 +double UCF(int quantity); // units conversion factor +int getInt(char *s, int *y); // get integer from string +int getFloat(char *s, float *y); // get float from string +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(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/src/solver/gage.c b/src/solver/gage.c index 031cc017c..435a036ff 100644 --- a/src/solver/gage.c +++ b/src/solver/gage.c @@ -53,20 +53,17 @@ static int getNextRainfall(int gage); static double convertRainfall(int gage, double rain); static void initPastRain(int gage); -//============================================================================= - -int gage_readParams(int j, char* tok[], int ntoks) -// -// Input: j = rain gage index -// tok[] = array of string tokens -// ntoks = number of tokens -// Output: returns an error code -// Purpose: reads rain gage parameters from a line of input data -// -// Data formats are: -// Name RainType RecdFreq SCF TIMESERIES SeriesName -// Name RainType RecdFreq SCF FILE FileName Station Units StartDate -// +/*! +* \brief Reads rain gage parameters from a line of input data +* \param[in] gageIndex Gage index +* \param[in] tok Array of string tokens +* \param[in] ntoks Number of tokens +* \return Error code +* \details Data formats are: +* Name RainType RecdFreq SCF TIMESERIES SeriesName +* Name RainType RecdFreq SCF FILE FileName Station Units StartDate +*/ +int gage_readParams(int gageIndex, char* tok[], int ntoks) { int k, err; char *id; @@ -107,24 +104,24 @@ int gage_readParams(int j, char* tok[], int ntoks) // --- save parameters to rain gage object if ( err > 0 ) return err; - Gage[j].ID = id; - Gage[j].tSeries = (int)x[0]; - Gage[j].rainType = (int)x[1]; - Gage[j].rainInterval = (int)x[2]; - Gage[j].snowFactor = x[3]; - Gage[j].rainUnits = (int)x[6]; - if ( Gage[j].tSeries >= 0 ) Gage[j].dataSource = RAIN_TSERIES; - else Gage[j].dataSource = RAIN_FILE; - if ( Gage[j].dataSource == RAIN_FILE ) + Gage[gageIndex].ID = id; + Gage[gageIndex].tSeries = (int)x[0]; + Gage[gageIndex].rainType = (int)x[1]; + Gage[gageIndex].rainInterval = (int)x[2]; + Gage[gageIndex].snowFactor = x[3]; + Gage[gageIndex].rainUnits = (int)x[6]; + if ( Gage[gageIndex].tSeries >= 0 ) Gage[gageIndex].dataSource = RAIN_TSERIES; + else Gage[gageIndex].dataSource = RAIN_FILE; + if ( Gage[gageIndex].dataSource == RAIN_FILE ) { - sstrncpy(Gage[j].fname, addAbsolutePath(fname), MAXFNAME); - sstrncpy(Gage[j].staID, staID, MAXMSG); - Gage[j].startFileDate = x[4]; - Gage[j].endFileDate = x[5]; + sstrncpy(Gage[gageIndex].fname, addAbsolutePath(fname), MAXFNAME); + sstrncpy(Gage[gageIndex].staID, staID, MAXMSG); + Gage[gageIndex].startFileDate = x[4]; + Gage[gageIndex].endFileDate = x[5]; } - Gage[j].unitsFactor = 1.0; - Gage[j].coGage = -1; - Gage[j].isUsed = FALSE; + Gage[gageIndex].unitsFactor = 1.0; + Gage[gageIndex].coGage = -1; + Gage[gageIndex].isUsed = FALSE; return 0; } @@ -204,40 +201,35 @@ int readGageFileFormat(char* tok[], int ntoks, double x[]) return 0; } -//============================================================================= - -void gage_validate(int j) -// -// Input: j = rain gage index -// Output: none -// Purpose: checks for valid rain gage parameters -// -// NOTE: assumes that any time series used by a rain gage has been -// previously validated. -// +/*! +* \brief Checks for valid rain gage parameters +* \param[in] gageIndex Gage index +* \note Assumes that any time series used by the gage has been previously validated +*/ +void gage_validate(int gageIndex) { int i, k; int gageInterval; // --- for gage with time series data: - if ( Gage[j].dataSource == RAIN_TSERIES ) + if ( Gage[gageIndex].dataSource == RAIN_TSERIES ) { // --- no validation for an unused gage - if ( !Gage[j].isUsed ) return; + if ( !Gage[gageIndex].isUsed ) return; // --- see if gage uses same time series as another gage - k = Gage[j].tSeries; - for (i=0; i= 0 ) { - report_writeErrorMsg(ERR_RAIN_GAGE_TSERIES, Gage[j].ID); + report_writeErrorMsg(ERR_RAIN_GAGE_TSERIES, Gage[gageIndex].ID); } gageInterval = (int)(floor(Tseries[k].dxMin*SECperDAY + 0.5)); - if ( gageInterval > 0 && Gage[j].rainInterval > gageInterval ) + if ( gageInterval > 0 && Gage[gageIndex].rainInterval > gageInterval ) { - report_writeErrorMsg(ERR_RAIN_GAGE_INTERVAL, Gage[j].ID); + report_writeErrorMsg(ERR_RAIN_GAGE_INTERVAL, Gage[gageIndex].ID); } - if ( Gage[j].rainInterval < gageInterval ) + if ( Gage[gageIndex].rainInterval < gageInterval ) { - report_writeWarningMsg(WARN09, Gage[j].ID); + report_writeWarningMsg(WARN09, Gage[gageIndex].ID); } - if ( Gage[j].rainInterval < WetStep ) + if ( Gage[gageIndex].rainInterval < WetStep ) { - report_writeWarningMsg(WARN01, Gage[j].ID); - WetStep = Gage[j].rainInterval; + report_writeWarningMsg(WARN01, Gage[gageIndex].ID); + WetStep = Gage[gageIndex].rainInterval; } } } -//============================================================================= - +/*! +* \brief Initializes state of a rain gage +* \param[in] gageIndex Rain gage index +*/ void gage_initState(int j) -// -// Input: j = rain gage index -// Output: none -// Purpose: initializes state of rain gage. -// { // --- initialize actual and reported rainfall Gage[j].rainfall = 0.0; @@ -412,66 +401,62 @@ void initPastRain(int j) 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. +/*! +* \brief Updates past MAXPASTRAIN hourly rain totals. +* \param[in] gageIndex Rain gage index +* \param[in] tStep Time step (sec) +* \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. +*/ +void gage_updatePastRain(int gageIndex, int tStep) { int i, t; double r; // --- current rainfall intensity (in/sec or mm/sec) - r = Gage[j].rainfall / 3600.; + r = Gage[gageIndex].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; + t = 3600 - Gage[gageIndex].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; + Gage[gageIndex].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]; + Gage[gageIndex].pastRain[i] = Gage[gageIndex].pastRain[i-1]; // --- begin a new most recent interval - Gage[j].pastInterval = 0; - Gage[j].pastRain[0] = 0.0; + Gage[gageIndex].pastInterval = 0; + Gage[gageIndex].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; + Gage[gageIndex].pastRain[0] += tStep * r; + Gage[gageIndex].pastInterval += tStep; tStep = 0; } } } -//============================================================================= - +/*! +* \brief Retrieves rainfall total over some previous number of hours. +* \param[in] gageIndex Rain gage index +* \param[in] hrs Number of hours +* \return Returns cumulative rain volume (inches or mm) in last n hours +*/ 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; @@ -481,67 +466,60 @@ double gage_getPastRain(int j, int n) return result; } -//============================================================================= - -DateTime gage_getNextRainDate(int j, DateTime aDate) -// -// Input: j = rain gage index -// aDate = calendar date/time -// Output: next date with rainfall occurring -// Purpose: finds the next date from specified date when rainfall occurs. -// +/*! +* \brief Finds the next date from specified date when rainfall occurs. +* \param[in] gageIndex Rain gage index +* \param[in] aDate Current date/time +* \return Returns next date when rainfall occurs +*/ +DateTime gage_getNextRainDate(int gageIndex, DateTime aDate) { - if ( Gage[j].isUsed == FALSE ) return aDate; + if ( Gage[gageIndex].isUsed == FALSE ) return aDate; aDate += OneSecond; - if ( aDate < Gage[j].startDate ) return Gage[j].startDate; - if ( aDate < Gage[j].endDate ) return Gage[j].endDate; - return Gage[j].nextDate; + if ( aDate < Gage[gageIndex].startDate ) return Gage[gageIndex].startDate; + if ( aDate < Gage[gageIndex].endDate ) return Gage[gageIndex].endDate; + return Gage[gageIndex].nextDate; } -//============================================================================= - -double gage_getPrecip(int j, double *rainfall, double *snowfall) -// -// Input: j = rain gage index -// Output: rainfall = rainfall rate (ft/sec) -// snowfall = snow fall rate (ft/sec) -// returns total precipitation (ft/sec) -// Purpose: determines whether gage's recorded rainfall is rain or snow. -// +/*! +* \brief Determines whether gage's recorded rainfall is rain or snow. +* \param[in] gageIndex Rain gage index +* \param[out] rainfall Rainfall rate (ft/sec) +* \param[out] snowfall Snowfall rate (ft/sec) +* \return Returns Total precipitation (ft/sec) +*/ +double gage_getPrecip(int gageIndex, double *rainfall, double *snowfall) { *rainfall = 0.0; *snowfall = 0.0; if ( !IgnoreSnowmelt && Temp.ta <= Snow.snotmp ) { - *snowfall = Gage[j].rainfall * Gage[j].snowFactor / UCF(RAINFALL); + *snowfall = Gage[gageIndex].rainfall * Gage[gageIndex].snowFactor / UCF(RAINFALL); } - else *rainfall = Gage[j].rainfall / UCF(RAINFALL); + else *rainfall = Gage[gageIndex].rainfall / UCF(RAINFALL); return (*rainfall) + (*snowfall); } -//============================================================================= - -void gage_setReportRainfall(int j, DateTime reportDate) -// -// Input: j = rain gage index -// reportDate = date/time value of current reporting time -// Output: none -// Purpose: sets the rainfall value reported at the current reporting time. -// +/*! +* \brief Sets the rainfall value reported at the current reporting time. +* \param[in] gageIndex Rain gage index +* \param[in] aDate Current date/time +*/ +void gage_setReportRainfall(int gageIndex, DateTime reportDate) { double result; // --- use value from co-gage if it exists - if ( Gage[j].coGage >= 0) + if ( Gage[gageIndex].coGage >= 0) { - Gage[j].reportRainfall = Gage[Gage[j].coGage].reportRainfall; + Gage[gageIndex].reportRainfall = Gage[Gage[gageIndex].coGage].reportRainfall; return; } // --- rainfall set by API call - if (Gage[j].apiRainfall != MISSING) + if (Gage[gageIndex].apiRainfall != MISSING) { - Gage[j].reportRainfall = Gage[j].apiRainfall; + Gage[gageIndex].reportRainfall = Gage[gageIndex].apiRainfall; return; } @@ -551,15 +529,15 @@ void gage_setReportRainfall(int j, DateTime reportDate) // --- use current rainfall if report date/time is before end // of current rain interval - if ( reportDate < Gage[j].endDate ) result = Gage[j].rainfall; + if ( reportDate < Gage[gageIndex].endDate ) result = Gage[gageIndex].rainfall; // --- use 0.0 if report date/time is before start of next rain interval - else if ( reportDate < Gage[j].nextDate ) result = 0.0; + else if ( reportDate < Gage[gageIndex].nextDate ) result = 0.0; // --- otherwise report date/time falls right on end of current rain // interval and start of next interval so use next interval's rainfall - else result = Gage[j].nextRainfall; - Gage[j].reportRainfall = result; + else result = Gage[gageIndex].nextRainfall; + Gage[gageIndex].reportRainfall = result; } //============================================================================= diff --git a/src/solver/gwater.c b/src/solver/gwater.c index 48db9de17..f35984b0c 100644 --- a/src/solver/gwater.c +++ b/src/solver/gwater.c @@ -114,9 +114,19 @@ static void updateMassBal(double area, double tStep); static int getVariableIndex(char* s); static double getVariableValue(int varIndex); -//============================================================================= - -int gwater_readAquiferParams(int j, char* tok[], int ntoks) +/*! +* \brief Reads aquifer parameter values from line of input data +* \param[in] aquiferIndex Index of aquifer +* \param[in] tok Array of string tokens +* \param[in] ntoks Number of tokens +* \return Error code +* \details Data line contains following parameters: +* ID, porosity, wiltingPoint, fieldCapacity, conductivity, +* conductSlope, tensionSlope, upperEvapFraction, lowerEvapDepth, +* gwRecession, bottomElev, waterTableElev, upperMoisture +* (evapPattern) +*/ +int gwater_readAquiferParams(int aquiferIndex, char* tok[], int ntoks) // // Input: j = aquifer index // tok[] = array of string tokens @@ -157,25 +167,33 @@ int gwater_readAquiferParams(int j, char* tok[], int ntoks) } // --- assign parameters to aquifer object - Aquifer[j].ID = id; - Aquifer[j].porosity = x[0]; - Aquifer[j].wiltingPoint = x[1]; - Aquifer[j].fieldCapacity = x[2]; - Aquifer[j].conductivity = x[3] / UCF(RAINFALL); - Aquifer[j].conductSlope = x[4]; - Aquifer[j].tensionSlope = x[5] / UCF(LENGTH); - Aquifer[j].upperEvapFrac = x[6]; - Aquifer[j].lowerEvapDepth = x[7] / UCF(LENGTH); - Aquifer[j].lowerLossCoeff = x[8] / UCF(RAINFALL); - Aquifer[j].bottomElev = x[9] / UCF(LENGTH); - Aquifer[j].waterTableElev = x[10] / UCF(LENGTH); - Aquifer[j].upperMoisture = x[11]; - Aquifer[j].upperEvapPat = p; + Aquifer[aquiferIndex].ID = id; + Aquifer[aquiferIndex].porosity = x[0]; + Aquifer[aquiferIndex].wiltingPoint = x[1]; + Aquifer[aquiferIndex].fieldCapacity = x[2]; + Aquifer[aquiferIndex].conductivity = x[3] / UCF(RAINFALL); + Aquifer[aquiferIndex].conductSlope = x[4]; + Aquifer[aquiferIndex].tensionSlope = x[5] / UCF(LENGTH); + Aquifer[aquiferIndex].upperEvapFrac = x[6]; + Aquifer[aquiferIndex].lowerEvapDepth = x[7] / UCF(LENGTH); + Aquifer[aquiferIndex].lowerLossCoeff = x[8] / UCF(RAINFALL); + Aquifer[aquiferIndex].bottomElev = x[9] / UCF(LENGTH); + Aquifer[aquiferIndex].waterTableElev = x[10] / UCF(LENGTH); + Aquifer[aquiferIndex].upperMoisture = x[11]; + Aquifer[aquiferIndex].upperEvapPat = p; return 0; } -//============================================================================= - +/*! +* \brief Reads groundwater inflow parameters for a subcatchment from +* a line of input data. +* \param[in] tok Array of string tokens +* \param[in] ntoks Number of tokens +* \return Error code +* \details Data format is: +* subcatch aquifer node surfElev a1 b1 a2 b2 a3 fixedDepth + +* (nodeElev bottomElev waterTableElev upperMoisture ) +*/ int gwater_readGroundwaterParams(char* tok[], int ntoks) // // Input: tok[] = array of string tokens @@ -310,58 +328,54 @@ int gwater_readFlowExpression(char* tok[], int ntoks) return 0; } -//============================================================================= - -void gwater_deleteFlowExpression(int j) -// -// Input: j = subcatchment index -// Output: none -// Purpose: deletes a subcatchment's custom groundwater flow expressions. -// +/*! +* \brief Veletes a subcatchment's custom groundwater flow expressions. +* \param[in] subcatchIndex Subcatchment index +*/ +void gwater_deleteFlowExpression(int subcatchIndex) { - mathexpr_delete(Subcatch[j].gwLatFlowExpr); - mathexpr_delete(Subcatch[j].gwDeepFlowExpr); + mathexpr_delete(Subcatch[subcatchIndex].gwLatFlowExpr); + mathexpr_delete(Subcatch[subcatchIndex].gwDeepFlowExpr); } -//============================================================================= - -void gwater_validateAquifer(int j) -// -// Input: j = aquifer index -// Output: none -// Purpose: validates groundwater aquifer properties . -// +/*! +* \brief Validates groundwater aquifer properties. +* \param[in] aquiferIndex Index of aquifer +*/ +void gwater_validateAquifer(int aquiferIndex) { int p; - if ( Aquifer[j].porosity <= 0.0 - || Aquifer[j].fieldCapacity >= Aquifer[j].porosity - || Aquifer[j].wiltingPoint >= Aquifer[j].fieldCapacity - || Aquifer[j].conductivity <= 0.0 - || Aquifer[j].conductSlope < 0.0 - || Aquifer[j].tensionSlope < 0.0 - || Aquifer[j].upperEvapFrac < 0.0 - || Aquifer[j].lowerEvapDepth < 0.0 - || Aquifer[j].waterTableElev < Aquifer[j].bottomElev - || Aquifer[j].upperMoisture > Aquifer[j].porosity - || Aquifer[j].upperMoisture < Aquifer[j].wiltingPoint ) - report_writeErrorMsg(ERR_AQUIFER_PARAMS, Aquifer[j].ID); - - p = Aquifer[j].upperEvapPat; + if ( Aquifer[aquiferIndex].porosity <= 0.0 + || Aquifer[aquiferIndex].fieldCapacity >= Aquifer[aquiferIndex].porosity + || Aquifer[aquiferIndex].wiltingPoint >= Aquifer[aquiferIndex].fieldCapacity + || Aquifer[aquiferIndex].conductivity <= 0.0 + || Aquifer[aquiferIndex].conductSlope < 0.0 + || Aquifer[aquiferIndex].tensionSlope < 0.0 + || Aquifer[aquiferIndex].upperEvapFrac < 0.0 + || Aquifer[aquiferIndex].lowerEvapDepth < 0.0 + || Aquifer[aquiferIndex].waterTableElev < Aquifer[aquiferIndex].bottomElev + || Aquifer[aquiferIndex].upperMoisture > Aquifer[aquiferIndex].porosity + || Aquifer[aquiferIndex].upperMoisture < Aquifer[aquiferIndex].wiltingPoint ) + report_writeErrorMsg(ERR_AQUIFER_PARAMS, Aquifer[aquiferIndex].ID); + + p = Aquifer[aquiferIndex].upperEvapPat; if ( p >= 0 && Pattern[p].type != MONTHLY_PATTERN ) { - report_writeErrorMsg(ERR_AQUIFER_PARAMS, Aquifer[j].ID); + report_writeErrorMsg(ERR_AQUIFER_PARAMS, Aquifer[aquiferIndex].ID); } } -//============================================================================= - -void gwater_validate(int j) +/*! +* \brief Validates groundwater parameters for a subcatchment. +* \param[in] subcatchIndex Subcatchment index +*/ +void gwater_validate(int subcatchIndex) { TAquifer a; // Aquifer data structure TGroundwater* gw; // Groundwater data structure - gw = Subcatch[j].groundwater; + gw = Subcatch[subcatchIndex].groundwater; if ( gw ) { a = Aquifer[gw->aquifer]; @@ -373,23 +387,20 @@ void gwater_validate(int j) // ... ground elevation can't be below water table elevation if ( gw->surfElev < gw->waterTableElev ) - report_writeErrorMsg(ERR_GROUND_ELEV, Subcatch[j].ID); + report_writeErrorMsg(ERR_GROUND_ELEV, Subcatch[subcatchIndex].ID); } } -//============================================================================= - -void gwater_initState(int j) -// -// Input: j = subcatchment index -// Output: none -// Purpose: initializes state of subcatchment's groundwater. -// +/*! +* \brief Initializes state of subcatchment's groundwater. +* \param[in] subcatchIndex Subcatchment index +*/ +void gwater_initState(int subcatchIndex) { TAquifer a; // Aquifer data structure TGroundwater* gw; // Groundwater data structure - gw = Subcatch[j].groundwater; + gw = Subcatch[subcatchIndex].groundwater; if ( gw ) { a = Aquifer[gw->aquifer]; @@ -416,36 +427,32 @@ void gwater_initState(int j) // ... initial available infiltration volume into upper zone gw->maxInfilVol = (gw->surfElev - gw->waterTableElev) * (a.porosity - gw->theta) / - subcatch_getFracPerv(j); + subcatch_getFracPerv(subcatchIndex); } } -//============================================================================= - -void gwater_getState(int j, double x[]) -// -// Input: j = subcatchment index -// Output: x[] = array of groundwater state variables -// Purpose: retrieves state of subcatchment's groundwater. -// +/*! +* \brief Retrieves state of subcatchment's groundwater. +* \param[in] subcatchIndex Subcatchment index +* \param[out] x Array of groundwater state variables +*/ +void gwater_getState(int subcatchIndex, double x[]) { - TGroundwater* gw = Subcatch[j].groundwater; + TGroundwater* gw = Subcatch[subcatchIndex].groundwater; x[0] = gw->theta; x[1] = gw->bottomElev + gw->lowerDepth; x[2] = gw->newFlow; x[3] = gw->maxInfilVol; } -//============================================================================= - -void gwater_setState(int j, double x[]) -// -// Input: j = subcatchment index -// x[] = array of groundwater state variables -// Purpose: assigns values to a subcatchment's groundwater state. -// +/*! +* \brief Assigns values to a subcatchment's groundwater state. +* \param[in] subcatchIndex Subcatchment index +* \param[in] x Array of groundwater state variables +*/ +void gwater_setState(int subcatchIndex, double x[]) { - TGroundwater* gw = Subcatch[j].groundwater; + TGroundwater* gw = Subcatch[subcatchIndex].groundwater; if ( gw == NULL ) return; gw->theta = x[0]; gw->lowerDepth = x[1] - gw->bottomElev; @@ -453,19 +460,17 @@ void gwater_setState(int j, double x[]) if ( x[3] != MISSING ) gw->maxInfilVol = x[3]; } -//============================================================================= - -double gwater_getVolume(int j) -// -// Input: j = subcatchment index -// Output: returns total volume of groundwater in ft/ft2 -// Purpose: finds volume of groundwater stored in upper & lower zones -// +/*! +* \brief Finds volume of groundwater stored in upper & lower zones. +* \param[in] subcatchIndex Subcatchment index +* \return Total volume of groundwater in ft/ft2 +*/ +double gwater_getVolume(int subcatchIndex) { TAquifer a; TGroundwater* gw; double upperDepth; - gw = Subcatch[j].groundwater; + gw = Subcatch[subcatchIndex].groundwater; if ( gw == NULL ) return 0.0; a = Aquifer[gw->aquifer]; upperDepth = gw->surfElev - gw->bottomElev - gw->lowerDepth; diff --git a/src/solver/include/swmm5.h b/src/solver/include/swmm5.h index b2e5af6bc..b5ccb1f4b 100644 --- a/src/solver/include/swmm5.h +++ b/src/solver/include/swmm5.h @@ -1,262 +1,655 @@ -//----------------------------------------------------------------------------- -// swmm5.h -// -// Project: EPA SWMM5 -// Version: 5.2 -// Date: 11/01/21 (Build 5.2.0) -// Author: L. Rossman -// -// Prototypes for SWMM5 API functions. -// -// Update History -// ============== -// Build 5.3.0: -// - Added new functions to support saving hotstart files at specific times. -// - Expansions to the SWMM API to include attributes of more objects and water quality. -// -//----------------------------------------------------------------------------- - +/*! \file swmm5.h + * \brief Prototypes for SWMM5 API functions. + * \author L. Rossman + * \date Created on: 2021-11-01 + * \date Last edited on: 2024-12-23 (Build 5.3.0) + * \details This file contains the prototypes for SWMM5 API functions. + * + * Update History + * ============== + * Build 5.3.0: + * - Added new functions to support saving hotstart files at specific times. + * - Expansions to the SWMM API to include attributes of more objects and water quality. + */ #ifndef SWMM5_H #define SWMM5_H -// --- define WINDOWS #undef WINDOWS #ifdef _WIN32 +/*! + * \def WINDOWS + * \brief Define for Windows platform + */ #define WINDOWS #endif #ifdef __WIN32__ +/*! + * \def WINDOWS + * \brief Define for Windows platform + */ #define WINDOWS #endif -// --- define DLLEXPORT - #ifdef WINDOWS + /*! + * \def DLLEXPORT + * \brief Export macro for Windows platform + */ #define DLLEXPORT __declspec(dllexport) __stdcall #else + /*! + * \def DLLEXPORT + * \brief Export macro for non-Windows platforms + */ #define DLLEXPORT #endif // --- use "C" linkage for C++ programs - #ifdef __cplusplus extern "C" { #endif +/*! +* \enum swmm_Object +* \brief Enumeration of object types used in SWMM5 +*/ typedef enum { + /*! \brief Rain gages */ swmm_GAGE, + /*! \brief Subcatchments */ swmm_SUBCATCH, + /*! \brief Nodes */ swmm_NODE, + /*! \brief Links */ swmm_LINK, + /*! \brief Pollutants */ swmm_POLLUTANT, + /*! \brief Land uses */ swmm_LANDUSE, + /*! \brief Time patterns */ swmm_TIME_PATTERN, + /*! \brief Curve functions */ swmm_CURVE, + /*! \brief Time series */ swmm_TIMESERIES, + /*! \brief Control rules */ swmm_CONTROL_RULE, + /*! \brief Transects */ swmm_TRANSECT, + /*! \brief Aquifers */ swmm_AQUIFER, + /*! \brief Unit hydrographs */ swmm_UNIT_HYDROGRAPH, + /*! \brief Snow packs */ swmm_SNOWPACK, + /*! \brief Cross section shape */ smmm_XSECTION_SHAPE, + /*! \brief Low impact development units */ swmm_LID, + /*! \brief Street*/ swmm_STREET, + /*! \brief Inlet */ swmm_INLET, + /*! \brief System */ swmm_SYSTEM = 100 } swmm_Object; +/*! +* \enum swmm_NodeType +* \brief Enumeration of node types used in SWMM5 +*/ typedef enum { + /*! \brief Junction node */ swmm_JUNCTION = 0, + /*! \brief Outfall node */ swmm_OUTFALL = 1, + /*! \brief Storage node */ swmm_STORAGE = 2, + /*! \brief Divider node */ swmm_DIVIDER = 3 } swmm_NodeType; +/*! +* \enum swmm_LinkType +* \brief Enumeration of link types used in SWMM5 +*/ typedef enum { + /*! \brief Conduit link */ swmm_CONDUIT = 0, + /*! \brief Pump link */ swmm_PUMP = 1, + /*! \brief Orifice link */ swmm_ORIFICE = 2, + /*! \brief Weir link */ swmm_WEIR = 3, + /*! \brief Outlet link */ swmm_OUTLET = 4 } swmm_LinkType; +/*! +* \enum swmm_GageProperty +* \brief Enumeration of gage properties used in SWMM5 +*/ typedef enum { + /*! \brief Total precipitation */ swmm_GAGE_TOTAL_PRECIPITATION = 100, + /*! \brief Rainfall */ swmm_GAGE_RAINFALL = 101, + /*! \brief Snowfall */ swmm_GAGE_SNOWFALL = 102, } swmm_GageProperty; +/*! +* \enum swmm_SubcatchProperty +* \brief Enumeration of subcatchment properties used in SWMM5 +*/ typedef enum { + /*! \brief Area */ swmm_SUBCATCH_AREA = 200, + /*! \brief Rain gage */ swmm_SUBCATCH_RAINGAGE = 201, + /*! \brief Rainfall */ swmm_SUBCATCH_RAINFALL = 202, + /*! \brief Evaporation */ swmm_SUBCATCH_EVAP = 203, + /*! \brief Infiltration */ swmm_SUBCATCH_INFIL = 204, + /*! \brief Runoff */ swmm_SUBCATCH_RUNOFF = 205, + /*! \brief Report flag */ swmm_SUBCATCH_RPTFLAG = 206, + /*! \brief Width */ swmm_SUBCATCH_WIDTH = 207, + /*! \brief Slope */ swmm_SUBCATCH_SLOPE = 208, + /*! \brief Curb length */ swmm_SUBCATCH_CURB_LENGTH = 209, + /*! \brief API provided rainfall */ swmm_SUBCATCH_API_RAINFALL = 210, + /*! \brief API provided snowfall */ swmm_SUBCATCH_API_SNOWFALL = 211, + /*! \brief Pollutant buildup */ swmm_SUBCATCH_POLLUTANT_BUILDUP = 212, + /*! \brief External pollutant buildup */ swmm_SUBCATCH_EXTERNAL_POLLUTANT_BUILDUP = 213, + /*! \brief Runoff concentration */ swmm_SUBCATCH_POLLUTANT_RUNOFF_CONCENTRATION = 214, + /*! \brief Ponded concentration */ swmm_SUBCATCH_POLLUTANT_PONDED_CONCENTRATION = 215, + /*! \brief Total pollutant load */ swmm_SUBCATCH_POLLUTANT_TOTAL_LOAD = 216, } swmm_SubcatchProperty; +/*! +* \enum swmm_NodeProperty +* \brief Enumeration of node properties used in SWMM5 +*/ typedef enum { + /*! \brief Node type */ swmm_NODE_TYPE = 300, + /*! \brief Elevation */ swmm_NODE_ELEV = 301, + /*! \brief Maximum depth */ swmm_NODE_MAXDEPTH = 302, + /*! \brief Depth */ swmm_NODE_DEPTH = 303, + /*! \brief Hydraulic head */ swmm_NODE_HEAD = 304, + /*! \brief Volume */ swmm_NODE_VOLUME = 305, + /*! \brief Lateral inflow */ swmm_NODE_LATFLOW = 306, + /*! \brief Inflow */ swmm_NODE_INFLOW = 307, + /*! \brief Overflow */ swmm_NODE_OVERFLOW = 308, + /*! \brief Report flag */ swmm_NODE_RPTFLAG = 309, + /*! \brief Surcharge depth */ swmm_NODE_SURCHARGE_DEPTH = 310, + /*! \brief Ponded area */ swmm_NODE_PONDED_AREA = 311, + /*! \brief Initial depth */ swmm_NODE_INITIAL_DEPTH = 312, + /*! \brief Pollutant concentration */ swmm_NODE_POLLUTANT_CONCENTRATION = 313, + /*! \brief Pollutant lateral mass flux inflow */ swmm_NODE_POLLUTANT_LATMASS_FLUX = 314, } swmm_NodeProperty; +/*! +* \enum swmm_LinkProperty +* \brief Enumeration of link properties used in SWMM5 +*/ typedef enum { + /*! \brief Link type */ swmm_LINK_TYPE = 400, + /*! \brief Upstream node */ swmm_LINK_NODE1 = 401, + /*! \brief Downstream node */ swmm_LINK_NODE2 = 402, + /*! \brief Length */ swmm_LINK_LENGTH = 403, + /*! \brief Slope */ swmm_LINK_SLOPE = 404, + /*! \brief Full depth */ swmm_LINK_FULLDEPTH = 405, + /*! \brief Full flow */ swmm_LINK_FULLFLOW = 406, + /*! \brief Setting */ swmm_LINK_SETTING = 407, + /*! \brief Time open */ swmm_LINK_TIMEOPEN = 408, + /*! \brief Time closed */ swmm_LINK_TIMECLOSED = 409, + /*! \brief Flow */ swmm_LINK_FLOW = 410, + /*! \brief Depth */ swmm_LINK_DEPTH = 411, + /*! \brief Velocity */ swmm_LINK_VELOCITY = 412, + /*! \brief Top width */ swmm_LINK_TOPWIDTH = 413, + /*! \brief Report flag */ swmm_LINK_RPTFLAG = 414, + /*! \brief Upstream invert offset */ swmm_LINK_OFFSET1 = 415, + /*! \brief Downstream invert offset */ swmm_LINK_OFFSET2 = 416, + /*! \brief Initial flow */ swmm_LINK_INITIAL_FLOW = 417, + /*! \brief Flow limit */ swmm_LINK_FLOW_LIMIT = 418, + /*! \brief Inlet loss */ swmm_LINK_INLET_LOSS = 419, + /*! \brief Outlet loss */ swmm_LINK_OUTLET_LOSS = 420, + /*! \brief Average loss */ swmm_LINK_AVERAGE_LOSS = 421, + /*! \brief Seepage rate */ swmm_LINK_SEEPAGE_RATE = 422, + /*! \brief Flap gate */ swmm_LINK_HAS_FLAPGATE = 423, + /*! \brief Pollutant concentration */ swmm_LINK_POLLUTANT_CONCENTRATION = 424, + /*! \brief Pollutant load */ swmm_LINK_POLLUTANT_LOAD = 425, + /*! \brief Pollutant lateral mass flux */ swmm_LINK_POLLUTANT_LATMASS_FLUX = 426, } swmm_LinkProperty; +/*! +* \enum swmm_SystemProperty +* \brief Enumeration of system properties used in SWMM5 +*/ typedef enum { + /*! \brief Start date */ swmm_STARTDATE = 0, + /*! \brief Current date */ swmm_CURRENTDATE = 1, + /*! \brief Elapsed time */ swmm_ELAPSEDTIME = 2, + /*! \brief Routing step */ swmm_ROUTESTEP = 3, + /*! \brief Maximum routing step */ swmm_MAXROUTESTEP = 4, + /*! \brief Reporting step */ swmm_REPORTSTEP = 5, + /*! \brief Total steps */ swmm_TOTALSTEPS = 6, + /*! \brief No report flag */ swmm_NOREPORT = 7, + /*! \brief Flow units */ swmm_FLOWUNITS = 8, + /*! \brief End date */ swmm_ENDDATE = 9, + /*! \brief Report start */ swmm_REPORTSTART = 10, + /*! \brief Unit system */ swmm_UNITSYSTEM = 11, + /*! \brief Surcharge method */ swmm_SURCHARGEMETHOD = 12, + /*! \brief Allow ponding */ swmm_ALLOWPONDING = 13, + /*! \brief Inertia damping */ swmm_INERTIADAMPING = 14, + /*! \brief Normal flow limited */ swmm_NORMALFLOWLTD = 15, + /*! \brief Skip steady state */ swmm_SKIPSTEADYSTATE = 16, + /*! \brief Ignore rainfall */ swmm_IGNORERAINFALL = 17, + /*! \brief Ignore RDII */ swmm_IGNORERDII = 18, + /*! \brief Ignore snowmelt */ swmm_IGNORESNOWMELT = 19, + /*! \brief Ignore groundwater */ swmm_IGNOREGROUNDWATER = 20, + /*! \brief Ignore routing */ swmm_IGNOREROUTING = 21, + /*! \brief Ignore quality */ swmm_IGNOREQUALITY = 22, + /*! \brief Error code */ swmm_ERROR_CODE = 23, + /*! \brief Rule step */ swmm_RULESTEP= 24, + /*! \brief Sweep start */ swmm_SWEEPSTART = 25, + /*! \brief Sweep end */ swmm_SWEEPEND = 26, + /*! \brief Maximum trials */ swmm_MAXTRIALS = 27, + /*! \brief Number of threads */ swmm_NUMTHREADS = 28, + /*! \brief Minimum routing step */ swmm_MINROUTESTEP = 29, + /*! \brief Lengthening step */ swmm_LENGTHENINGSTEP = 30, + /*! \brief Start dry days */ swmm_STARTDRYDAYS = 31, + /*! \brief Courant factor */ swmm_COURANTFACTOR = 32, + /*! \brief Minimum surface area */ swmm_MINSURFAREA = 33, + /*! \brief Minimum slope */ swmm_MINSLOPE = 34, + /*! \brief Runoff error */ swmm_RUNOFFERROR = 35, + /*! \brief Flow error */ swmm_FLOWERROR = 36, + /*! \brief Quality error */ swmm_QUALERROR = 37, + /*! \brief Head tolerance */ swmm_HEADTOL = 38, + /*! \brief System flow tolerance */ swmm_SYSFLOWTOL = 39, + /*! \brief Lateral flow tolerance */ swmm_LATFLOWTOL = 40, } swmm_SystemProperty; - - +/*! +* \enum swmm_FlowUnitsProperty +* \brief Enumeration of flow units used in SWMM5 +*/ typedef enum { - swmm_CFS = 0, // cubic feet per second - swmm_GPM = 1, // gallons per minute - swmm_MGD = 2, // million gallons per day - swmm_CMS = 3, // cubic meters per second - swmm_LPS = 4, // liters per second - swmm_MLD = 5 // million liters per day + /*! \brief Cubic feet per second */ + swmm_CFS = 0, + /*! \brief Gallons per minute */ + swmm_GPM = 1, + /*! \brief Million gallons per day */ + swmm_MGD = 2, + /*! \brief Cubic meters per second */ + swmm_CMS = 3, + /*! \brief Liters per second */ + swmm_LPS = 4, + /*! \brief Million liters per day */ + swmm_MLD = 5 } swmm_FlowUnitsProperty; - +/*! +* \enum swmm_API_Errors +* \brief Enumeration of API errors used in SWMM5 +*/ typedef enum { - // ... API Errors + /*! \brief API error for file not opened */ ERR_API_NOT_OPEN = -999901, + /*! \brief API error for API not started */ ERR_API_NOT_STARTED = -999902, + /*! \brief API error for API not ended */ ERR_API_NOT_ENDED = -999903, + /*! \brief API error for errorneous object type */ ERR_API_OBJECT_TYPE = -999904, + /*! \brief API error for errorneous object index */ ERR_API_OBJECT_INDEX = -999905, + /*! \brief API error for errorneous object name */ ERR_API_OBJECT_NAME = -999906, + /*! \brief API error for errorneous property type */ ERR_API_PROPERTY_TYPE = -999907, + /*! \brief API error for errorneous property value */ ERR_API_PROPERTY_VALUE = -999908, + /*! \brief API error for errorneous time period */ ERR_API_TIME_PERIOD = -999909, + /*! \brief API error for errorneous hotstart file open */ ERR_API_HOTSTART_FILE_OPEN = -999910, + /*! \brief API error for errorneous hotstart file format */ ERR_API_HOTSTART_FILE_FORMAT= -999911, + /*! \brief API error for API already running */ ERR_API_IS_RUNNING = -999912, } swmm_API_Errors; +/*! +* \typedef progress_callback +* \brief Callback function for progress reporting +* \param[in] progress Progress value between 0 and 1 +*/ typedef void (*progress_callback)(double progress); -int DLLEXPORT swmm_run(const char *inputFile, const char *reportFile, const char *outputFile); -int DLLEXPORT swmm_run_with_callback(const char *inputFile, const char *reportFile, const char *outputFile, progress_callback callback); -int DLLEXPORT swmm_open(const char *inputFile, const char *reportFile, const char *outputFile); -int DLLEXPORT swmm_start(int saveFlag); -int DLLEXPORT swmm_step(double *elapsedTime); -int DLLEXPORT swmm_stride(int strideStep, double *elapsedTime); -int DLLEXPORT swmm_useHotStart(const char* hotStartFile); -int DLLEXPORT swmm_saveHotStart(const char* hotStartFile); -int DLLEXPORT swmm_end(void); -int DLLEXPORT swmm_report(void); -int DLLEXPORT swmm_close(void); - -int DLLEXPORT swmm_getMassBalErr(float *runoffErr, float *flowErr, float *qualErr); -int DLLEXPORT swmm_getVersion(void); -int DLLEXPORT swmm_getError(char *errMsg, int msgLen); -int DLLEXPORT swmm_getErrorFromCode(int error_code, char *outErrMsg[1024]); -int DLLEXPORT swmm_getWarnings(void); - -int DLLEXPORT swmm_getCount(int objType); -int DLLEXPORT swmm_getName(int objType, int index, char *name, int size); -int DLLEXPORT swmm_getIndex(int objType, const char *name); +/*! +* \brief Run a SWMM simulation with the given input file, report file, and output file. +* \param[in] inputFile Path to the input file +* \param[in] reportFile Path to the report file +* \param[in] outputFile Path to the output file +* \return Error code +*/ +int DLLEXPORT swmm_run(const char *inputFile, const char *reportFile, const char *outputFile); + +/*! +* \brief Run a SWMM simulation with the given input file, report file, and output file with a progress callback. +* \param[in] inputFile Path to the input file +* \param[in] reportFile Path to the report file +* \param[in] outputFile Path to the output file +* \param[in] callback Progress callback function +* \return Error code +*/ +int DLLEXPORT swmm_run_with_callback(const char *inputFile, const char *reportFile, const char *outputFile, progress_callback callback); + +/*! +* \brief Open a SWMM simulation with the given input file, report file, and output file. +* \param[in] inputFile Path to the input file +* \param[in] reportFile Path to the report file +* \param[in] outputFile Path to the output file +* \return Error code +*/ +int DLLEXPORT swmm_open(const char *inputFile, const char *reportFile, const char *outputFile); + +/*! +* \brief Start a SWMM simulation with the given save flag. +* \param[in] saveFlag Flag to save simulation +* \return Error code +*/ +int DLLEXPORT swmm_start(int saveFlag); + +/*! +* \brief Perform a SWMM simulation step and return the elapsed time. +* \param[out] elapsedTime Elapsed time +* \return Error code +*/ +int DLLEXPORT swmm_step(double *elapsedTime); + +/*! +* \brief Perform a SWMM simulation step with a stride step and return the elapsed time. +* \param[in] strideStep Stride step +* \param[out] elapsedTime Elapsed time +* \return Error code +*/ +int DLLEXPORT swmm_stride(int strideStep, double *elapsedTime); + +/*! +* \brief Set hotstart file for SWMM simulation. +* \details Sets the hotstart file to use for simulation. Errors does not terminate simulation unless +* there is a prior terminating error. +* \param[in] hotStartFile Path to the hotstart file +* \return Error code +*/ +int DLLEXPORT swmm_useHotStart(const char* hotStartFile); + +/*! +* \brief Save hotstart file for SWMM simulation at current time. +* \param[in] hotStartFile Path to the hotstart file +* \return Error code +*/ +int DLLEXPORT swmm_saveHotStart(const char* hotStartFile); + +/*! +* \brief End a SWMM simulation. +* \return Error code +*/ +int DLLEXPORT swmm_end(void); + +/*! +* \brief Writes simulation results to the report file. +* \return Error code +*/ +int DLLEXPORT swmm_report(void); + +/*! +* \brief Close a SWMM simulation. +* \return Error code +*/ +int DLLEXPORT swmm_close(void); + +/*! +* \brief Get the mass balance errors for a SWMM simulation. +* \param[out] runoffErr Runoff error (percent) +* \param[out] flowErr Flow error (percent) +* \param[out] qualErr Quality error (percent) +* \return Error code +*/ +int DLLEXPORT swmm_getMassBalErr(float *runoffErr, float *flowErr, float *qualErr); + +/*! +* \brief Get the version of the SWMM engine. +* \return Version number +*/ +int DLLEXPORT swmm_getVersion(void); + +/*! +* \brief Retrieves the code number and text of the error condition that +* caused SWMM to abort its analysis. +* \param[out] errMsg Error message text +* \param[in] msgLen Maximum size of errMsg +* \return Error message code number +*/ +int DLLEXPORT swmm_getError(char *errMsg, int msgLen); + +/*! +* \brief Retrieves the text of the error message that corresponds to the error code number. +* \param[in] errorCode Error code number +* \param[out] outErrMsg Error message text +* \return Error code +*/ +int DLLEXPORT swmm_getErrorFromCode(int error_code, char *outErrMsg[1024]); + +/*! +* \brief Gets the number of warnings issued during a simulation. +* \return Number of warning messages issued +*/ +int DLLEXPORT swmm_getWarnings(void); + +/*! +* \brief Retrieves the number of objects of a specific type. +* \param[in] objType Type of SWMM object +* \return Number of objects or error code +*/ +int DLLEXPORT swmm_getCount(int objType); + +/*! +* \brief Retrieves the ID name of an object. +* \param[in] objType Type of SWMM object +* \param[in] index Object index +* \param[out] name Object name +* \param[in] size Size of the name array +* \return Error code +*/ +int DLLEXPORT swmm_getName(int objType, int index, char *name, int size); + +/*! +* \brief Retrieves the index of a named object. +* \param[in] objType Type of SWMM object +* \param[in] name Object name +* \return Object index or error code +*/ +int DLLEXPORT swmm_getIndex(int objType, const char *name); + +/*! +* \brief Get the value of a property for an object of a given property in the SWMM model. +* \param[in] property Property type +* \param[in] index Object index +* \return Property value +* \deprecated Use swmm_getValueExpanded instead. Function will be changed to swmm_getValueExpanded in future versions. +*/ double DLLEXPORT swmm_getValue(int property, int index); + +/*! +* \brief Get the value of a property for an object given property, index, and subindex in the SWMM model. +* \param[in] objType Object type +* \param[in] property Property type +* \param[in] index Object index +* \param[in] subIndex Optional Subindex for the property +* \return Property value +*/ double DLLEXPORT swmm_getValueExpanded(int objType, int property, int index, int subIndex); -int DLLEXPORT swmm_setValue(int property, int index, double value); -int DLLEXPORT swmm_setValueExpanded(int objType, int property, int index, int subIndex, double value); + +/*! +* \brief Set the value of a property for an object of a given property and index in the SWMM model. +* \param[in] property Property type +* \param[in] index Object index +* \param[in] value Property value +* \return Error code +* \deprecated Use swmm_setValueExpanded instead. Function will be changed to swmm_setValueExpanded in future versions. +*/ +int DLLEXPORT swmm_setValue(int property, int index, double value); + +/*! +* \brief Set the value of a property for an object given property, index, and subindex in the SWMM model. +* \param[in] objType Object type +* \param[in] property Property type +* \param[in] index Object index +* \param[in] subIndex Optional Subindex for the property +* \param[in] value Property value +* \return Error code +*/ +int DLLEXPORT swmm_setValueExpanded(int objType, int property, int index, int subIndex, double value); + +/*! +* \brief Get saved value of +* \param[in] property Property type +* \param[in] index Object index +* \return Property value +*/ double DLLEXPORT swmm_getSavedValue(int property, int index, int period); -void DLLEXPORT swmm_writeLine(const char *line); -void DLLEXPORT swmm_decodeDate(double date, int *year, int *month, int *day, + +/*! +* \brief Write a line of text to the SWMM report file. +* \param[in] line Line of text +*/ +void DLLEXPORT swmm_writeLine(const char *line); + +/*! +* \brief Decode double date value into year, month, day, hour, minute, second, and day of week. +* \param[in] date Date value +* \param[out] year Year +* \param[out] month Month +* \param[out] day Day +* \param[out] hour Hour +* \param[out] minute Minute +* \param[out] second Second +* \param[out] dayOfWeek Day of week (0=Sunday, 1=Monday, ..., 6=Saturday) +*/ +void DLLEXPORT swmm_decodeDate(double date, int *year, int *month, int *day, int *hour, int *minute, int *second, int *dayOfWeek); + +/*! +* \brief Encode date values into a double date value. +* \param[in] year Year +* \param[in] month Month +* \param[in] day Day +* \param[in] hour Hour +* \param[in] minute Minute +* \param[in] second Second +* \return Date value +*/ double DLLEXPORT swmm_encodeDate(int year, int month, int day, int hour, int minute, int second); diff --git a/src/solver/inflow.c b/src/solver/inflow.c index 7cb5e250f..741e3da0f 100644 --- a/src/solver/inflow.c +++ b/src/solver/inflow.c @@ -38,17 +38,16 @@ double getPatternFactor(int p, int month, int day, int hour); +/*! +* \brief Reads parameters of a direct external inflow from a line of input. +* \param[in] tok Array of string tokens +* \param[in] ntoks Number of tokens +* \return Error code +* \details Data formats of data line are: +* nodeID FLOW tSeriesID (FLOW 1.0 scaleFactor baseline basePat) +* nodeID pollutID tSeriesID (CONCEN/MASS unitsFactor scaleFactor baseline basePat) +*/ int inflow_readExtInflow(char* tok[], int ntoks) -// -// Input: tok[] = array of string tokens -// ntoks = number of tokens -// Output: returns an error message -// Purpose: reads parameters of a direct external inflow from a line of input. -// -// Formats of data line are: -// nodeID FLOW tSeriesID (FLOW 1.0 scaleFactor baseline basePat) -// nodeID pollutID tSeriesID (CONCEN/MASS unitsFactor scaleFactor baseline basePat) -// { int j; // object index int param; // FLOW (-1) or pollutant index @@ -133,26 +132,25 @@ int inflow_readExtInflow(char* tok[], int ntoks) cf, baseline, sf)); } -//============================================================================= - -int inflow_setExtInflow(int j, int param, int type, int tseries, int basePat, +/*! +* \brief This function assigns property values to the inflow object. +* \param[in] nodeIndex Node index +* \param[in] param Parameter code +* \param[in] type Inflow type code +* \param[in] tSeries Time series index +* \param[in] basePat Base pattern index +* \param[in] cf Conversion factor +* \param[in] baseline Baseline value +* \param[in] sf Scale factor +* \return Error code +*/ +int inflow_setExtInflow(int nodeIndex, int param, int type, int tseries, int basePat, double cf, double baseline, double sf) -// Purpose: This function assigns property values to the inflow object -// Inputs: j = Node index -// param = FLOW (-1) or pollutant index -// type = FLOW, CONCEN or MASS inflow -// tSeries = time series index -// basePat = baseline pattern -// cf = units conversion factor -// baseline = baseline inflow value -// sf = scaling factor -// Return: returns Error Code - { TExtInflow* inflow; // external inflow object // --- check if an external inflow object for this constituent already exists - inflow = Node[j].extInflow; + inflow = Node[nodeIndex].extInflow; while ( inflow ) { if ( inflow->param == param ) break; @@ -167,8 +165,8 @@ int inflow_setExtInflow(int j, int param, int type, int tseries, int basePat, { return error_setInpError(ERR_MEMORY, ""); } - inflow->next = Node[j].extInflow; - Node[j].extInflow = inflow; + inflow->next = Node[nodeIndex].extInflow; + Node[nodeIndex].extInflow = inflow; } // --- assign property values to the inflow object @@ -182,18 +180,15 @@ int inflow_setExtInflow(int j, int param, int type, int tseries, int basePat, return 0; } -//============================================================================= - -void inflow_deleteExtInflows(int j) -// -// Input: j = node index -// Output: none -// Purpose: deletes all time series inflow data for a node. -// +/*! +* \brief Deletes all time series inflow data for a node. +* \param[in] nodeIndex Node index +*/ +void inflow_deleteExtInflows(int nodeIndex) { TExtInflow* inflow1; TExtInflow* inflow2; - inflow1 = Node[j].extInflow; + inflow1 = Node[nodeIndex].extInflow; while ( inflow1 ) { inflow2 = inflow1->next; @@ -202,16 +197,14 @@ void inflow_deleteExtInflows(int j) } } -//============================================================================= - +/*! +* \brief Retrieves the value of an external inflow at a specific +* date and time. +* \param[in] inflow External inflow data structure +* \param[in] aDate Current simulation date/time +* \return Returns current value of external inflow parameter +*/ double inflow_getExtInflow(TExtInflow* inflow, DateTime aDate) -// -// Input: inflow = external inflow data structure -// aDate = current simulation date/time -// Output: returns current value of external inflow parameter -// Purpose: retrieves the value of an external inflow at a specific -// date and time. -// { int month, day, hour; int p = inflow->basePat; // baseline pattern @@ -232,18 +225,15 @@ double inflow_getExtInflow(TExtInflow* inflow, DateTime aDate) return cf * (tsv + blv); } -//============================================================================= - +/*! +* \brief Reads dry weather inflow parameters from line of input data. +* \param[in] tok Array of string tokens +* \param[in] ntoks Number of tokens +* \return Error code +* \details Format of data line is: +* nodeID FLOW/pollutID avgValue (pattern1 pattern2 ... pattern4) +*/ int inflow_readDwfInflow(char* tok[], int ntoks) -// -// Input: tok[] = array of string tokens -// ntoks = number of tokens -// Output: returns an error message -// Purpose: reads dry weather inflow parameters from line of input data. -// -// Format of data line is: -// nodeID FLOW/pollutID avgValue (pattern1 pattern2 ... pattern4) -// { int i; int j; // node index @@ -308,7 +298,7 @@ int inflow_readDwfInflow(char* tok[], int ntoks) //============================================================================= -void inflow_deleteDwfInflows(int j) +void inflow_deleteDwfInflows(int nodeIndex) // // Input: j = node index // Output: none @@ -317,7 +307,7 @@ void inflow_deleteDwfInflows(int j) { TDwfInflow* inflow1; TDwfInflow* inflow2; - inflow1 = Node[j].dwfInflow; + inflow1 = Node[nodeIndex].dwfInflow; while ( inflow1 ) { inflow2 = inflow1->next; @@ -326,18 +316,14 @@ void inflow_deleteDwfInflows(int j) } } -//============================================================================= - -void inflow_initDwfInflow(TDwfInflow* inflow) -// -// Input: inflow = dry weather inflow data structure -// Output: none -// Purpose: initialzes a dry weather inflow by ordering its time patterns. -// -// This function sorts the user-supplied time patterns for a dry weather -// inflow in the order of the PatternType enumeration (monthly, daily, -// weekday hourly, weekend hourly) to help speed up pattern processing. -// +/*! +* \brief Initialzes a dry weather inflow by ordering its time patterns. +* \param[in] inflow Dry weather inflow object +* \details This function sorts the user-supplied time patterns for a dry weather +* inflow in the order of the PatternType enumeration (monthly, daily, +* weekday hourly, weekend hourly) to help speed up pattern processing. +*/ +void inflow_initDwfInflow(TDwfInflow* inflow) { int i, p; int tmpPattern[4]; // index of each type of DWF pattern @@ -389,34 +375,29 @@ double inflow_getDwfInflow(TDwfInflow* inflow, int month, int day, int hour) } -//============================================================================= - -void inflow_initDwfPattern(int j) -// -// Input: j = time pattern index -// Output: none -// Purpose: initialzes a dry weather inflow time pattern. -// +/*! +* \brief Initialzes a dry weather inflow time pattern. +* \param[in] patternIndex Time pattern index +*/ +void inflow_initDwfPattern(int patternIndex) { int i; - for (i=0; i<24; i++) Pattern[j].factor[i] = 1.0; - Pattern[j].count = 0; - Pattern[j].type = -1; - Pattern[j].ID = NULL; + for (i=0; i<24; i++) Pattern[patternIndex].factor[i] = 1.0; + Pattern[patternIndex].count = 0; + Pattern[patternIndex].type = -1; + Pattern[patternIndex].ID = NULL; } -//============================================================================= - +/*! +* \brief Reads values of a time pattern from a line of input data. +* \param[in] tok Array of string tokens +* \param[in] ntoks Number of tokens +* \return Error code +* \details Format of data line is: +* patternID patternType value(1) value(2) ... +* patternID value(n) value(n+1) .... (for continuation lines) +*/ int inflow_readDwfPattern(char* tok[], int ntoks) -// -// Input: tok[] = array of string tokens -// ntoks = number of tokens -// Output: returns an error message -// Purpose: reads values of a time pattern from a line of input data. -// -// Format of data line is: -// patternID patternType value(1) value(2) ... -// patternID value(n) value(n+1) .... (for continuation lines) { int i, j, k, n = 1; diff --git a/src/solver/kinwave.c b/src/solver/kinwave.c index cafa2e346..abb00e074 100644 --- a/src/solver/kinwave.c +++ b/src/solver/kinwave.c @@ -52,26 +52,24 @@ static TXsect* pXsect; static int solveContinuity(double qin, double ain, double* aout); static void evalContinuity(double a, double* f, double* df, void* p); -//============================================================================= - -int kinwave_execute(int j, double* qinflow, double* qoutflow, double tStep) -// -// Input: j = link index -// qinflow = inflow at current time (cfs) -// tStep = time step (sec) -// Output: qoutflow = outflow at current time (cfs), -// returns number of iterations used -// Purpose: finds outflow over time step tStep given flow entering a -// conduit using Kinematic Wave flow routing. -// -// -// ^ q3 -// t | -// | qin, ain |-------------------| qout, aout -// | | Flow ---> | -// |----> x q1, a1 |-------------------| q2, a2 -// -// +/*! +* \brief Finds outflow over time step tStep given flow entering a +* conduit using Kinematic Wave flow routing. +* \param[in] linkIndex Link index +* \param[in] qinflow Inflow at current time (cfs) +* \param[out] qoutflow Outflow at current time (cfs) +* \param[in] tStep Time step (sec) +* \return Returns number of iterations used +* \details +* Orientation of link and variables: +* q1, a1, q2, a2, q3, a3, x, t +* ^ q3 +* t | +* | qin, ain |-------------------| qout, aout +* | | Flow ---> | +* |----> x q1, a1 |-------------------| q2, a2 +*/ +int kinwave_execute(int linkIndex, double* qinflow, double* qoutflow, double tStep) { int k; int result = 1; @@ -82,16 +80,16 @@ int kinwave_execute(int j, double* qinflow, double* qoutflow, double tStep) // --- no routing for non-conduit link (*qoutflow) = (*qinflow); - if ( Link[j].type != CONDUIT ) return result; + if ( Link[linkIndex].type != CONDUIT ) return result; // --- no routing for dummy xsection - if ( Link[j].xsect.type == DUMMY ) return result; + if ( Link[linkIndex].xsect.type == DUMMY ) return result; // --- assign module-level variables - pXsect = &Link[j].xsect; - Qfull = Link[j].qFull; - Afull = Link[j].xsect.aFull; - k = Link[j].subIndex; + pXsect = &Link[linkIndex].xsect; + Qfull = Link[linkIndex].qFull; + Afull = Link[linkIndex].xsect.aFull; + k = Link[linkIndex].subIndex; Beta1 = Conduit[k].beta / Qfull; // --- normalize previous flows @@ -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, KW, qin*Qfull, tStep) / Qfull; + q3 = link_getLossRate(linkIndex, KW, qin*Qfull, tStep) / Qfull; // --- normalize previous areas a1 = Conduit[k].a1 / Afull; @@ -125,7 +123,7 @@ int kinwave_execute(int j, double* qinflow, double* qoutflow, double tStep) else { // --- compute constant factors - dxdt = link_getLength(j) / tStep * Afull / Qfull; + dxdt = link_getLength(linkIndex) / tStep * Afull / Qfull; dq = q2 - q1; C1 = dxdt * WT / WX; C2 = (1.0 - WT) * (ain - a1); @@ -143,7 +141,7 @@ int kinwave_execute(int j, double* qinflow, double* qoutflow, double tStep) // --- report error if continuity eqn. not solved if ( result == -1 ) { - report_writeErrorMsg(ERR_KINWAVE, Link[j].ID); + report_writeErrorMsg(ERR_KINWAVE, Link[linkIndex].ID); return 1; } if ( result <= 0 ) result = 1; diff --git a/src/solver/landuse.c b/src/solver/landuse.c index 4c8402790..acb2e90bc 100644 --- a/src/solver/landuse.c +++ b/src/solver/landuse.c @@ -46,60 +46,54 @@ static double landuse_getWashoffQual(int landuse, int pollut, double buildup, static double landuse_getExternalBuildup(int i, int p, double buildup, double tStep); -//============================================================================= - -int landuse_readParams(int j, char* tok[], int ntoks) -// -// Input: j = land use index -// tok[] = array of string tokens -// ntoks = number of tokens -// Output: returns an error code -// Purpose: reads landuse parameters from a tokenized line of input. -// -// Data format is: -// landuseID (sweepInterval sweepRemoval sweepDays0) -// +/*! +* \brief Reads landuse parameters from a tokenized line of input. +* \param[in] landuseIndex Land use index +* \param[in] tok Array of string tokens +* \param[in] ntoks Number of tokens +* \return Error code +* \details Data format is: +* landuseID (sweepInterval sweepRemoval sweepDays0) +*/ +int landuse_readParams(int landuseIndex, char* tok[], int ntoks) { char *id; if ( ntoks < 1 ) return error_setInpError(ERR_ITEMS, ""); id = project_findID(LANDUSE, tok[0]); if ( id == NULL ) return error_setInpError(ERR_NAME, tok[0]); - Landuse[j].ID = id; + Landuse[landuseIndex].ID = id; if ( ntoks > 1 ) { if ( ntoks < 4 ) return error_setInpError(ERR_ITEMS, ""); - if ( ! getDouble(tok[1], &Landuse[j].sweepInterval) ) + if ( ! getDouble(tok[1], &Landuse[landuseIndex].sweepInterval) ) return error_setInpError(ERR_NUMBER, tok[1]); - if ( ! getDouble(tok[2], &Landuse[j].sweepRemoval) ) + if ( ! getDouble(tok[2], &Landuse[landuseIndex].sweepRemoval) ) return error_setInpError(ERR_NUMBER, tok[2]); - if ( ! getDouble(tok[3], &Landuse[j].sweepDays0) ) + if ( ! getDouble(tok[3], &Landuse[landuseIndex].sweepDays0) ) return error_setInpError(ERR_NUMBER, tok[3]); } else { - Landuse[j].sweepInterval = 0.0; - Landuse[j].sweepRemoval = 0.0; - Landuse[j].sweepDays0 = 0.0; + Landuse[landuseIndex].sweepInterval = 0.0; + Landuse[landuseIndex].sweepRemoval = 0.0; + Landuse[landuseIndex].sweepDays0 = 0.0; } - if ( Landuse[j].sweepRemoval < 0.0 - || Landuse[j].sweepRemoval > 1.0 ) + if ( Landuse[landuseIndex].sweepRemoval < 0.0 + || Landuse[landuseIndex].sweepRemoval > 1.0 ) return error_setInpError(ERR_NUMBER, tok[2]); return 0; } -//============================================================================= - -int landuse_readPollutParams(int j, char* tok[], int ntoks) -// -// Input: j = pollutant index -// tok[] = array of string tokens -// ntoks = number of tokens -// Output: returns an error code -// Purpose: reads pollutant parameters from a tokenized line of input. -// -// Data format is: -// ID Units cRain cGW cRDII kDecay (snowOnly coPollut coFrac cDWF cInit) -// +/*! +* \brief Reads pollutant parameters from a tokenized line of input. +* \param[in] pollutIndex Pollutant index +* \param[in] tok Array of string tokens +* \param[in] ntoks Number of tokens +* \return Error code +* \details Data format is: +* ID Units cRain cGW cRDII kDecay (snowOnly coPollut coFrac cDWF cInit) +*/ +int landuse_readPollutParams(int pollutIndex, char* tok[], int ntoks) { int i, k, coPollut, snowFlag; double x[4], coFrac, cDWF, cInit; @@ -168,20 +162,20 @@ int landuse_readPollutParams(int j, char* tok[], int ntoks) } // --- save values for pollutant object - Pollut[j].ID = id; - Pollut[j].units = k; - if ( Pollut[j].units == MG ) Pollut[j].mcf = UCF(MASS); - else if ( Pollut[j].units == UG ) Pollut[j].mcf = UCF(MASS) / 1000.0; - else Pollut[j].mcf = 1.0; - Pollut[j].pptConcen = x[0]; - Pollut[j].gwConcen = x[1]; - Pollut[j].rdiiConcen = x[2]; - Pollut[j].kDecay = x[3]/SECperDAY; - Pollut[j].snowOnly = snowFlag; - Pollut[j].coPollut = coPollut; - Pollut[j].coFraction = coFrac; - Pollut[j].dwfConcen = cDWF; - Pollut[j].initConcen = cInit; + Pollut[pollutIndex].ID = id; + Pollut[pollutIndex].units = k; + if ( Pollut[pollutIndex].units == MG ) Pollut[pollutIndex].mcf = UCF(MASS); + else if ( Pollut[pollutIndex].units == UG ) Pollut[pollutIndex].mcf = UCF(MASS) / 1000.0; + else Pollut[pollutIndex].mcf = 1.0; + Pollut[pollutIndex].pptConcen = x[0]; + Pollut[pollutIndex].gwConcen = x[1]; + Pollut[pollutIndex].rdiiConcen = x[2]; + Pollut[pollutIndex].kDecay = x[3]/SECperDAY; + Pollut[pollutIndex].snowOnly = snowFlag; + Pollut[pollutIndex].coPollut = coPollut; + Pollut[pollutIndex].coFraction = coFrac; + Pollut[pollutIndex].dwfConcen = cDWF; + Pollut[pollutIndex].initConcen = cInit; return 0; } @@ -276,17 +270,15 @@ int landuse_readBuildupParams(char* tok[], int ntoks) return 0; } -//============================================================================= - +/*! +* \brief Reads pollutant washoff parameters from a tokenized line of input. +* \param[in] tok Array of string tokens +* \param[in] ntoks Number of tokens +* \return Error code +* \details Data format is: +* landuseID pollutID washoffType c1 c2 sweepEffic bmpRemoval +*/ int landuse_readWashoffParams(char* tok[], int ntoks) -// -// Input: tok[] = array of string tokens -// ntoks = number of tokens -// Output: returns an error code -// Purpose: reads pollutant washoff parameters from a tokenized line of input. -// -// Data format is: -// landuseID pollutID washoffType c1 c2 sweepEffic bmpRemoval { int i, j, p; int func; @@ -408,7 +400,7 @@ void landuse_getInitBuildup(TLandFactor* landFactor, double* initBuildup, //============================================================================= -double landuse_getBuildup(int i, int p, double area, double curb, double buildup, +double landuse_getBuildup(int landuseIndex, int pollutIndex, double area, double curb, double buildup, double tStep) // // Input: i = land use index @@ -426,31 +418,31 @@ double landuse_getBuildup(int i, int p, double area, double curb, double buildu double perUnit; // normalizer value (area or curb length) // --- return current buildup if no buildup function or time increment - if ( Landuse[i].buildupFunc[p].funcType == NO_BUILDUP || tStep == 0.0 ) + if ( Landuse[landuseIndex].buildupFunc[pollutIndex].funcType == NO_BUILDUP || tStep == 0.0 ) { return buildup; } // --- see what buildup is normalized to - n = Landuse[i].buildupFunc[p].normalizer; + n = Landuse[landuseIndex].buildupFunc[pollutIndex].normalizer; perUnit = 1.0; if ( n == PER_AREA ) perUnit = area; if ( n == PER_CURB ) perUnit = curb; if ( perUnit == 0.0 ) return 0.0; // --- buildup determined by loading time series - if ( Landuse[i].buildupFunc[p].funcType == EXTERNAL_BUILDUP ) + if ( Landuse[landuseIndex].buildupFunc[pollutIndex].funcType == EXTERNAL_BUILDUP ) { - return landuse_getExternalBuildup(i, p, buildup/perUnit, tStep) * + return landuse_getExternalBuildup(landuseIndex, pollutIndex, buildup/perUnit, tStep) * perUnit; } // --- determine equivalent days of current buildup - days = landuse_getBuildupDays(i, p, buildup/perUnit); + days = landuse_getBuildupDays(landuseIndex, pollutIndex, buildup/perUnit); // --- compute buildup after adding on time increment days += tStep / SECperDAY; - return landuse_getBuildupMass(i, p, days) * perUnit; + return landuse_getBuildupMass(landuseIndex, pollutIndex, days) * perUnit; } //============================================================================= @@ -527,9 +519,14 @@ double landuse_getBuildupMass(int i, int p, double days) return b; } -//============================================================================= - -double landuse_getAvgBmpEffic(int j, int p) +/*! +* \brief Finds the overall average BMP removal achieved for pollutant pollutIndex +* treated in subcatchment j. +* \param[in] subcatchIndex Subcatchment index +* \param[in] pollutIndex Pollutant index +* \return Returns a BMP removal fraction for a pollutant pollutIndex +*/ +double landuse_getAvgBmpEffic(int subcatchIndex, int pollutIndex) // // Input: j = subcatchment index // p = pollutant index @@ -542,8 +539,8 @@ double landuse_getAvgBmpEffic(int j, int p) double r = 0.0; for (i = 0; i < Nobjects[LANDUSE]; i++) { - r += Subcatch[j].landFactor[i].fraction * - Landuse[i].washoffFunc[p].bmpEffic; + r += Subcatch[subcatchIndex].landFactor[i].fraction * + Landuse[i].washoffFunc[pollutIndex].bmpEffic; } return r; } @@ -660,8 +657,12 @@ double landuse_getWashoffQual(int i, int p, double buildup, double runoff, return cWashoff; } -//============================================================================= - +/*! +* \brief Finds washoff mass added by a co-pollutant of a given pollutant. +* \param[in] p Pollutant index +* \param[in] washoff Array of washoff mass for each pollutant +* \return Returns washoff mass added by co-pollutant relation (mass) +*/ double landuse_getCoPollutLoad(int p, double washoff[]) // // Input: p = pollutant index diff --git a/src/solver/massbal.c b/src/solver/massbal.c index 16a103a92..80bb10e50 100644 --- a/src/solver/massbal.c +++ b/src/solver/massbal.c @@ -334,15 +334,12 @@ double massbal_getBuildup(int p) return load; } -//============================================================================= - +/*! +* \brief Updates runoff totals after current time step. +* \param[in] flowType Type of flow (RUNOFF_RAINFALL, RUNOFF_EVAP, RUNOFF_RUNOFF, etc.) +* \param[in] v Volume of flow (ft3) +*/ void massbal_updateRunoffTotals(int flowType, double v) -// -// Input: flowType = type of flow -// v = flow volume (ft3) -// Output: none -// Purpose: updates runoff totals after current time step. -// { switch(flowType) { @@ -355,19 +352,16 @@ void massbal_updateRunoffTotals(int flowType, double v) } } -//============================================================================= - +/*! +* \brief Updates groundwater totals after current time step. +* \param[in] vInfil Volume depth of infiltration (ft) +* \param[in] vUpperEvap Volume depth of upper zone evaporation (ft) +* \param[in] vLowerEvap Volume depth of lower zone evaporation (ft) +* \param[in] vLowerPerc Volume depth of percolation to deep GW (ft) +* \param[in] vGwater Volume depth of groundwater outflow (ft) +*/ void massbal_updateGwaterTotals(double vInfil, double vUpperEvap, double vLowerEvap, double vLowerPerc, double vGwater) -// -// Input: vInfil = volume depth of infiltrated water (ft) -// vUpperEvap = volume depth of upper evaporation (ft) -// vLowerEvap = volume depth of lower evaporation (ft) -// vLowerPerc = volume depth of percolation to deep GW (ft) -// vGwater = volume depth of groundwater outflow (ft) -// Output: none -// Purpose: updates groundwater totals after current time step. -// { GwaterTotals.infil += vInfil; GwaterTotals.upperEvap += vUpperEvap; @@ -376,14 +370,10 @@ void massbal_updateGwaterTotals(double vInfil, double vUpperEvap, double vLowerE GwaterTotals.gwater += vGwater; } -//============================================================================= - +/*! +* \brief Initializes routing totals for current time step. +*/ void massbal_initTimeStepTotals() -// -// Input: none -// Output: none -// Purpose: initializes routing totals for current time step. -// { int j; OldStepFlowTotals = StepFlowTotals; @@ -413,15 +403,12 @@ void massbal_initTimeStepTotals() } } -//============================================================================= - +/*! +* \brief Adds flow inflow to routing totals for current time step. +* \param[in] type Type of inflow +* \param[in] q Inflow rate (cfs) +*/ void massbal_addInflowFlow(int type, double q) -// -// Input: type = type of inflow -// q = inflow rate (cfs) -// Output: none -// Purpose: adds flow inflow to routing totals for current time step. -// { switch (type) { @@ -433,16 +420,13 @@ void massbal_addInflowFlow(int type, double q) } } -//============================================================================= - +/*! +* \brief Adds inflow mass loading to loading totals for current time step. +* \param[in] type Type of inflow +* \param[in] pollutIndex Pollutant index +* \param[in] w Mass loading +*/ void massbal_updateLoadingTotals(int type, int p, double w) -// -// Input: type = type of inflow -// p = pollutant index -// w = mass loading -// Output: none -// Purpose: adds inflow mass loading to loading totals for current time step. -// { switch (type) { @@ -456,52 +440,43 @@ void massbal_updateLoadingTotals(int type, int p, double w) } } -//============================================================================= - -void massbal_addInflowQual(int type, int p, double w) -// -// Input: type = type of inflow -// p = pollutant index -// w = mass flow rate (mass/sec) -// Output: none -// Purpose: adds quality inflow to routing totals for current time step. -// +/*! +* \brief Adds quality inflow to routing totals for current time step. +* \param[in] type Type of inflow +* \param[in] pollutIndex Pollutant index +* \param[in] w Mass flow rate (mass/sec) +*/ +void massbal_addInflowQual(int type, int pollutIndex, double w) { - if ( p < 0 || p >= Nobjects[POLLUT] ) return; + if ( pollutIndex < 0 || pollutIndex >= Nobjects[POLLUT] ) return; switch (type) { - case DRY_WEATHER_INFLOW: StepQualTotals[p].dwInflow += w; break; - case WET_WEATHER_INFLOW: StepQualTotals[p].wwInflow += w; break; - case GROUNDWATER_INFLOW: StepQualTotals[p].gwInflow += w; break; - case EXTERNAL_INFLOW: StepQualTotals[p].exInflow += w; break; - case RDII_INFLOW: StepQualTotals[p].iiInflow += w; break; + case DRY_WEATHER_INFLOW: StepQualTotals[pollutIndex].dwInflow += w; break; + case WET_WEATHER_INFLOW: StepQualTotals[pollutIndex].wwInflow += w; break; + case GROUNDWATER_INFLOW: StepQualTotals[pollutIndex].gwInflow += w; break; + case EXTERNAL_INFLOW: StepQualTotals[pollutIndex].exInflow += w; break; + case RDII_INFLOW: StepQualTotals[pollutIndex].iiInflow += w; break; } } -//============================================================================= - +/*! +* \brief Adds flow outflow over current time step to routing totals. +* \param[in] q Outflow rate (cfs) +* \param[in] isFlooded TRUE if outflow represents internal flooding +*/ void massbal_addOutflowFlow(double q, int isFlooded) -// -// Input: q = outflow flow rate (cfs) -// isFlooded = TRUE if outflow represents internal flooding -// Output: none -// Purpose: adds flow outflow over current time step to routing totals. -// { if ( isFlooded ) StepFlowTotals.flooding += q; else StepFlowTotals.outflow += q; } -//============================================================================= - +/*! +* \brief Adds pollutant outflow over current time step to routing totals. +* \param[in] pollutIndex Pollutant index +* \param[in] mass Mass outflow rate (mass/sec) +* \param[in] isFlooded TRUE if outflow represents internal flooding +*/ void massbal_addOutflowQual(int p, double w, int isFlooded) -// -// Input: p = pollutant index -// w = mass outflow rate (mass/sec) -// isFlooded = TRUE if outflow represents internal flooding -// Output: none -// Purpose: adds pollutant outflow over current time step to routing totals. -// { if ( p < 0 || p >= Nobjects[POLLUT] ) return; if ( w >= 0.0 ) @@ -512,78 +487,65 @@ void massbal_addOutflowQual(int p, double w, int isFlooded) else StepQualTotals[p].exInflow -= w; } -//============================================================================= - -void massbal_addReactedMass(int p, double w) -// -// Input: p = pollutant index -// w = rate of mass reacted (mass/sec) -// Output: none -// Purpose: adds mass reacted during current time step to routing totals. -// +/*! +* \brief Adds mass reacted during current time step to routing totals. +* \param[in] pollutIndex Pollutant index +* \param[in] mass Rate of mass reacted (mass/sec) +*/ +void massbal_addReactedMass(int pollutIndex, double w) { - if ( p < 0 || p >= Nobjects[POLLUT] ) return; - StepQualTotals[p].reacted += w; + if ( pollutIndex < 0 || pollutIndex >= Nobjects[POLLUT] ) return; + StepQualTotals[pollutIndex].reacted += w; } -//============================================================================= - -void massbal_addSeepageLoss(int p, double w) -// -// Input: p = pollutant index -// w = mass seepage rate (mass/sec) -// Output: none -// Purpose: adds mass lost to seepage during current time step to routing totals. -// +/*! +* \brief Adds mass lost to seepage during current time step to routing totals. +* \param[in] pollutIndex Pollutant index +* \param[in] seepLoss Mass seepage rate (mass/sec) +*/ +void massbal_addSeepageLoss(int pollutIndex, double w) { - if ( p < 0 || p >= Nobjects[POLLUT] ) return; - StepQualTotals[p].seepLoss += w; + if ( pollutIndex < 0 || pollutIndex >= Nobjects[POLLUT] ) return; + StepQualTotals[pollutIndex].seepLoss += w; } -//============================================================================= - -void massbal_addToFinalStorage(int p, double w) -// -// Input: p = pollutant index -// w = pollutant mass -// Output: none -// Purpose: adds mass remaining on dry surface to routing totals. -// +/*! +* \brief Adds mass remaining on dry surface to routing totals. +* \param[in] pollutIndex Pollutant index +* \param[in] mass Pollutant mass +*/ +void massbal_addToFinalStorage(int pollutIndex, double w) { - if ( p < 0 || p >= Nobjects[POLLUT] ) return; - StepQualTotals[p].finalStorage += w; + if ( pollutIndex < 0 || pollutIndex >= Nobjects[POLLUT] ) return; + StepQualTotals[pollutIndex].finalStorage += w; } -//============================================================================= - +/*! +* \brief Adds node losses over current time step to routing totals. +* \param[in] evapLoss Evaporation loss from all nodes (ft3/sec) +* \param[in] infilLoss Seepage loss from all nodes (ft3/sec) +*/ void massbal_addNodeLosses(double evapLoss, double seepLoss) -// -// Input: evapLoss = evaporation loss from all nodes (ft3/sec) -// seepLoss = seepage loss from all nodes (ft3/sec) -// Output: none -// Purpose: adds node losses over current time step to routing totals. -// { StepFlowTotals.evapLoss += evapLoss; StepFlowTotals.seepLoss += seepLoss; } -//============================================================================= - +/*! +* \brief Adds link losses over current time step to routing totals. +* \param[in] evapLoss Evaporation loss from all links (ft3/sec) +* \param[in] infilLoss Infiltration loss from all links (ft3/sec) +*/ void massbal_addLinkLosses(double evapLoss, double seepLoss) -// -// Input: evapLoss = evaporation loss from all links (ft3/sec) -// infilLoss = infiltration loss from all links (ft3/sec) -// Output: none -// Purpose: adds link losses over current time step to routing totals. -// { StepFlowTotals.evapLoss += evapLoss; StepFlowTotals.seepLoss += seepLoss; } -//============================================================================= - +/*! +* \brief Updates overall routing totals with totals from current time step. +* \param[in] tStep Time step (sec) +*/ void massbal_updateRoutingTotals(double tStep) // // Input: tStep = time step (sec) diff --git a/src/solver/node.c b/src/solver/node.c index ee73820c6..90ad859c8 100644 --- a/src/solver/node.c +++ b/src/solver/node.c @@ -100,25 +100,23 @@ static void divider_validate(int j); static double divider_getOutflow(int j, int link); -//============================================================================= - -int node_readParams(int j, int type, int k, char* tok[], int ntoks) -// -// Input: j = node index -// type = node type code -// k = index of node type -// tok[] = array of string tokens -// ntoks = number of tokens -// Output: returns an error code -// Purpose: reads node properties from a tokenized line of input. -// +/*! +* \brief Reads node properties from a tokenized line of input. +* \param[in] nodeIndex Node index +* \param[in] type Node type code +* \param[in] subIndex Node sub-type code +* \param[in] tok Array of string tokens +* \param[in] ntoks Number of tokens +* \return Error code +*/ +int node_readParams(int nodeIndex, int type, int k, char* tok[], int ntoks) { switch ( type ) { - case JUNCTION: return junc_readParams(j, k, tok, ntoks); - case OUTFALL: return outfall_readParams(j, k, tok, ntoks); - case STORAGE: return storage_readParams(j, k, tok, ntoks); - case DIVIDER: return divider_readParams(j, k, tok, ntoks); + case JUNCTION: return junc_readParams(nodeIndex, k, tok, ntoks); + case OUTFALL: return outfall_readParams(nodeIndex, k, tok, ntoks); + case STORAGE: return storage_readParams(nodeIndex, k, tok, ntoks); + case DIVIDER: return divider_readParams(nodeIndex, k, tok, ntoks); default: return 0; } } @@ -198,36 +196,33 @@ void node_setParams(int j, int type, int k, double x[]) } } -//============================================================================= - -void node_validate(int j) -// -// Input: j = node index -// Output: none -// Purpose: validates a node's properties. -// +/*! +* \brief Validates a node's properties. +* \param[in] nodeIndex Node index +*/ +void node_validate(int nodeIndex) { TDwfInflow* inflow; // --- see if full depth was increased to accommodate conduit crown - if ( Node[j].fullDepth > Node[j].oldDepth && Node[j].oldDepth > 0.0 ) + if ( Node[nodeIndex].fullDepth > Node[nodeIndex].oldDepth && Node[nodeIndex].oldDepth > 0.0 ) { - report_writeWarningMsg(WARN02, Node[j].ID); + report_writeWarningMsg(WARN02, Node[nodeIndex].ID); } // --- check that initial depth does not exceed max. depth - if ( Node[j].initDepth > Node[j].fullDepth + Node[j].surDepth ) - report_writeErrorMsg(ERR_NODE_DEPTH, Node[j].ID); + if ( Node[nodeIndex].initDepth > Node[nodeIndex].fullDepth + Node[nodeIndex].surDepth ) + report_writeErrorMsg(ERR_NODE_DEPTH, Node[nodeIndex].ID); // --- check for negative volume for storage node at full depth - if (Node[j].type == STORAGE) - if (node_getVolume(j, Node[j].fullDepth) < 0.0) - report_writeErrorMsg(ERR_STORAGE_VOLUME, Node[j].ID); + if (Node[nodeIndex].type == STORAGE) + if (node_getVolume(nodeIndex, Node[nodeIndex].fullDepth) < 0.0) + report_writeErrorMsg(ERR_STORAGE_VOLUME, Node[nodeIndex].ID); - if ( Node[j].type == DIVIDER ) divider_validate(j); + if ( Node[nodeIndex].type == DIVIDER ) divider_validate(nodeIndex); // --- initialize dry weather inflows - inflow = Node[j].dwfInflow; + inflow = Node[nodeIndex].dwfInflow; while (inflow) { inflow_initDwfInflow(inflow); @@ -235,44 +230,41 @@ void node_validate(int j) } } -//============================================================================= - -void node_initState(int j) -// -// Input: j = node index -// Output: none -// Purpose: initializes a node's state variables at start of simulation. -// +/*! +* \brief Initializes a node's state variables at start of simulation. +* \param[in] nodeIndex Node index +*/ +void node_initState(int nodeIndex) { int p, k; // --- initialize depth - Node[j].oldDepth = Node[j].initDepth; - Node[j].newDepth = Node[j].oldDepth; - Node[j].crownElev = Node[j].invertElev; + Node[nodeIndex].oldDepth = Node[nodeIndex].initDepth; + Node[nodeIndex].newDepth = Node[nodeIndex].oldDepth; + Node[nodeIndex].crownElev = Node[nodeIndex].invertElev; - Node[j].fullVolume = node_getVolume(j, Node[j].fullDepth); - Node[j].oldVolume = node_getVolume(j, Node[j].oldDepth); - Node[j].newVolume = Node[j].oldVolume; + Node[nodeIndex].fullVolume = node_getVolume(nodeIndex, Node[nodeIndex].fullDepth); + Node[nodeIndex].oldVolume = node_getVolume(nodeIndex, Node[nodeIndex].oldDepth); + Node[nodeIndex].newVolume = Node[nodeIndex].oldVolume; // --- initialize water quality state for (p = 0; p < Nobjects[POLLUT]; p++) { - Node[j].oldQual[p] = 0.0; - Node[j].newQual[p] = 0.0; + Node[nodeIndex].oldQual[p] = 0.0; + Node[nodeIndex].newQual[p] = 0.0; } // --- initialize any inflow - Node[j].oldLatFlow = 0.0; - Node[j].newLatFlow = 0.0; - Node[j].apiExtInflow = 0.0; - Node[j].losses = 0.0; + Node[nodeIndex].oldLatFlow = 0.0; + Node[nodeIndex].newLatFlow = 0.0; + Node[nodeIndex].apiExtInflow = 0.0; + Node[nodeIndex].losses = 0.0; // --- initialize storage nodes - if ( Node[j].type == STORAGE ) + if ( Node[nodeIndex].type == STORAGE ) { // --- set hydraulic residence time to 0 - k = Node[j].subIndex; + k = Node[nodeIndex].subIndex; Storage[k].hrt = 0.0; // --- initialize exfiltration properties @@ -280,9 +272,9 @@ void node_initState(int j) } // --- initialize flow stream routed from outfall onto a subcatchment - if ( Node[j].type == OUTFALL ) + if ( Node[nodeIndex].type == OUTFALL ) { - k = Node[j].subIndex; + k = Node[nodeIndex].subIndex; if ( Outfall[k].routeTo >= 0 ) { Outfall[k].vRouted = 0.0; @@ -291,105 +283,90 @@ void node_initState(int j) } } -//============================================================================= - -void node_setOldHydState(int j) -// -// Input: j = node index -// Output: none -// Purpose: replaces a node's old hydraulic state values with new ones. -// +/*! +* \brief Replaces a node's old hydraulic state values with new ones. +* \param[in] nodeIndex Node index +*/ +void node_setOldHydState(int nodeIndex) { - Node[j].oldDepth = Node[j].newDepth; - Node[j].oldVolume = Node[j].newVolume; - Node[j].oldFlowInflow = Node[j].inflow; - Node[j].oldNetInflow = Node[j].inflow - Node[j].outflow; + Node[nodeIndex].oldDepth = Node[nodeIndex].newDepth; + Node[nodeIndex].oldVolume = Node[nodeIndex].newVolume; + Node[nodeIndex].oldFlowInflow = Node[nodeIndex].inflow; + Node[nodeIndex].oldNetInflow = Node[nodeIndex].inflow - Node[nodeIndex].outflow; } -//============================================================================= - -void node_setOldQualState(int j) -// -// Input: j = node index -// Output: none -// Purpose: replaces a node's old water quality state values with new ones. -// +/*! +* \brief Replaces a node's old water quality state values with new ones. +* \param[in] nodeIndex Node index +*/ +void node_setOldQualState(int nodeIndex) { int p; for (p = 0; p < Nobjects[POLLUT]; p++) { - Node[j].oldQual[p] = Node[j].newQual[p]; - Node[j].newQual[p] = 0.0; + Node[nodeIndex].oldQual[p] = Node[nodeIndex].newQual[p]; + Node[nodeIndex].newQual[p] = 0.0; } } -//============================================================================= - -void node_initFlows(int j, double tStep) -// -// Input: j = node index -// tStep = time step (sec) -// Output: none -// Purpose: initializes a node's inflow/outflow/overflow at start of time step. -// +/*! +* \brief Initializes a node's inflow/outflow/overflow at start of time step. +* \param[in] nodeIndex Node index +* \param[in] tStep Time step (sec) +*/ +void node_initFlows(int nodeIndex, double tStep) { // --- initialize inflow & outflow - Node[j].inflow = Node[j].newLatFlow; - Node[j].outflow = Node[j].losses; + Node[nodeIndex].inflow = Node[nodeIndex].newLatFlow; + Node[nodeIndex].outflow = Node[nodeIndex].losses; // --- set overflow to any excess stored volume - if ( Node[j].newVolume > Node[j].fullVolume ) - Node[j].overflow = (Node[j].newVolume - Node[j].fullVolume) / tStep; - else Node[j].overflow = 0.0; + if ( Node[nodeIndex].newVolume > Node[nodeIndex].fullVolume ) + Node[nodeIndex].overflow = (Node[nodeIndex].newVolume - Node[nodeIndex].fullVolume) / tStep; + else Node[nodeIndex].overflow = 0.0; } -//============================================================================= - -double node_getDepth(int j, double v) -// -// Input: j = node index -// v = volume (ft3) -// Output: returns depth of water at a node (ft) -// Purpose: computes a node's water depth from its volume. -// +/*! +* \brief Computes a node's water depth from its volume. +* \param[in] nodeIndex Node index +* \param[in] volume Volume of water stored at node (ft3) +* \return Returns water depth at node (ft) +*/ +double node_getDepth(int nodeIndex, double v) { - switch ( Node[j].type ) + switch ( Node[nodeIndex].type ) { - case STORAGE: return storage_getDepth(j, v); + case STORAGE: return storage_getDepth(nodeIndex, v); default: return 0.0; } } -//============================================================================= - -double node_getVolume(int j, double d) -// -// Input: j = node index -// d = water depth (ft) -// Output: returns volume of water at a node (ft3) -// Purpose: computes volume stored at a node from its water depth. -// +/*! +* \brief Computes volume stored at a node from its water depth. +* \param[in] nodeIndex Node index +* \param[in] depth Water depth at node (ft) +* \return Returns volume of water stored at node (ft3) +*/ +double node_getVolume(int nodeIndex, double depth) { - switch ( Node[j].type ) + switch ( Node[nodeIndex].type ) { - case STORAGE: return storage_getVolume(j, d); + case STORAGE: return storage_getVolume(nodeIndex, depth); default: - if ( Node[j].fullDepth > 0.0 ) - return Node[j].fullVolume * (d / Node[j].fullDepth); + if ( Node[nodeIndex].fullDepth > 0.0 ) + return Node[nodeIndex].fullVolume * (depth / Node[nodeIndex].fullDepth); else return 0.0; } } -//============================================================================= - -double node_getSurfArea(int j, double d) -// -// Input: j = node index -// d = water depth (ft) -// Output: returns surface area of water at a node (ft2) -// Purpose: computes surface area of water stored at a node from water depth. -// +/*! +* \brief Computes surface area of water stored at a node from water depth. +* \param[in] nodeIndex Node index +* \param[in] depth Water depth (ft) +* \return Returns surface area of water stored at node (ft2) +*/ +double node_getSurfArea(int j, double d) { switch (Node[j].type) { @@ -398,39 +375,35 @@ double node_getSurfArea(int j, double d) } } -//============================================================================= - -double node_getOutflow(int j, int k) -// -// Input: j = node index -// k = link index -// Output: returns flow rate (cfs) -// Purpose: computes outflow from node available for inflow into a link. -// +/*! +* \brief Computes outflow from node available for inflow into a link. +* \param[in] nodeIndex Node index +* \param[in] linkIndex Link index +* \return Returns flow rate leaving node (cfs) +*/ +double node_getOutflow(int nodeIndex, int linkIndex) { - switch ( Node[j].type ) + switch ( Node[nodeIndex].type ) { - case DIVIDER: return divider_getOutflow(j, k); - case STORAGE: return storage_getOutflow(j, k); - default: return Node[j].inflow + Node[j].overflow; + case DIVIDER: return divider_getOutflow(nodeIndex, linkIndex); + case STORAGE: return storage_getOutflow(nodeIndex, linkIndex); + default: return Node[nodeIndex].inflow + Node[nodeIndex].overflow; } } -//============================================================================= - -double node_getMaxOutflow(int j, double q, double tStep) -// -// Input: j = node index -// q = original outflow rate (cfs) -// tStep = time step (sec) -// Output: returns modified flow rate (cfs) -// Purpose: limits outflow rate from a node with storage volume. -// +/*! +* \brief Limits outflow rate from a node with storage volume. +* \param[in] nodeIndex Node index +* \param[in] q Flow rate leaving node (cfs) +* \param[in] tStep Time step (sec) +* \return Returns modified flow rate (cfs) +*/ +double node_getMaxOutflow(int nodeIndex, double q, double tStep) { double qMax; - if ( Node[j].fullVolume > 0.0 ) + if ( Node[nodeIndex].fullVolume > 0.0 ) { - qMax = Node[j].inflow + Node[j].oldVolume / tStep; + qMax = Node[nodeIndex].inflow + Node[nodeIndex].oldVolume / tStep; if ( q > qMax ) q = qMax; } return MAX(0.0, q); @@ -495,52 +468,46 @@ double node_getSystemOutflow(int j, int *isFlooded) return outflow; } -//============================================================================= - -void node_getResults(int j, double f, float x[]) -// -// Input: j = node index -// f = weighting factor -// x[] = array of nodal reporting variables -// Output: none -// Purpose: computes weighted average of old and new results at a node. -// +/*! +* \brief Computes weighted average of old and new results at a node. +* \param[in] nodeIndex Node index +* \param[in] wt Weighting factor +* \param[out] x Array of node results +*/ +void node_getResults(int nodeIndex, double wt, float x[]) { int p; double z; - double f1 = 1.0 - f; + double f1 = 1.0 - wt; - z = (f1 * Node[j].oldDepth + f * Node[j].newDepth) * UCF(LENGTH); + z = (f1 * Node[nodeIndex].oldDepth + wt * Node[nodeIndex].newDepth) * UCF(LENGTH); x[NODE_DEPTH] = (float)z; - z = Node[j].invertElev * UCF(LENGTH); + z = Node[nodeIndex].invertElev * UCF(LENGTH); x[NODE_HEAD] = x[NODE_DEPTH] + (float)z; - z = (f1*Node[j].oldVolume + f*Node[j].newVolume) * UCF(VOLUME); + z = (f1*Node[nodeIndex].oldVolume + wt * Node[nodeIndex].newVolume) * UCF(VOLUME); x[NODE_VOLUME] = (float)z; - z = (f1*Node[j].oldLatFlow + f*Node[j].newLatFlow) * UCF(FLOW); + z = (f1*Node[nodeIndex].oldLatFlow + wt * Node[nodeIndex].newLatFlow) * UCF(FLOW); x[NODE_LATFLOW] = (float)z; - z = (f1*Node[j].oldFlowInflow + f*Node[j].inflow) * UCF(FLOW); + z = (f1*Node[nodeIndex].oldFlowInflow + wt * Node[nodeIndex].inflow) * UCF(FLOW); x[NODE_INFLOW] = (float)z; - z = Node[j].overflow * UCF(FLOW); + z = Node[nodeIndex].overflow * UCF(FLOW); x[NODE_OVERFLOW] = (float)z; if ( !IgnoreQuality ) for (p = 0; p < Nobjects[POLLUT]; p++) { - z = f1*Node[j].oldQual[p] + f*Node[j].newQual[p]; + z = f1*Node[nodeIndex].oldQual[p] + wt * Node[nodeIndex].newQual[p]; x[NODE_QUAL+p] = (float)z; } } -//============================================================================= - -void node_setOutletDepth(int j, double yNorm, double yCrit, double z) -// -// Input: j = node index -// yNorm = normal flow depth (ft) -// yCrit = critical flow depth (ft) -// z = offset of connecting outfall link from node invert (ft) -// Output: none -// Purpose: sets water depth at a node that serves as an outlet point. -// +/*! +* \brief Sets water depth at a node that serves as an outlet point. +* \param[in] nodeIndex Node index +* \param[in] yNorm Normal flow depth (ft) +* \param[in] yCrit Critical flow depth (ft) +* \param[in] z Offset of connecting outfall link from node invert (ft) +*/ +void node_setOutletDepth(int j, double yNorm, double yCrit, double z) { switch (Node[j].type) { @@ -559,45 +526,41 @@ void node_setOutletDepth(int j, double yNorm, double yCrit, double z) } } -//============================================================================= - -double node_getPondedArea(int j, double d) -// -// Input: j = node index -// d = water depth (ft) -// Output: returns surface area of water at a node (ft2) -// Purpose: computes surface area of water at a node based on depth. -// +/*! +* \brief Computes surface area of water at a node based on depth. +* \param[in] nodeIndex Node index +* \param[in] depth Water depth at node (ft) +* \return Returns surface area of water at node (ft2) +*/ +double node_getPondedArea(int nodeIndex, double depth) { double a; // --- use regular getSurfArea function if node not flooded - if ( d <= Node[j].fullDepth || Node[j].pondedArea == 0.0 ) + if ( depth <= Node[nodeIndex].fullDepth || Node[nodeIndex].pondedArea == 0.0 ) { - return node_getSurfArea(j, d); + return node_getSurfArea(nodeIndex, depth); } // --- compute ponded depth - d = d - Node[j].fullDepth; + depth = depth - Node[nodeIndex].fullDepth; // --- use ponded area for flooded node - a = Node[j].pondedArea; - if ( a <= 0.0 ) a = node_getSurfArea(j, Node[j].fullDepth); + a = Node[nodeIndex].pondedArea; + if ( a <= 0.0 ) a = node_getSurfArea(nodeIndex, Node[nodeIndex].fullDepth); return a; } -//============================================================================= - -double node_getLosses(int j, double tStep) -// -// Input: j = node index -// tStep = time step (sec) -// Output: returns water loss rate at node (ft3) -// Purpose: computes the rates of evaporation and infiltration over a given -// time step for a node. -// +/*! +* \brief Computes the rates of evaporation and infiltration over a given +* time step for a node. +* \param[in] nodeIndex Node index +* \param[in] tStep Time step (sec) +* \return Returns water loss rate at node (ft3) +*/ +double node_getLosses(int nodeIndex, double tStep) { - if ( Node[j].type == STORAGE ) return storage_getLosses(j, tStep); + if ( Node[nodeIndex].type == STORAGE ) return storage_getLosses(nodeIndex, tStep); else return 0.0; } diff --git a/src/solver/output.c b/src/solver/output.c index b1fe8cbdf..67e67b081 100644 --- a/src/solver/output.c +++ b/src/solver/output.c @@ -119,14 +119,10 @@ static void output_saveAvgResults(FILE* file); // output_readLinkResults (called by report_Links) -//============================================================================= - +/*! +* \brief Writes basic project data to binary output file. +*/ int output_open() -// -// Input: none -// Output: returns an error code -// Purpose: writes basic project data to binary output file. -// { int j; int m; @@ -425,7 +421,6 @@ void output_checkFileSize() } */ -//============================================================================= void output_openOutFile() // @@ -455,8 +450,10 @@ void output_openOutFile() } } -//============================================================================= - +/*! +* \brief Writes computed results for current report time to binary file. +* \param[in] reportTime Elapsed simulation time (millisec) +*/ void output_saveResults(double reportTime) // // Input: reportTime = elapsed simulation time (millisec) @@ -513,14 +510,10 @@ void output_saveResults(double reportTime) Nperiods++; } -//============================================================================= - +/*! +* \brief Writes closing records to binary file. +*/ void output_end() -// -// Input: none -// Output: none -// Purpose: writes closing records to binary file. -// { INT4 k; fwrite(&IDStartPos, sizeof(INT4), 1, Fout.file); @@ -537,14 +530,10 @@ void output_end() } } -//============================================================================= - +/*! +* \brief Frees memory used for accessing the binary file. +*/ void output_close() -// -// Input: none -// Output: none -// Purpose: frees memory used for accessing the binary file. -// { FREE(SubcatchResults); FREE(NodeResults); @@ -697,15 +686,13 @@ void output_saveLinkResults(double reportTime, FILE* file) } } -//============================================================================= - +/*! +* \brief Retrieves the date/time for a specific reporting period +* from the binary output file. +* \param[in] period Index of reporting time period +* \param[out] days Date/time value +*/ void output_readDateTime(long period, DateTime* days) -// -// Input: period = index of reporting time period -// Output: days = date/time value -// Purpose: retrieves the date/time for a specific reporting period -// from the binary output file. -// { F_OFF p = period; F_OFF bytePos = OutputStartPos + (p-1)*BytesPerPeriod; @@ -714,16 +701,13 @@ void output_readDateTime(long period, DateTime* days) fread(days, sizeof(REAL8), 1, Fout.file); } -//============================================================================= - +/*! +* \brief Reads computed results for a subcatchment at a specific time +* period. +* \param[in] period Index of reporting time period +* \param[in] index Subcatchment index in binary output file +*/ void output_readSubcatchResults(long period, int index) -// -// Input: period = index of reporting time period -// index = subcatchment index in binary output file -// Output: none -// Purpose: reads computed results for a subcatchment at a specific time -// period. -// { long offset = index*NumSubcatchVars; F_OFF p = period; @@ -733,15 +717,12 @@ void output_readSubcatchResults(long period, int index) fread(SubcatchResults, sizeof(REAL4), NumSubcatchVars, Fout.file); } -//============================================================================= - +/*! +* \brief Reads computed results for a node at a specific time period. +* \param[in] period Index of reporting time period +* \param[in] index Node index in binary output file +*/ void output_readNodeResults(long period, int index) -// -// Input: period = index of reporting time period -// index = node index in binary output file -// Output: none -// Purpose: reads computed results for a node at a specific time period. -// { long offset = NumSubcatch*NumSubcatchVars + index*NumNodeVars; F_OFF p = period; @@ -751,15 +732,12 @@ void output_readNodeResults(long period, int index) fread(NodeResults, sizeof(REAL4), NumNodeVars, Fout.file); } -//============================================================================= - +/*! +* \brief Reads computed results for a link at a specific time period. +* \param[in] period Index of reporting time period +* \param[in] index Link index in binary output file +*/ void output_readLinkResults(long period, int index) -// -// Input: period = index of reporting time period -// index = link index in binary output file -// Output: none -// Purpose: reads computed results for a link at a specific time period. -// { long offset = (NumSubcatch*NumSubcatchVars + NumNodes*NumNodeVars + index*NumLinkVars); F_OFF p = period; diff --git a/src/solver/project.c b/src/solver/project.c index 427a5f7e7..c041127aa 100644 --- a/src/solver/project.c +++ b/src/solver/project.c @@ -105,27 +105,31 @@ static char MemPoolAllocated; // TRUE if memory pool allocated //----------------------------------------------------------------------------- static void initPointers(void); static void setDefaults(void); -static void openFiles(const char *f1, const char *f2, const char *f3); + +/*! +* \brief Opens a project's input and report files. +* \param[in] inp_file SWMM input file +* \param[in] rpt_file SWMM report file +* \param[in] out_file SWMM output file +*/ +static void openFiles(const char *inp_file, const char *rpt_file, const char *out_file); static void createObjects(void); static void deleteObjects(void); static void createHashTables(void); static void deleteHashTables(void); -//============================================================================= - -void project_open(const char *f1, const char *f2, const char *f3) -// -// Input: f1 = pointer to name of input file -// f2 = pointer to name of report file -// f3 = pointer to name of binary output file -// Output: none -// Purpose: opens a new SWMM project. -// +/*! +* \brief Opens a new SWMM project +* \param[in] inp_file SWMM input file +* \param[in] rpt_file SWMM report file +* \param[in] out_file SWMM output file +*/ +void project_open(const char *inp_file, const char *rpt_file, const char *out_file) { initPointers(); setDefaults(); - openFiles(f1, f2, f3); + openFiles(inp_file, rpt_file, out_file); } //============================================================================= @@ -962,16 +966,13 @@ void setDefaults() Adjust.hydconFactor = 1.0; } -//============================================================================= - -void openFiles(const char *f1, const char *f2, const char *f3) -// -// Input: f1 = name of input file -// f2 = name of report file -// f3 = name of binary output file -// Output: none -// Purpose: opens a project's input and report files. -// +/*! +* \brief Opens a project's input and report files. +* \param[in] inp_file SWMM input file +* \param[in] rpt_file SWMM report file +* \param[in] out_file SWMM output file +*/ +void openFiles(const char *inp_file, const char *rpt_file, const char *out_file) { // --- initialize file pointers to NULL Finp.file = NULL; @@ -979,12 +980,12 @@ void openFiles(const char *f1, const char *f2, const char *f3) Fout.file = NULL; // --- save file names - sstrncpy(Finp.name, f1, MAXFNAME); - sstrncpy(Frpt.name, f2, MAXFNAME); - sstrncpy(Fout.name, f3, MAXFNAME); + sstrncpy(Finp.name, inp_file, MAXFNAME); + sstrncpy(Frpt.name, rpt_file, MAXFNAME); + sstrncpy(Fout.name, out_file, MAXFNAME); // --- check that file names are not identical - if (strcomp(f1, f2) || strcomp(f1, f3) || strcomp(f2, f3)) + if (strcomp(inp_file, rpt_file) || strcomp(inp_file, out_file) || strcomp(rpt_file, out_file)) { writecon(FMT11); ErrorCode = ERR_FILE_NAME; @@ -992,14 +993,14 @@ void openFiles(const char *f1, const char *f2, const char *f3) } // --- open input and report files - if ((Finp.file = fopen(f1,"rt")) == NULL) + if ((Finp.file = fopen(inp_file,"rt")) == NULL) { writecon(FMT12); - writecon(f1); + writecon(inp_file); ErrorCode = ERR_INP_FILE; return; } - if ((Frpt.file = fopen(f2,"wt")) == NULL) + if ((Frpt.file = fopen(rpt_file,"wt")) == NULL) { writecon(FMT13); ErrorCode = ERR_RPT_FILE; diff --git a/src/solver/qualrout.c b/src/solver/qualrout.c index 409a0546f..36e9ed56e 100644 --- a/src/solver/qualrout.c +++ b/src/solver/qualrout.c @@ -60,14 +60,11 @@ static void updateHRT(int j, double v, double q, double tStep); static double getReactedQual(int p, double c, double v1, double tStep); static double getMixedQual(double c, double v1, double wIn, double qIn, double tStep); -//============================================================================= +/*! +* \brief Initializes water quality concentrations in all nodes and links. +*/ void qualrout_init() -// -// Input: none -// Output: none -// Purpose: initializes water quality concentrations in all nodes and links. -// { int i, p, isWet; double c; @@ -101,15 +98,12 @@ void qualrout_init() } } -//============================================================================= - +/*! +* \brief Routes water quality constituents through the drainage +* network over the current time step. +* \param[in] tStep Routing time step (sec) +*/ void qualrout_execute(double tStep) -// -// Input: tStep = routing time step (sec) -// Output: none -// Purpose: routes water quality constituents through the drainage -// network over the current time step. -// { int i, j; double qIn, vAvg; diff --git a/src/solver/rdii.c b/src/solver/rdii.c index ed00db89d..d46a77507 100644 --- a/src/solver/rdii.c +++ b/src/solver/rdii.c @@ -193,14 +193,11 @@ int rdii_readRdiiInflow(char* tok[], int ntoks) return 0; } -//============================================================================= - -void rdii_initUnitHyd(int j) -// -// Input: j = UH group index -// Output: none -// Purpose: initializes properties of a unit hydrograph group. -// +/*! +* \brief Initializes properties of a unit hydrograph group. +* \param[in] unitHydIndex Unit hydrograph group index +*/ +void rdii_initUnitHyd(int unitHydIndex) { int i; // individual UH index int m; // month index @@ -209,12 +206,12 @@ void rdii_initUnitHyd(int j) { for (i=0; i<3; i++) { - UnitHyd[j].iaMax[m][i] = 0.0; - UnitHyd[j].iaRecov[m][i] = 0.0; - UnitHyd[j].iaInit[m][i] = 0.0; - UnitHyd[j].r[m][i] = 0.0; - UnitHyd[j].tPeak[m][i] = 0; - UnitHyd[j].tBase[m][i] = 0; + UnitHyd[unitHydIndex].iaMax[m][i] = 0.0; + UnitHyd[unitHydIndex].iaRecov[m][i] = 0.0; + UnitHyd[unitHydIndex].iaInit[m][i] = 0.0; + UnitHyd[unitHydIndex].r[m][i] = 0.0; + UnitHyd[unitHydIndex].tPeak[m][i] = 0; + UnitHyd[unitHydIndex].tBase[m][i] = 0; } } } @@ -386,17 +383,17 @@ void setUnitHydParams(int j, int i, int m, double x[]) //============================================================================= -void rdii_deleteRdiiInflow(int j) +void rdii_deleteRdiiInflow(int nodeIndex) // // Input: j = node index // Output: none // Purpose: deletes the RDII inflow object for a node. // { - if ( Node[j].rdiiInflow ) + if ( Node[nodeIndex].rdiiInflow ) { - free(Node[j].rdiiInflow); - Node[j].rdiiInflow = NULL; + free(Node[nodeIndex].rdiiInflow); + Node[nodeIndex].rdiiInflow = NULL; } } @@ -404,13 +401,10 @@ void rdii_deleteRdiiInflow(int j) //============================================================================= // Reading Inflow Data From a RDII File //============================================================================= - +/*! +* \brief Opens an exisiting RDII interface file or creates a new one. +*/ void rdii_openRdii() -// -// Input: none -// Output: none -// Purpose: opens an exisiting RDII interface file or creates a new one. -// { char fStamp[] = FILE_STAMP; @@ -492,14 +486,10 @@ void openRdiiTextFile() } } -//============================================================================= - +/*! +* \brief Closes the RDII interface file. +*/ void rdii_closeRdii() -// -// Input: none -// Output: none -// Purpose: closes the RDII interface file. -// { if ( Frdii.file ) fclose(Frdii.file); if ( Frdii.mode == SCRATCH_FILE ) remove(Frdii.name); @@ -507,14 +497,12 @@ void rdii_closeRdii() FREE(RdiiNodeFlow); } -//============================================================================= - +/*! +* \brief Finds number of RDII inflows at a specified date. +* \param[in] aDate Current date/time +* \return Returns 0 if no RDII flow or number of nodes with RDII inflows +*/ int rdii_getNumRdiiFlows(DateTime aDate) -// -// Input: aDate = current date/time -// Output: returns 0 if no RDII flow or number of nodes with RDII inflows -// Purpose: finds number of RDII inflows at a specified date. -// { // --- default result is 0 indicating no RDII inflow at specified date if ( NumRdiiNodes == 0 ) return 0; @@ -537,21 +525,18 @@ int rdii_getNumRdiiFlows(DateTime aDate) return 0; } -//============================================================================= - -void rdii_getRdiiFlow(int i, int* j, double* q) -// -// Input: i = RDII node index -// j = pointer to project node index -// q = pointer to RDII flow rate -// Output: sets node index and RDII inflow for node -// Purpose: finds index and current RDII inflow for an RDII node. -// +/*! +* \brief Finds index and current RDII inflow for an RDII node. +* \param[in] index RDII node index +* \param[out] nodeIndex Node index +* \param[out] q RDII flow rate +*/ +void rdii_getRdiiFlow(int index, int* nodeIndex, double* q) { - if ( i >= 0 && i < NumRdiiNodes ) + if ( index >= 0 && index < NumRdiiNodes ) { - *j = RdiiNodeIndex[i]; - *q = RdiiNodeFlow[i]; + *nodeIndex = RdiiNodeIndex[index]; + *q = RdiiNodeFlow[index]; } } diff --git a/src/solver/routing.c b/src/solver/routing.c index bf93bd6bb..07b8882b1 100644 --- a/src/solver/routing.c +++ b/src/solver/routing.c @@ -150,8 +150,12 @@ void routing_close(int routingModel) FREE(SortedLinks); } -//============================================================================= - +/*! +* \brief Determines time step used for flow routing at current time period. +* \param[in] routingModel Routing method code +* \param[in] fixedStep User-supplied time step (sec) +* \return Time step used for flow routing (sec) +*/ double routing_getRoutingStep(int routingModel, double fixedStep) // // Input: routingModel = routing method code diff --git a/src/solver/runoff.c b/src/solver/runoff.c index 080674fbb..90dde8b02 100644 --- a/src/solver/runoff.c +++ b/src/solver/runoff.c @@ -77,8 +77,10 @@ static void runoff_readFromFile(void); static void runoff_saveToFile(float tStep); static void runoff_getOutfallRunon(double tStep); -//============================================================================= - +/*! +* \brief Opens the runoff analyzer. +* \return Error code +*/ int runoff_open() // // Input: none @@ -119,8 +121,9 @@ int runoff_open() return ErrorCode; } -//============================================================================= - +/*! +* \brief Closes the runoff analyzer. +*/ void runoff_close() // // Input: none @@ -150,8 +153,9 @@ void runoff_close() if ( Fclimate.file ) fclose(Fclimate.file); } -//============================================================================= - +/*! +* \brief Computes runoff for each subcatchment at the current runoff time. +*/ void runoff_execute() // // Input: none diff --git a/src/solver/snow.c b/src/solver/snow.c index 2e1c74370..990259c21 100644 --- a/src/solver/snow.c +++ b/src/solver/snow.c @@ -63,8 +63,16 @@ static void updateColdContent(TSnowpack* snowpack, int i, double asc, double snowfall, double tStep); -//============================================================================= - +/*! +* \brief Reads snowmelt parameters from a tokenized line of input data. +* \param[in] tok Array of string tokens +* \param[in] ntoks Number of tokens +* \return Error code +* \details +* Format of data are: +* - Name SubArea Cmin Cmax Tbase FWF SD0 FW0 SNN0/SD100 +* - Name REMOVAL SDplow Fout Fimperv Fperv Fimelt Fsubcatch (Subcatch) +*/ int snow_readMeltParams(char* tok[], int ntoks) // // Input: tok[] = array of string tokens @@ -121,9 +129,13 @@ int snow_readMeltParams(char* tok[], int ntoks) return 0; } -//============================================================================= - -int snow_createSnowpack(int j, int k) +/*! +* \brief Creates a snowpack object for a subcatchment. +* \param[in] subcatchIndex Subcatchment index +* \param[in] snowIndex Snowmelt parameter set index +* \return TRUE if successful, FALSE if not +*/ +int snow_createSnowpack(int subcatchIndex, int snowIndex) // // Input: j = subcatchment index // k = snow melt parameter set index @@ -134,14 +146,16 @@ int snow_createSnowpack(int j, int k) TSnowpack* snowpack; snowpack = (TSnowpack *) malloc(sizeof(TSnowpack)); if ( !snowpack ) return FALSE; - Subcatch[j].snowpack = snowpack; - snowpack->snowmeltIndex = k; + Subcatch[subcatchIndex].snowpack = snowpack; + snowpack->snowmeltIndex = snowIndex; return TRUE; } -//============================================================================= - -void snow_initSnowpack(int j) +/*! +* \brief Initializes state of a subcatchment's snowpack. +* \param[in] subcatchIndex Subcatchment index +*/ +void snow_initSnowpack(int subcatchIndex) // // Input: j = subcatchment index // Output: none @@ -155,17 +169,17 @@ void snow_initSnowpack(int j) TSnowpack* snowpack; // ptr. to snow pack object // --- get ptr. to subcatchment's snow pack object - snowpack = Subcatch[j].snowpack; + snowpack = Subcatch[subcatchIndex].snowpack; if ( snowpack == NULL ) return; // --- identify index of snow melt data set used by snow pack - k = Subcatch[j].snowpack->snowmeltIndex; + k = Subcatch[subcatchIndex].snowpack->snowmeltIndex; // --- find fractional area of each snow surface f = Snowmelt[k].snn; - snowpack->fArea[SNOW_PLOWABLE] = f * Subcatch[j].fracImperv; - snowpack->fArea[SNOW_IMPERV] = (1.0 - f) * Subcatch[j].fracImperv; - snowpack->fArea[SNOW_PERV] = 1.0 - Subcatch[j].fracImperv; + snowpack->fArea[SNOW_PLOWABLE] = f * Subcatch[subcatchIndex].fracImperv; + snowpack->fArea[SNOW_IMPERV] = (1.0 - f) * Subcatch[subcatchIndex].fracImperv; + snowpack->fArea[SNOW_PERV] = 1.0 - Subcatch[subcatchIndex].fracImperv; // --- initialize state of snow pack on each snow surface for (i=SNOW_PLOWABLE; i<=SNOW_PERV; i++) @@ -185,12 +199,14 @@ void snow_initSnowpack(int j) snowpack->awe[i] = 1.0; snowDepth += snowpack->wsnow[i] * snowpack->fArea[i]; } - Subcatch[j].newSnowDepth = snowDepth; + Subcatch[subcatchIndex].newSnowDepth = snowDepth; } -//============================================================================= - -void snow_initSnowmelt(int j) +/*! +* \brief Initializes values in a snowmelt parameter set. +* \param[in] snowIndex Snowmelt parameter set index +*/ +void snow_initSnowmelt(int snowIndex) // // Input: j = snowmelt parameter set index // Output: none @@ -200,23 +216,25 @@ void snow_initSnowmelt(int j) int i, k; for (i=0; i<3; i++) { - Snowmelt[j].snn = 0.0; - Snowmelt[j].si[i] = 0.0; - Snowmelt[j].dhmin[i] = 0.0; - Snowmelt[j].dhmax[i] = 0.0; - Snowmelt[j].tbase[i] = 0.0; - Snowmelt[j].fwfrac[i] = 0.0; - Snowmelt[j].wsnow[i] = 0.0; - Snowmelt[j].fwnow[i] = 0.0; - Snowmelt[j].weplow = 1.0e6; - for (k=0; k<5; k++) Snowmelt[j].sfrac[k] = 0.0; - Snowmelt[j].toSubcatch = -1; + Snowmelt[snowIndex].snn = 0.0; + Snowmelt[snowIndex].si[i] = 0.0; + Snowmelt[snowIndex].dhmin[i] = 0.0; + Snowmelt[snowIndex].dhmax[i] = 0.0; + Snowmelt[snowIndex].tbase[i] = 0.0; + Snowmelt[snowIndex].fwfrac[i] = 0.0; + Snowmelt[snowIndex].wsnow[i] = 0.0; + Snowmelt[snowIndex].fwnow[i] = 0.0; + Snowmelt[snowIndex].weplow = 1.0e6; + for (k=0; k<5; k++) Snowmelt[snowIndex].sfrac[k] = 0.0; + Snowmelt[snowIndex].toSubcatch = -1; } } -//============================================================================= - -void snow_validateSnowmelt(int j) +/*! +* \brief Checks for valid values in a snowmelt parameter set. +* \param[in] snowIndex Snowmelt parameter set index +*/ +void snow_validateSnowmelt(int snowIndex) // // Input: j = snowmelt parameter set index // Output: none @@ -230,24 +248,28 @@ void snow_validateSnowmelt(int j) for ( k = SNOW_PLOWABLE; k <= SNOW_PERV; k++ ) { // --- check melt coeffs. - if ( Snowmelt[j].dhmin[k] > Snowmelt[j].dhmax[k] ) err = TRUE; + if ( Snowmelt[snowIndex].dhmin[k] > Snowmelt[snowIndex].dhmax[k] ) err = TRUE; // --- check free water fraction - if ( Snowmelt[j].fwfrac[k] < 0.0 || - Snowmelt[j].fwfrac[k] > 1.0) err = TRUE; + if ( Snowmelt[snowIndex].fwfrac[k] < 0.0 || + Snowmelt[snowIndex].fwfrac[k] > 1.0) err = TRUE; } // --- check fraction of imperv. area plowable - if ( Snowmelt[j].snn < 0.0 || Snowmelt[j].snn > 1.0 ) err = TRUE; + if ( Snowmelt[snowIndex].snn < 0.0 || Snowmelt[snowIndex].snn > 1.0 ) err = TRUE; // --- check that removal fractions sum <= 1.0 - for ( k=0; k<5; k++ ) sum += Snowmelt[j].sfrac[k]; + for ( k=0; k<5; k++ ) sum += Snowmelt[snowIndex].sfrac[k]; if ( sum > 1.01 ) err = TRUE; - if ( err ) report_writeErrorMsg(ERR_SNOWPACK_PARAMS, Snowmelt[j].ID); + if ( err ) report_writeErrorMsg(ERR_SNOWPACK_PARAMS, Snowmelt[snowIndex].ID); } -//============================================================================= - +/*! +* \brief Retrieves the current state of a snowpack object. +* \param[in] subcatch Subcatchment index +* \param[in] subArea Snowpack sub-area index +* \param[out] x Array of snowpack state variables +*/ void snow_getState(int i, int j, double x[]) // // Input: i = subcatchment index @@ -265,9 +287,13 @@ void snow_getState(int i, int j, double x[]) x[4] = snowpack->awe[j]; } -//============================================================================= - -void snow_setState(int i, int j, double x[]) +/*! +* \brief Sets the current state of a snowpack object. +* \param[in] subcatchIndex Subcatchment index +* \param[in] subAreaIndex Snowpack sub-area index +* \param[in] x Array of snowpack state variables +*/ +void snow_setState(int subcatchIndex, int subAreaIndex, double x[]) // // Input: i = subcatchment index // j = snow pack sub-area index @@ -276,13 +302,13 @@ void snow_setState(int i, int j, double x[]) // Purpose: sets the current state of a snow pack object. // { - TSnowpack* snowpack = Subcatch[i].snowpack; + TSnowpack* snowpack = Subcatch[subcatchIndex].snowpack; if ( snowpack == NULL ) return; - snowpack->wsnow[j] = x[0]; - snowpack->fw[j] = x[1]; - snowpack->coldc[j] = x[2]; - snowpack->ati[j] = x[3]; - snowpack->awe[j] = x[4]; + snowpack->wsnow[subAreaIndex] = x[0]; + snowpack->fw[subAreaIndex] = x[1]; + snowpack->coldc[subAreaIndex] = x[2]; + snowpack->ati[subAreaIndex] = x[3]; + snowpack->awe[subAreaIndex] = x[4]; } //============================================================================= @@ -336,9 +362,12 @@ void setMeltParams(int j, int k, double x[]) } } -//============================================================================= - -void snow_setMeltCoeffs(int j, double s) +/*! +* \brief Sets values of snowmelt coefficients for a particular time of year. +* \param[in] snowIndex Snowmelt parameter set index +* \param[in] season Season of the year +*/ +void snow_setMeltCoeffs(int snowIndex, double season) // // Input: j = snowmelt parameter set index // s = snow season of year @@ -350,14 +379,17 @@ void snow_setMeltCoeffs(int j, double s) for (k=SNOW_PLOWABLE; k<=SNOW_PERV; k++) { - Snowmelt[j].dhm[k] = 0.5 * (Snowmelt[j].dhmax[k] * (1.0 + s) - + Snowmelt[j].dhmin[k] * (1.0 - s)); + Snowmelt[snowIndex].dhm[k] = 0.5 * (Snowmelt[snowIndex].dhmax[k] * (1.0 + season) + + Snowmelt[snowIndex].dhmin[k] * (1.0 - season)); } } -//============================================================================= - -void snow_plowSnow(int j, double tStep) +/*! +* \brief Adds new snow to subcatchment and plows it between sub-areas. +* \param[in] subcatchIndex Subcatchment index +* \param[in] tStep Time step (sec) +*/ +void snow_plowSnow(int subcatchIndex, double tStep) // // Input: j = subcatchment index // tStep = time step (sec) @@ -375,11 +407,11 @@ void snow_plowSnow(int j, double tStep) double sfracTotal; // total fraction of snow moved TSnowpack* snowpack; // ptr. to snow pack object - snowpack = Subcatch[j].snowpack; + snowpack = Subcatch[subcatchIndex].snowpack; if ( !snowpack ) return; // --- see if there's any snowfall - gage_getPrecip(Subcatch[j].gage, &rainfall, &snowfall); + gage_getPrecip(Subcatch[subcatchIndex].gage, &rainfall, &snowfall); // --- add snowfall to snow pack for (i=SNOW_PLOWABLE; i<=SNOW_PERV; i++) @@ -402,7 +434,7 @@ void snow_plowSnow(int j, double tStep) // --- plow out of system f = snowpack->fArea[SNOW_PLOWABLE] * - (Subcatch[j].area - Subcatch[j].lidArea); + (Subcatch[subcatchIndex].area - Subcatch[subcatchIndex].lidArea); Snow.removed += Snowmelt[k].sfrac[0] * exc * f; sfracTotal = Snowmelt[k].sfrac[0]; @@ -453,9 +485,17 @@ void snow_plowSnow(int j, double tStep) } } -//============================================================================= - -double snow_getSnowMelt(int j, double rainfall, double snowfall, double tStep, +/*! +* \brief Modifies rainfall input to subcatchment's subareas based on possible +* snow melt and updates snow depth over entire subcatchment. +* \param[in] subcatchIndex Subcatchment index +* \param[in] rainfall Rainfall (ft/sec) +* \param[in] snowfall Snowfall (ft/sec) +* \param[in] tStep Time step (sec) +* \param[out] netPrecip Rainfall + snowmelt on each runoff sub-area (ft/sec) +* \return New snow depth over subcatchment +*/ +double snow_getSnowMelt(int subcatchIndex, double rainfall, double snowfall, double tStep, double netPrecip[]) // // Input: j = subcatchment index @@ -477,7 +517,7 @@ double snow_getSnowMelt(int j, double rainfall, double snowfall, double tStep, TSnowpack* snowpack; // ptr. to snow pack object // --- get ptr. to subcatchment's snowpack - snowpack = Subcatch[j].snowpack; + snowpack = Subcatch[subcatchIndex].snowpack; // --- compute snowmelt over entire subcatchment when rain falling rmelt = getRainmelt(rainfall); @@ -515,21 +555,24 @@ double snow_getSnowMelt(int j, double rainfall, double snowfall, double tStep, } // --- combine netPrecip on plowable & non-plowable imperv. areas - if ( Subcatch[j].fracImperv > 0.0 ) + if ( Subcatch[subcatchIndex].fracImperv > 0.0 ) { impervPrecip = (netPrecip[SNOW_PLOWABLE] * snowpack->fArea[SNOW_PLOWABLE] + netPrecip[SNOW_IMPERV] * snowpack->fArea[SNOW_IMPERV]) / - Subcatch[j].fracImperv; + Subcatch[subcatchIndex].fracImperv; netPrecip[IMPERV0] = impervPrecip; netPrecip[IMPERV1] = impervPrecip; } return snowDepth; } -//============================================================================= - -double snow_getSnowCover(int j) +/*! +* \brief Computes volume of snow on a subcatchment. +* \param[in] subcatchIndex Subcatchment index +* \return Volume of snow cover (ft3) +*/ +double snow_getSnowCover(int subcatchIndex) // // Input: j = subcatchment index // Output: returns volume of snow cover (ft3) @@ -540,14 +583,14 @@ double snow_getSnowCover(int j) double snowCover = 0.0; // snow cover volume (ft3) TSnowpack* snowpack; // ptr. to snowpack object - snowpack = Subcatch[j].snowpack; + snowpack = Subcatch[subcatchIndex].snowpack; if ( !snowpack ) return 0.0; for (i=SNOW_PLOWABLE; i<=SNOW_PERV; i++) { snowCover += (snowpack->wsnow[i] + snowpack->fw[i]) * snowpack->fArea[i]; } - return snowCover * (Subcatch[j].area - Subcatch[j].lidArea); + return snowCover * (Subcatch[subcatchIndex].area - Subcatch[subcatchIndex].lidArea); } //============================================================================= diff --git a/src/solver/stats.c b/src/solver/stats.c index 65100ac09..6d38f70a3 100644 --- a/src/solver/stats.c +++ b/src/solver/stats.c @@ -97,14 +97,11 @@ static void stats_updateLinkStats(int link, double tStep, DateTime aDate); static void stats_findMaxStats(void); static void stats_updateMaxStats(TMaxStats maxStats[], int i, int j, double x); -//============================================================================= - -int stats_open() -// -// Input: none -// Output: returns an error code -// Purpose: opens the simulation statistics system. -// +/*! +* \brief Opens the simulation statistics system. +* \return Error code +*/ +int stats_open() { int j, k; double timeStepDelta; @@ -313,14 +310,10 @@ int stats_open() return 0; } -//============================================================================= - -void stats_close() -// -// Input: none -// Output: -// Purpose: closes the simulation statistics system. -// +/*! +* \brief Closes the simulation statistics system. +*/ +void stats_close() { int j; @@ -337,14 +330,10 @@ void stats_close() FREE(PumpStats); } -//============================================================================= - -void stats_report() -// -// Input: none -// Output: none -// Purpose: reports simulation statistics. -// +/*! +* \brief Reports simulation statistics. +*/ +void stats_report() { // --- report flow routing accuracy statistics if ( Nobjects[LINK] > 0 && RouteModel != NO_ROUTING ) @@ -364,64 +353,67 @@ void stats_report() statsrpt_writeReport(); } -//============================================================================= - -void stats_updateSubcatchStats(int j, double rainVol, double runonVol, +/*! +* \brief Updates totals of runoff components for a specific subcatchment. +* \param[in] subcatchIndex Subcatchment index +* \param[in] rainVol Rainfall + snowfall volume (ft3) +* \param[in] runonVol Runon volume from other subcatchments (ft3) +* \param[in] evapVol Evaporation volume (ft3) +* \param[in] infilVol Infiltration volume (ft3) +* \param[in] impervVol Impervious runoff volume (ft3) +* \param[in] pervVol Pervious runoff volume (ft3) +* \param[in] runoffVol Total runoff volume (ft3) +* \param[in] runoff Runoff rate (cfs) +*/ +void stats_updateSubcatchStats(int subcatchIndex, double rainVol, double runonVol, double evapVol, double infilVol, double impervVol, double pervVol, double runoffVol, double runoff) -// -// Input: j = subcatchment index -// rainVol = rainfall + snowfall volume (ft3) -// runonVol = runon volume from other subcatchments (ft3) -// evapVol = evaporation volume (ft3) -// infilVol = infiltration volume (ft3) -// impervVol = impervious runoff volume (ft3) -// pervVol = pervious runoff volume (ft3) -// runoffVol = runoff volume (ft3) -// runoff = runoff rate (cfs) -// Output: none -// Purpose: updates totals of runoff components for a specific subcatchment. -// { - SubcatchStats[j].precip += rainVol; - SubcatchStats[j].runon += runonVol; - SubcatchStats[j].evap += evapVol; - SubcatchStats[j].infil += infilVol; - SubcatchStats[j].runoff += runoffVol; - SubcatchStats[j].maxFlow = MAX(SubcatchStats[j].maxFlow, runoff); - SubcatchStats[j].impervRunoff += impervVol; - SubcatchStats[j].pervRunoff += pervVol; + SubcatchStats[subcatchIndex].precip += rainVol; + SubcatchStats[subcatchIndex].runon += runonVol; + SubcatchStats[subcatchIndex].evap += evapVol; + SubcatchStats[subcatchIndex].infil += infilVol; + SubcatchStats[subcatchIndex].runoff += runoffVol; + SubcatchStats[subcatchIndex].maxFlow = MAX(SubcatchStats[subcatchIndex].maxFlow, runoff); + SubcatchStats[subcatchIndex].impervRunoff += impervVol; + SubcatchStats[subcatchIndex].pervRunoff += pervVol; } -//============================================================================= - -void stats_updateGwaterStats(int j, double infil, double evap, double latFlow, +/*! +* \brief Updates groundwater statistics for a specific subcatchment. +* \param[in] subcatchIndex Subcatchment index +* \param[in] infil Infiltration volume (ft3) +* \param[in] evap Evaporation volume (ft3) +* \param[in] latFlow Lateral flow volume (ft3) +* \param[in] deepFlow Deep flow volume (ft3) +* \param[in] theta Soil moisture content +* \param[in] waterTable Depth to water table (ft) +* \param[in] tStep Time step (sec) +*/ +void stats_updateGwaterStats(int subcatchIndex, double infil, double evap, double latFlow, double deepFlow, double theta, double waterTable, double tStep) { - Subcatch[j].groundwater->stats.infil += infil * tStep; - Subcatch[j].groundwater->stats.evap += evap * tStep; - Subcatch[j].groundwater->stats.latFlow += latFlow * tStep; - Subcatch[j].groundwater->stats.deepFlow += deepFlow * tStep; - Subcatch[j].groundwater->stats.avgUpperMoist += theta * tStep; - Subcatch[j].groundwater->stats.avgWaterTable += waterTable * tStep; - Subcatch[j].groundwater->stats.finalUpperMoist = theta; - Subcatch[j].groundwater->stats.finalWaterTable = waterTable; - if ( fabs(latFlow) > fabs(Subcatch[j].groundwater->stats.maxFlow) ) + Subcatch[subcatchIndex].groundwater->stats.infil += infil * tStep; + Subcatch[subcatchIndex].groundwater->stats.evap += evap * tStep; + Subcatch[subcatchIndex].groundwater->stats.latFlow += latFlow * tStep; + Subcatch[subcatchIndex].groundwater->stats.deepFlow += deepFlow * tStep; + Subcatch[subcatchIndex].groundwater->stats.avgUpperMoist += theta * tStep; + Subcatch[subcatchIndex].groundwater->stats.avgWaterTable += waterTable * tStep; + Subcatch[subcatchIndex].groundwater->stats.finalUpperMoist = theta; + Subcatch[subcatchIndex].groundwater->stats.finalWaterTable = waterTable; + if ( fabs(latFlow) > fabs(Subcatch[subcatchIndex].groundwater->stats.maxFlow) ) { - Subcatch[j].groundwater->stats.maxFlow = latFlow; + Subcatch[subcatchIndex].groundwater->stats.maxFlow = latFlow; } } -//============================================================================= - -void stats_updateMaxRunoff() -// -// Input: none -// Output: updates global variable MaxRunoffFlow -// Purpose: updates value of maximum system runoff rate. -// +/*! +* \brief Updates value of maximum system runoff rate. +* \note Updates global variable MaxRunoffFlow +*/ +void stats_updateMaxRunoff() { int j; double sysRunoff = 0.0; @@ -430,29 +422,23 @@ void stats_updateMaxRunoff() MaxRunoffFlow = MAX(MaxRunoffFlow, sysRunoff); } -//============================================================================= - -void stats_updateMaxNodeDepth(int j, double depth) -// -// Input: j = node index -// depth = water depth at node at current reporting time (ft) -// Output: none -// Purpose: updates a node's maximum depth recorded at reporting times. -// +/*! +* \brief Updates a node's maximum depth recorded at reporting times. +* \param[in] nodeIndex Node index +* \param[in] depth Water depth at node at current reporting time (ft) +*/ +void stats_updateMaxNodeDepth(int j, double depth) { if ( NodeStats != NULL ) NodeStats[j].maxRptDepth = MAX(NodeStats[j].maxRptDepth, depth); } -//============================================================================= - -void stats_updateFlowStats(double tStep, DateTime aDate) -// -// Input: tStep = routing time step (sec) -// aDate = current date/time -// Output: none -// Purpose: updates various flow routing statistics at current time period. -// +/*! +* \brief Updates various flow routing statistics at current time period. +* \param[in] tStep Routing time step (sec) +* \param[in] aDate Current date/time +*/ +void stats_updateFlowStats(double tStep, DateTime aDate) { int j; @@ -479,16 +465,13 @@ void stats_updateFlowStats(double tStep, DateTime aDate) MaxOutfallFlow = MAX(MaxOutfallFlow, SysOutfallFlow); } -//============================================================================= - +/*! +* \brief Updates flow routing time step statistics. +* \param[in] tStep Current flow routing time step (sec) +* \param[in] trialsCount Number of trials used to solve routing +* \param[in] steadyState TRUE if steady flow conditions exist +*/ void stats_updateTimeStepStats(double tStep, int trialsCount, int steadyState) -// -// Input: tStep = current flow routing time step (sec) -// trialsCount = number of trials used to solve routing -// steadyState = TRUE if steady flow conditions exist -// Output: none -// Purpose: updates flow routing time step statistics. -// { int j; @@ -517,22 +500,22 @@ void stats_updateTimeStepStats(double tStep, int trialsCount, int steadyState) } } -//============================================================================= - -void stats_updateCriticalTimeCount(int node, int link) -// -// Input: node = node index -// link = link index -// Output: none -// Purpose: updates count of times a node or link was time step-critical. -// +/*! +* \brief Updates count of times a node or link was time step-critical. +* \param[in] nodeIndex Node index +* \param[in] linkIndex Link index +*/ +void stats_updateCriticalTimeCount(int nodeIndex, int linkIndex) { - if ( node >= 0 ) NodeStats[node].timeCourantCritical += 1.0; - else if ( link >= 0 ) LinkStats[link].timeCourantCritical += 1.0; + if ( nodeIndex >= 0 ) NodeStats[nodeIndex].timeCourantCritical += 1.0; + else if ( linkIndex >= 0 ) LinkStats[linkIndex].timeCourantCritical += 1.0; } -//============================================================================= - +/*! +* \brief Updates convergence statistics node +* \param[in] nodeIndex Node index +* \param[in] converged TRUE if node has converged +*/ void stats_updateConvergenceStats(int node, int converged) { if (converged == FALSE) NodeStats[node].nonConvergedCount++; @@ -644,7 +627,7 @@ void stats_updateNodeStats(int j, double tStep, DateTime aDate) //============================================================================= -void stats_updateLinkStats(int j, double tStep, DateTime aDate) +void stats_updateLinkStats(int j, double tStep, DateTime aDate) // // Input: j = link index // tStep = routing time step (sec) @@ -753,7 +736,7 @@ void stats_updateLinkStats(int j, double tStep, DateTime aDate) //============================================================================= -void stats_findMaxStats() +void stats_findMaxStats() // // Input: none // Output: none @@ -838,7 +821,7 @@ void stats_findMaxStats() //============================================================================= -void stats_updateMaxStats(TMaxStats maxStats[], int i, int j, double x) +void stats_updateMaxStats(TMaxStats maxStats[], int i, int j, double x) // // Input: maxStats[] = array of critical statistics values // i = object category (NODE or LINK) diff --git a/src/solver/subcatch.c b/src/solver/subcatch.c index 7e8ea3232..2bf089f1f 100644 --- a/src/solver/subcatch.c +++ b/src/solver/subcatch.c @@ -116,19 +116,16 @@ static void updatePondedDepth(TSubarea* subarea, double* tx); static void getDdDt(double t, double* d, double* dddt); static void adjustSubareaParams(int subareaType, int subcatch); -//============================================================================= - -int subcatch_readParams(int j, char* tok[], int ntoks) -// -// Input: j = subcatchment index -// tok[] = array of string tokens -// ntoks = number of tokens -// Output: returns an error code -// Purpose: reads subcatchment parameters from a tokenized line of input data. -// -// Data has format: -// Name RainGage Outlet Area %Imperv Width Slope CurbLength Snowpack -// +/*! +* \brief Reads subcatchment parameters from a tokenized line of input data. +* \param[in] subcatchIndex Subcatchment index +* \param[in] tok Array of string tokens +* \param[in] ntoks Number of tokens +* \return Error code +* \details Data has format: +* Name RainGage Outlet Area %Imperv Width Slope CurbLength Snowpack +*/ +int subcatch_readParams(int subcatchIndex, char* tok[], int ntoks) { int i, k, m; char* id; @@ -171,41 +168,38 @@ int subcatch_readParams(int j, char* tok[], int ntoks) } // --- assign input values to subcatch's properties - Subcatch[j].ID = id; - Subcatch[j].gage = (int)x[0]; - Subcatch[j].outNode = (int)x[1]; - Subcatch[j].outSubcatch = (int)x[2]; - Subcatch[j].area = x[3] / UCF(LANDAREA); - Subcatch[j].fracImperv = MIN(x[4], 100.0) / 100.0; - Subcatch[j].width = x[5] / UCF(LENGTH); - Subcatch[j].slope = x[6] / 100.0; - Subcatch[j].curbLength = x[7]; - Subcatch[j].nPervPattern = -1; - Subcatch[j].dStorePattern = -1; - Subcatch[j].infilPattern = -1; + Subcatch[subcatchIndex].ID = id; + Subcatch[subcatchIndex].gage = (int)x[0]; + Subcatch[subcatchIndex].outNode = (int)x[1]; + Subcatch[subcatchIndex].outSubcatch = (int)x[2]; + Subcatch[subcatchIndex].area = x[3] / UCF(LANDAREA); + Subcatch[subcatchIndex].fracImperv = MIN(x[4], 100.0) / 100.0; + Subcatch[subcatchIndex].width = x[5] / UCF(LENGTH); + Subcatch[subcatchIndex].slope = x[6] / 100.0; + Subcatch[subcatchIndex].curbLength = x[7]; + Subcatch[subcatchIndex].nPervPattern = -1; + Subcatch[subcatchIndex].dStorePattern = -1; + Subcatch[subcatchIndex].infilPattern = -1; // --- create the snow pack object if it hasn't already been created if ( x[8] >= 0 ) { - if ( !snow_createSnowpack(j, (int)x[8]) ) + if ( !snow_createSnowpack(subcatchIndex, (int)x[8]) ) return error_setInpError(ERR_MEMORY, ""); } return 0; } -//============================================================================= - +/*! +* \brief Rreads subcatchment's subarea parameters from a tokenized +* line of input data. +* \param[in] tok Array of string tokens +* \param[in] ntoks Number of tokens +* \return Error code +* \details Data has format: +* Subcatch Imperv_N Perv_N Imperv_S Perv_S PctZero RouteTo (PctRouted) +*/ int subcatch_readSubareaParams(char* tok[], int ntoks) -// -// Input: tok[] = array of string tokens -// ntoks = number of tokens -// Output: returns an error code -// Purpose: reads subcatchment's subarea parameters from a tokenized -// line of input data. -// -// Data has format: -// Subcatch Imperv_N Perv_N Imperv_S Perv_S PctZero RouteTo (PctRouted) -// { int i, j, k, m; double x[7]; @@ -281,19 +275,16 @@ int subcatch_readSubareaParams(char* tok[], int ntoks) return 0; } -//============================================================================= - +/*! +* \brief Reads assignment of landuses to subcatchment from a tokenized +* line of input data. +* \param[in] tok Array of string tokens +* \param[in] ntoks Number of tokens +* \return Error code +* \details Data has format: +* Subcatch landuse percent .... landuse percent +*/ int subcatch_readLanduseParams(char* tok[], int ntoks) -// -// Input: tok[] = array of string tokens -// ntoks = number of tokens -// Output: returns an error code -// Purpose: reads assignment of landuses to subcatchment from a tokenized -// line of input data. -// -// Data has format: -// Subcatch landuse percent .... landuse percent -// { int j, k, m; double f; @@ -321,19 +312,16 @@ int subcatch_readLanduseParams(char* tok[], int ntoks) return 0; } -//============================================================================= - +/*! +* \brief Reads initial pollutant buildup on subcatchment from +* tokenized line of input data. +* \param[in] tok Array of string tokens +* \param[in] ntoks Number of tokens +* \return Error code +* \details Data has format: +* Subcatch pollut initLoad .... pollut initLoad +*/ int subcatch_readInitBuildup(char* tok[], int ntoks) -// -// Input: tok[] = array of string tokens -// ntoks = number of tokens -// Output: returns an error code -// Purpose: reads initial pollutant buildup on subcatchment from -// tokenized line of input data. -// -// Data has format: -// Subcatch pollut initLoad .... pollut initLoad -// { int j, k, m; double x; @@ -361,9 +349,11 @@ int subcatch_readInitBuildup(char* tok[], int ntoks) return 0; } -//============================================================================= - -void subcatch_validate(int j) +/*! +* \brief Checks for valid subcatchment input parameters. +* \param[in] subcatchIndex Subcatchment index +*/ +void subcatch_validate(int subcatchIndex) // // Input: j = subcatchment index // Output: none @@ -372,17 +362,17 @@ void subcatch_validate(int j) { int i; double area; - double nonLidArea = Subcatch[j].area; + double nonLidArea = Subcatch[subcatchIndex].area; // --- check for ambiguous outlet name - if ( Subcatch[j].outNode >= 0 && Subcatch[j].outSubcatch >= 0 ) - report_writeErrorMsg(ERR_SUBCATCH_OUTLET, Subcatch[j].ID); + if ( Subcatch[subcatchIndex].outNode >= 0 && Subcatch[subcatchIndex].outSubcatch >= 0 ) + report_writeErrorMsg(ERR_SUBCATCH_OUTLET, Subcatch[subcatchIndex].ID); // --- validate subcatchment's groundwater component - gwater_validate(j); + gwater_validate(subcatchIndex); // --- validate placement of LIDs in the subcatchment - nonLidArea -= Subcatch[j].lidArea; + nonLidArea -= Subcatch[subcatchIndex].lidArea; // --- compute alpha (i.e. WCON in old SWMM) for overland flow // NOTE: the area which contributes to alpha for both imperv @@ -391,105 +381,97 @@ void subcatch_validate(int j) { if ( i == PERV ) { - area = (1.0 - Subcatch[j].fracImperv) * nonLidArea; + area = (1.0 - Subcatch[subcatchIndex].fracImperv) * nonLidArea; } else { - area = Subcatch[j].fracImperv * nonLidArea; + area = Subcatch[subcatchIndex].fracImperv * nonLidArea; } - Subcatch[j].subArea[i].alpha = 0.0; + Subcatch[subcatchIndex].subArea[i].alpha = 0.0; //// Possible change to how sub-area width should be assigned. //// //// area = nonLidArea; ///////////////////////////////////////////////////////////////////// - if ( area > 0.0 && Subcatch[j].subArea[i].N > 0.0 ) + if ( area > 0.0 && Subcatch[subcatchIndex].subArea[i].N > 0.0 ) { - Subcatch[j].subArea[i].alpha = PHI * Subcatch[j].width / area * - sqrt(Subcatch[j].slope) / Subcatch[j].subArea[i].N; + Subcatch[subcatchIndex].subArea[i].alpha = PHI * Subcatch[subcatchIndex].width / area * + sqrt(Subcatch[subcatchIndex].slope) / Subcatch[subcatchIndex].subArea[i].N; } } // --- set isUsed property of subcatchment's rain gage - i = Subcatch[j].gage; + i = Subcatch[subcatchIndex].gage; if (i >= 0) Gage[i].isUsed = TRUE; } -//============================================================================= - -void subcatch_initState(int j) -// -// Input: j = subcatchment index -// Output: none -// Purpose: Initializes the state of a subcatchment. -// +/*! +* \brief Initializes state of a subcatchment. +* \param[in] subcatchIndex Subcatchment index +*/ +void subcatch_initState(int subcatchIndex) { int i; // --- initialize rainfall, runoff, & snow depth - Subcatch[j].rainfall = 0.0; - Subcatch[j].oldRunoff = 0.0; - Subcatch[j].newRunoff = 0.0; - Subcatch[j].oldSnowDepth = 0.0; - Subcatch[j].newSnowDepth = 0.0; - Subcatch[j].runon = 0.0; - Subcatch[j].evapLoss = 0.0; - Subcatch[j].infilLoss = 0.0; - Subcatch[j].apiRainfall = 0.0; - Subcatch[j].apiSnowfall = 0.0; + Subcatch[subcatchIndex].rainfall = 0.0; + Subcatch[subcatchIndex].oldRunoff = 0.0; + Subcatch[subcatchIndex].newRunoff = 0.0; + Subcatch[subcatchIndex].oldSnowDepth = 0.0; + Subcatch[subcatchIndex].newSnowDepth = 0.0; + Subcatch[subcatchIndex].runon = 0.0; + Subcatch[subcatchIndex].evapLoss = 0.0; + Subcatch[subcatchIndex].infilLoss = 0.0; + Subcatch[subcatchIndex].apiRainfall = 0.0; + Subcatch[subcatchIndex].apiSnowfall = 0.0; // --- initialize state of infiltration, groundwater, & snow pack objects - if ( Subcatch[j].infil == j ) infil_initState(j); - if ( Subcatch[j].groundwater ) gwater_initState(j); - if ( Subcatch[j].snowpack ) snow_initSnowpack(j); + if ( Subcatch[subcatchIndex].infil == subcatchIndex ) infil_initState(subcatchIndex); + if ( Subcatch[subcatchIndex].groundwater ) gwater_initState(subcatchIndex); + if ( Subcatch[subcatchIndex].snowpack ) snow_initSnowpack(subcatchIndex); // --- initialize state of sub-areas for (i = IMPERV0; i <= PERV; i++) { - Subcatch[j].subArea[i].depth = 0.0; - Subcatch[j].subArea[i].inflow = 0.0; - Subcatch[j].subArea[i].runoff = 0.0; + Subcatch[subcatchIndex].subArea[i].depth = 0.0; + Subcatch[subcatchIndex].subArea[i].inflow = 0.0; + Subcatch[subcatchIndex].subArea[i].runoff = 0.0; } // --- initialize runoff quality - surfqual_initState(j); + surfqual_initState(subcatchIndex); } -//============================================================================= - -void subcatch_setOldState(int j) -// -// Input: j = subcatchment index -// Output: none -// Purpose: replaces old state of subcatchment with new state. -// +/*! +* \brief Replaces old state of subcatchment with new state. +* \param[in] subcatchIndex Subcatchment index +*/ +void subcatch_setOldState(int subcatchIndex) { int i; - Subcatch[j].oldRunoff = Subcatch[j].newRunoff; - Subcatch[j].oldSnowDepth = Subcatch[j].newSnowDepth; + Subcatch[subcatchIndex].oldRunoff = Subcatch[subcatchIndex].newRunoff; + Subcatch[subcatchIndex].oldSnowDepth = Subcatch[subcatchIndex].newSnowDepth; for (i = IMPERV0; i <= PERV; i++) { - Subcatch[j].subArea[i].inflow = 0.0; + Subcatch[subcatchIndex].subArea[i].inflow = 0.0; } for (i = 0; i < Nobjects[POLLUT]; i++) { - Subcatch[j].oldQual[i] = Subcatch[j].newQual[i]; - Subcatch[j].newQual[i] = 0.0; + Subcatch[subcatchIndex].oldQual[i] = Subcatch[subcatchIndex].newQual[i]; + Subcatch[subcatchIndex].newQual[i] = 0.0; } - lid_setOldGroupState(j); + lid_setOldGroupState(subcatchIndex); } -//============================================================================= - +/*! +* \brief Determines what fraction of subcatchment area, including any LID +* area, is pervious. +* \param[in] subcatchIndex Subcatchment index +* \return Returns fraction of subcatchment area that is pervious cover +*/ double subcatch_getFracPerv(int j) -// -// Purpose: determines what fraction of subcatchment area, including any LID -// area, is pervious. -// Input: j = subcatchment index -// Output: returns fraction of area with pervious cover -// { double fracPerv = 1.0 - Subcatch[j].fracImperv; @@ -502,36 +484,31 @@ double subcatch_getFracPerv(int j) return fracPerv; } -//============================================================================= - -double subcatch_getStorage(int j) -// -// Input: j = subcatchment index -// Output: returns total volume of stored water (ft3) -// Purpose: finds total volume of water stored on a subcatchment's surface -// and its LIDs at the current time. -// +/*! +* \brief Finds total volume of water stored on a subcatchment's surface +* and its LIDs at the current time. +* \param[in] subcatchIndex Subcatchment index +* \return Returns total volume of water stored on subcatchment's surface (ft3) +*/ +double subcatch_getStorage(int subcatchIndex) { int i; double v = 0.0; for ( i = IMPERV0; i <= PERV; i++) { - v += Subcatch[j].subArea[i].depth * Subcatch[j].subArea[i].fArea; + v += Subcatch[subcatchIndex].subArea[i].depth * Subcatch[subcatchIndex].subArea[i].fArea; } - return v * (Subcatch[j].area - Subcatch[j].lidArea) + - lid_getStoredVolume(j); + return v * (Subcatch[subcatchIndex].area - Subcatch[subcatchIndex].lidArea) + + lid_getStoredVolume(subcatchIndex); } -//============================================================================= - -void subcatch_getRunon(int j) -// -// Input: j = subcatchment index -// Output: none -// Purpose: Routes runoff from a subcatchment to its outlet subcatchment -// or between its subareas. -// +/*! +* \brief Routes runoff from a subcatchment to its outlet subcatchment +* or between its subareas. +* \param[in] subcatchIndex Subcatchment index +*/ +void subcatch_getRunon(int subcatchIndex) { int k; // outlet subcatchment index int p; // pollutant index @@ -541,112 +518,106 @@ void subcatch_getRunon(int j) // --- add previous period's runoff from this subcatchment to the // runon of the outflow subcatchment, if it exists - k = Subcatch[j].outSubcatch; - q = Subcatch[j].oldRunoff; - if ( k >= 0 && k != j ) + k = Subcatch[subcatchIndex].outSubcatch; + q = Subcatch[subcatchIndex].oldRunoff; + if ( k >= 0 && k != subcatchIndex ) { subcatch_addRunonFlow(k, q); for (p = 0; p < Nobjects[POLLUT]; p++) { - Subcatch[k].newQual[p] += q * Subcatch[j].oldQual[p] * LperFT3; + Subcatch[k].newQual[p] += q * Subcatch[subcatchIndex].oldQual[p] * LperFT3; } } // --- add any LID underdrain flow sent from this subcatchment to // other subcatchments - if ( Subcatch[j].lidArea > 0.0 ) lid_addDrainRunon(j); + if ( Subcatch[subcatchIndex].lidArea > 0.0 ) lid_addDrainRunon(subcatchIndex); // --- add to sub-area inflow any outflow from other subarea in previous period // (NOTE: no transfer of runoff pollutant load, since runoff loads are // based on runoff flow from entire subcatchment.) // --- Case 1: imperv --> perv - if ( Subcatch[j].fracImperv < 1.0 && - Subcatch[j].subArea[IMPERV0].routeTo == TO_PERV ) + if ( Subcatch[subcatchIndex].fracImperv < 1.0 && + Subcatch[subcatchIndex].subArea[IMPERV0].routeTo == TO_PERV ) { // --- add area-wtd. outflow from imperv1 subarea to perv area inflow - q1 = Subcatch[j].subArea[IMPERV0].runoff * - Subcatch[j].subArea[IMPERV0].fArea; - q2 = Subcatch[j].subArea[IMPERV1].runoff * - Subcatch[j].subArea[IMPERV1].fArea; + q1 = Subcatch[subcatchIndex].subArea[IMPERV0].runoff * + Subcatch[subcatchIndex].subArea[IMPERV0].fArea; + q2 = Subcatch[subcatchIndex].subArea[IMPERV1].runoff * + Subcatch[subcatchIndex].subArea[IMPERV1].fArea; q = q1 + q2; - Subcatch[j].subArea[PERV].inflow += q * - (1.0 - Subcatch[j].subArea[IMPERV0].fOutlet) / - Subcatch[j].subArea[PERV].fArea; + Subcatch[subcatchIndex].subArea[PERV].inflow += q * + (1.0 - Subcatch[subcatchIndex].subArea[IMPERV0].fOutlet) / + Subcatch[subcatchIndex].subArea[PERV].fArea; } // --- Case 2: perv --> imperv - if ( Subcatch[j].fracImperv > 0.0 && - Subcatch[j].subArea[PERV].routeTo == TO_IMPERV && - Subcatch[j].subArea[IMPERV1].fArea > 0.0 ) + if ( Subcatch[subcatchIndex].fracImperv > 0.0 && + Subcatch[subcatchIndex].subArea[PERV].routeTo == TO_IMPERV && + Subcatch[subcatchIndex].subArea[IMPERV1].fArea > 0.0 ) { - q = Subcatch[j].subArea[PERV].runoff; - Subcatch[j].subArea[IMPERV1].inflow += - q * (1.0 - Subcatch[j].subArea[PERV].fOutlet) * - Subcatch[j].subArea[PERV].fArea / - Subcatch[j].subArea[IMPERV1].fArea; + q = Subcatch[subcatchIndex].subArea[PERV].runoff; + Subcatch[subcatchIndex].subArea[IMPERV1].inflow += + q * (1.0 - Subcatch[subcatchIndex].subArea[PERV].fOutlet) * + Subcatch[subcatchIndex].subArea[PERV].fArea / + Subcatch[subcatchIndex].subArea[IMPERV1].fArea; } // --- Add any return flow from LID units to pervious subarea - if ( Subcatch[j].lidArea > 0.0 && Subcatch[j].fracImperv < 1.0 ) + if ( Subcatch[subcatchIndex].lidArea > 0.0 && Subcatch[subcatchIndex].fracImperv < 1.0 ) { - pervArea = Subcatch[j].subArea[PERV].fArea * - (Subcatch[j].area - Subcatch[j].lidArea); + pervArea = Subcatch[subcatchIndex].subArea[PERV].fArea * + (Subcatch[subcatchIndex].area - Subcatch[subcatchIndex].lidArea); - q = lid_getFlowToPerv(j); + q = lid_getFlowToPerv(subcatchIndex); if ( pervArea > 0.0 ) { - Subcatch[j].subArea[PERV].inflow += q / pervArea; + Subcatch[subcatchIndex].subArea[PERV].inflow += q / pervArea; } } } -//============================================================================= - -void subcatch_addRunonFlow(int k, double q) -// -// Input: k = subcatchment index -// q = runon flow rate (cfs) to subcatchment k -// Output: none -// Purpose: Updates the total runon flow (ft/s) seen by a subcatchment that -// receives runon flow from an upstream subcatchment. -// +/*! +* \brief Updates the total runon flow (ft/s) seen by a subcatchment that +* receives runon flow from an upstream subcatchment. +* \param[in] subcatchIndex Subcatchment index +* \param[in] flow Runon flow rate (cfs) to subcatchment subcatchIndex +*/ +void subcatch_addRunonFlow(int subcatchIndex, double flow) { int i; double nonLidArea; // --- distribute runoff from upstream subcatchment (in cfs) // uniformly over the non-LID area of current subcatchment (ft/sec) - if ( Subcatch[k].area <= 0.0 ) return; - nonLidArea = Subcatch[k].area - Subcatch[k].lidArea; - if ( nonLidArea > 0.0 ) q = q / nonLidArea; - else q = q / Subcatch[k].area; - Subcatch[k].runon += q; + if ( Subcatch[subcatchIndex].area <= 0.0 ) return; + nonLidArea = Subcatch[subcatchIndex].area - Subcatch[subcatchIndex].lidArea; + if ( nonLidArea > 0.0 ) flow = flow / nonLidArea; + else flow = flow / Subcatch[subcatchIndex].area; + Subcatch[subcatchIndex].runon += flow; // --- assign this flow to the 3 types of subareas for (i = IMPERV0; i <= PERV; i++) { - Subcatch[k].subArea[i].inflow += q; + Subcatch[subcatchIndex].subArea[i].inflow += flow; } } -//============================================================================= - -double subcatch_getRunoff(int j, double tStep) -// -// Input: j = subcatchment index -// tStep = time step (sec) -// Output: returns total runoff produced by subcatchment (ft/sec) -// Purpose: Computes runoff & new storage depth for subcatchment. -// -// The 'runoff' value returned by this function is the total runoff -// generated (in ft/sec) by the subcatchment before any internal -// re-routing is applied. It is used to compute pollutant washoff. -// -// The 'outflow' value computed here (in cfs) is the surface runoff -// that actually leaves the subcatchment after any LID controls are -// applied and is saved to Subcatch[j].newRunoff. -// +/*! +* \brief Computes runoff & new storage depth for subcatchment. +* \param[in] subcatchIndex Subcatchment index +* \param[in] tStep Time step (sec) +* \details +* The 'runoff' value returned by this function is the total runoff +* generated (in ft/sec) by the subcatchment before any internal +* re-routing is applied. It is used to compute pollutant washoff. +* +* The 'outflow' value computed here (in cfs) is the surface runoff +* that actually leaves the subcatchment after any LID controls are +* applied and is saved to Subcatch[j].newRunoff. +*/ +double subcatch_getRunoff(int subcatchIndex, double tStep) { int i; // subarea index double nonLidArea; // non-LID portion of subcatch area (ft2) @@ -675,24 +646,24 @@ double subcatch_getRunoff(int j, double tStep) // --- find volume of inflow to non-LID portion of subcatchment as existing // ponded water + any runon volume from upstream areas; // rainfall and snowmelt will be added as each sub-area is analyzed - nonLidArea = MAX(0.0, Subcatch[j].area - Subcatch[j].lidArea); - vRunon = Subcatch[j].runon * tStep * nonLidArea; - Vinflow = vRunon + subcatch_getDepth(j) * nonLidArea; + nonLidArea = MAX(0.0, Subcatch[subcatchIndex].area - Subcatch[subcatchIndex].lidArea); + vRunon = Subcatch[subcatchIndex].runon * tStep * nonLidArea; + Vinflow = vRunon + subcatch_getDepth(subcatchIndex) * nonLidArea; // --- find LID runon only if LID occupies full subcatchment if ( nonLidArea <= 0.0 ) - vRunon = Subcatch[j].runon * tStep * Subcatch[j].area; + vRunon = Subcatch[subcatchIndex].runon * tStep * Subcatch[subcatchIndex].area; // --- get net precip. (rainfall + snowfall + snowmelt) on the 3 types // of subcatchment sub-areas and update Vinflow with it - getNetPrecip(j, netPrecip, tStep); + getNetPrecip(subcatchIndex, netPrecip, tStep); // --- find potential evaporation rate - if ( Evap.dryOnly && Subcatch[j].rainfall > 0.0 ) evapRate = 0.0; + if ( Evap.dryOnly && Subcatch[subcatchIndex].rainfall > 0.0 ) evapRate = 0.0; else evapRate = Evap.rate; // --- set monthly infiltration adjustment factor - infil_setInfilFactor(j); + infil_setInfilFactor(subcatchIndex); // --- examine each type of sub-area (impervious w/o depression storage, // impervious w/ depression storage, and pervious) @@ -702,10 +673,10 @@ double subcatch_getRunoff(int j, double tStep) { // --- get runoff from sub-area updating Vevap, Vpevap, // Vinfil & Voutflow) - area = nonLidArea * Subcatch[j].subArea[i].fArea; - Subcatch[j].subArea[i].runoff = - getSubareaRunoff(j, i, area, netPrecip[i], evapRate, tStep); - subAreaRunoff = Subcatch[j].subArea[i].runoff * area; + area = nonLidArea * Subcatch[subcatchIndex].subArea[i].fArea; + Subcatch[subcatchIndex].subArea[i].runoff = + getSubareaRunoff(subcatchIndex, i, area, netPrecip[i], evapRate, tStep); + subAreaRunoff = Subcatch[subcatchIndex].subArea[i].runoff * area; if (i == PERV) vPervRunoff = subAreaRunoff * tStep; else vImpervRunoff += subAreaRunoff * tStep; runoff += subAreaRunoff; @@ -714,40 +685,40 @@ double subcatch_getRunoff(int j, double tStep) // --- evaluate any LID treatment provided (updating Vevap, // Vpevap, VlidInfil, VlidIn, VlidOut, & VlidDrain) - if ( Subcatch[j].lidArea > 0.0 ) + if ( Subcatch[subcatchIndex].lidArea > 0.0 ) { - lid_getRunoff(j, tStep); + lid_getRunoff(subcatchIndex, tStep); } // --- update groundwater levels & flows if applicable - if ( !IgnoreGwater && Subcatch[j].groundwater ) + if ( !IgnoreGwater && Subcatch[subcatchIndex].groundwater ) { - gwater_getGroundwater(j, Vpevap, Vinfil+VlidInfil, tStep); + gwater_getGroundwater(subcatchIndex, Vpevap, Vinfil+VlidInfil, tStep); } // --- save subcatchment's total loss rates (ft/s) - area = Subcatch[j].area; - Subcatch[j].evapLoss = Vevap / tStep / area; - Subcatch[j].infilLoss = (Vinfil + VlidInfil) / tStep / area; + area = Subcatch[subcatchIndex].area; + Subcatch[subcatchIndex].evapLoss = Vevap / tStep / area; + Subcatch[subcatchIndex].infilLoss = (Vinfil + VlidInfil) / tStep / area; // --- find net surface runoff volume // (VlidDrain accounts for LID drain flows) vOutflow = Voutflow // runoff from all non-LID areas - VlidIn // runoff treated by LID units + VlidOut; // runoff from LID units - Subcatch[j].newRunoff = vOutflow / tStep; + Subcatch[subcatchIndex].newRunoff = vOutflow / tStep; // --- obtain external precip. volume (without any snowmelt) - vRain = Subcatch[j].rainfall * tStep * area; + vRain = Subcatch[subcatchIndex].rainfall * tStep * area; // --- update the cumulative stats for this subcatchment - stats_updateSubcatchStats(j, vRain, vRunon, Vevap, Vinfil + VlidInfil, + stats_updateSubcatchStats(subcatchIndex, vRain, vRunon, Vevap, Vinfil + VlidInfil, vImpervRunoff, vPervRunoff, vOutflow + VlidDrain, - Subcatch[j].newRunoff + VlidDrain/tStep); + Subcatch[subcatchIndex].newRunoff + VlidDrain/tStep); // --- include this subcatchment's contribution to overall flow balance // only if its outlet is a drainage system node - if ( Subcatch[j].outNode == -1 && Subcatch[j].outSubcatch != j ) + if ( Subcatch[subcatchIndex].outNode == -1 && Subcatch[subcatchIndex].outSubcatch != subcatchIndex ) { vOutflow = 0.0; } @@ -827,23 +798,25 @@ double subcatch_getDepth(int j) return depth; } -//============================================================================= - -double subcatch_getWtdOutflow(int j, double f) -// -// Input: j = subcatchment index -// f = weighting factor. -// Output: returns weighted runoff value -// Purpose: computes wtd. combination of old and new subcatchment runoff. -// +/*! +* \brief Computes weighted combination of old and new subcatchment runoff. +* \param[in] subcatchIndex Subcatchment index +* \param[in] wt Weighting factor +* \return Returns weighted runoff value (ft/sec) +*/ +double subcatch_getWtdOutflow(int subcatchIndex, double f) { - if ( Subcatch[j].area == 0.0 ) return 0.0; - return (1.0 - f) * Subcatch[j].oldRunoff + f * Subcatch[j].newRunoff; + if ( Subcatch[subcatchIndex].area == 0.0 ) return 0.0; + return (1.0 - f) * Subcatch[subcatchIndex].oldRunoff + f * Subcatch[subcatchIndex].newRunoff; } -//============================================================================= - -void subcatch_getResults(int j, double f, float x[]) +/*! +* \brief Computes wtd. combination of old and new subcatchment results. +* \param[in] subcatchIndex Subcatchment index +* \param[in] wt Weighting factor +* \param[out] x Array of subcatchment results +*/ +void subcatch_getResults(int subcatchIndex, double wt, float x[]) // // Input: j = subcatchment index // f = weighting factor @@ -853,42 +826,42 @@ void subcatch_getResults(int j, double f, float x[]) { int p; // pollutant index int k; // rain gage index - double f1 = 1.0 - f; + double f1 = 1.0 - wt; double z; double runoff; TGroundwater* gw; // ptr. to groundwater object // --- retrieve rainfall for current report period - k = Subcatch[j].gage; + k = Subcatch[subcatchIndex].gage; if ( k >= 0 ) x[SUBCATCH_RAINFALL] = (float)Gage[k].reportRainfall; else x[SUBCATCH_RAINFALL] = 0.0f; // --- retrieve snow depth - z = ( f1 * Subcatch[j].oldSnowDepth + - f * Subcatch[j].newSnowDepth ) * UCF(RAINDEPTH); + z = ( f1 * Subcatch[subcatchIndex].oldSnowDepth + + wt * Subcatch[subcatchIndex].newSnowDepth ) * UCF(RAINDEPTH); x[SUBCATCH_SNOWDEPTH] = (float)z; // --- retrieve runoff and losses - x[SUBCATCH_EVAP] = (float)(Subcatch[j].evapLoss * UCF(EVAPRATE)); - x[SUBCATCH_INFIL] = (float)(Subcatch[j].infilLoss * UCF(RAINFALL)); - runoff = f1 * Subcatch[j].oldRunoff + f * Subcatch[j].newRunoff; + x[SUBCATCH_EVAP] = (float)(Subcatch[subcatchIndex].evapLoss * UCF(EVAPRATE)); + x[SUBCATCH_INFIL] = (float)(Subcatch[subcatchIndex].infilLoss * UCF(RAINFALL)); + runoff = f1 * Subcatch[subcatchIndex].oldRunoff + wt * Subcatch[subcatchIndex].newRunoff; // --- add any LID drain flow to reported runoff - if ( Subcatch[j].lidArea > 0.0 ) + if ( Subcatch[subcatchIndex].lidArea > 0.0 ) { - runoff += f1 * lid_getDrainFlow(j, PREVIOUS) + - f * lid_getDrainFlow(j, CURRENT); + runoff += f1 * lid_getDrainFlow(subcatchIndex, PREVIOUS) + + wt * lid_getDrainFlow(subcatchIndex, CURRENT); } // --- if runoff is really small, report it as zero - if ( runoff < MIN_RUNOFF * Subcatch[j].area ) runoff = 0.0; + if ( runoff < MIN_RUNOFF * Subcatch[subcatchIndex].area ) runoff = 0.0; x[SUBCATCH_RUNOFF] = (float)(runoff * UCF(FLOW)); // --- retrieve groundwater results - gw = Subcatch[j].groundwater; + gw = Subcatch[subcatchIndex].groundwater; if ( gw ) { - z = (f1 * gw->oldFlow + f * gw->newFlow) * Subcatch[j].area * UCF(FLOW); + z = (f1 * gw->oldFlow + wt * gw->newFlow) * Subcatch[subcatchIndex].area * UCF(FLOW); x[SUBCATCH_GW_FLOW] = (float)z; z = (gw->bottomElev + gw->lowerDepth) * UCF(LENGTH); x[SUBCATCH_GW_ELEV] = (float)z; @@ -906,7 +879,7 @@ void subcatch_getResults(int j, double f, float x[]) if ( !IgnoreQuality ) for (p = 0; p < Nobjects[POLLUT]; p++ ) { if ( runoff == 0.0 ) z = 0.0; - else z = f1 * Subcatch[j].oldQual[p] + f * Subcatch[j].newQual[p]; + else z = f1 * Subcatch[subcatchIndex].oldQual[p] + wt * Subcatch[subcatchIndex].newQual[p]; x[SUBCATCH_WASHOFF+p] = (float)z; } } diff --git a/src/solver/surfqual.c b/src/solver/surfqual.c index d4b0fc49d..3e3908da0 100644 --- a/src/solver/surfqual.c +++ b/src/solver/surfqual.c @@ -60,9 +60,11 @@ static void findWashoffLoads(int j, double runoff); static void findPondedLoads(int j, double tStep); static void findLidLoads(int j, double tStep); -//============================================================================= - -void surfqual_initState(int j) +/*! +* \brief Initializes pollutant buildup, ponded mass, and washoff. +* \param[in] subcatchIndex Subcatchment index +*/ +void surfqual_initState(int subcatchIndex) // // Input: j = subcatchment index // Output: none @@ -74,26 +76,23 @@ void surfqual_initState(int j) // --- initialize washoff quality for (p = 0; p < Nobjects[POLLUT]; p++) { - Subcatch[j].oldQual[p] = 0.0; - Subcatch[j].newQual[p] = 0.0; - Subcatch[j].pondedQual[p] = 0.0; - Subcatch[j].apiExtBuildup[p] = 0.0; + Subcatch[subcatchIndex].oldQual[p] = 0.0; + Subcatch[subcatchIndex].newQual[p] = 0.0; + Subcatch[subcatchIndex].pondedQual[p] = 0.0; + Subcatch[subcatchIndex].apiExtBuildup[p] = 0.0; } // --- initialize pollutant buildup - landuse_getInitBuildup(Subcatch[j].landFactor, Subcatch[j].initBuildup, - Subcatch[j].area, Subcatch[j].curbLength); + landuse_getInitBuildup(Subcatch[subcatchIndex].landFactor, Subcatch[subcatchIndex].initBuildup, + Subcatch[subcatchIndex].area, Subcatch[subcatchIndex].curbLength); } -//============================================================================= - -void surfqual_getBuildup(int j, double tStep) -// -// Input: j = subcatchment index -// tStep = time step (sec) -// Output: none -// Purpose: adds to pollutant buildup on subcatchment surface. -// +/*! +* \brief Adds to pollutant buildup on subcatchment surface. +* \param[in] subcatchIndex Subcatchment index +* \param[in] tStep Time step (sec) +*/ +void surfqual_getBuildup(int subcatchIndex, double tStep) { int i; // land use index int p; // pollutant index @@ -107,45 +106,42 @@ void surfqual_getBuildup(int j, double tStep) for (i = 0; i < Nobjects[LANDUSE]; i++) { // --- skip landuse if not in subcatch - f = Subcatch[j].landFactor[i].fraction; + f = Subcatch[subcatchIndex].landFactor[i].fraction; if ( f == 0.0 ) continue; // --- get land area (in acres or hectares) & curb length - area = f * Subcatch[j].area * UCF(LANDAREA); - curb = f * Subcatch[j].curbLength; + area = f * Subcatch[subcatchIndex].area * UCF(LANDAREA); + curb = f * Subcatch[subcatchIndex].curbLength; // --- examine each pollutant for (p = 0; p < Nobjects[POLLUT]; p++) { // --- see if snow-only buildup is in effect if (Pollut[p].snowOnly - && Subcatch[j].newSnowDepth < 0.001/12.0) continue; + && Subcatch[subcatchIndex].newSnowDepth < 0.001/12.0) continue; // --- use land use's buildup function to update buildup amount - oldBuildup = Subcatch[j].landFactor[i].buildup[p]; + oldBuildup = Subcatch[subcatchIndex].landFactor[i].buildup[p]; newBuildup = landuse_getBuildup(i, p, area, curb, oldBuildup, tStep); newBuildup = MAX(newBuildup, oldBuildup); //--- add bounded building from external API - newBuildup = MAX(0.0, newBuildup + Subcatch[j].apiExtBuildup[p] * area); + newBuildup = MAX(0.0, newBuildup + Subcatch[subcatchIndex].apiExtBuildup[p] * area); - Subcatch[j].landFactor[i].buildup[p] = newBuildup; + Subcatch[subcatchIndex].landFactor[i].buildup[p] = newBuildup; massbal_updateLoadingTotals(BUILDUP_LOAD, p, (newBuildup - oldBuildup)); } } } -//============================================================================= - -void surfqual_sweepBuildup(int j, DateTime aDate) -// -// Input: j = subcatchment index -// aDate = current date/time -// Output: none -// Purpose: reduces pollutant buildup over a subcatchment if sweeping occurs. -// +/*! +* \brief Reduces pollutant buildup over a subcatchment if sweeping occurs. +* \param[in] subcatchIndex Subcatchment index +* \param[in] aDate Current date/time +*/ +void surfqual_sweepBuildup(int subcatchIndex, DateTime aDate) { int i; // land use index int p; // pollutant index @@ -153,36 +149,36 @@ void surfqual_sweepBuildup(int j, DateTime aDate) double newBuildup; // buildup after sweeping (lbs or kg) // --- no sweeping if there is snow on plowable impervious area - if ( Subcatch[j].snowpack != NULL && - Subcatch[j].snowpack->wsnow[IMPERV0] > MIN_TOTAL_DEPTH ) return; + if ( Subcatch[subcatchIndex].snowpack != NULL && + Subcatch[subcatchIndex].snowpack->wsnow[IMPERV0] > MIN_TOTAL_DEPTH ) return; // --- consider each land use for (i = 0; i < Nobjects[LANDUSE]; i++) { // --- skip land use if not in subcatchment - if ( Subcatch[j].landFactor[i].fraction == 0.0 ) continue; + if ( Subcatch[subcatchIndex].landFactor[i].fraction == 0.0 ) continue; // --- see if land use is subject to sweeping if ( Landuse[i].sweepInterval == 0.0 ) continue; // --- see if sweep interval has been reached - if ( aDate - Subcatch[j].landFactor[i].lastSwept >= + if ( aDate - Subcatch[subcatchIndex].landFactor[i].lastSwept >= Landuse[i].sweepInterval ) { // --- update time when last swept - Subcatch[j].landFactor[i].lastSwept = aDate; + Subcatch[subcatchIndex].landFactor[i].lastSwept = aDate; // --- examine each pollutant for (p = 0; p < Nobjects[POLLUT]; p++) { // --- reduce buildup by the fraction available // times the sweeping effic. - oldBuildup = Subcatch[j].landFactor[i].buildup[p]; + oldBuildup = Subcatch[subcatchIndex].landFactor[i].buildup[p]; newBuildup = oldBuildup * (1.0 - Landuse[i].sweepRemoval * Landuse[i].washoffFunc[p].sweepEffic); newBuildup = MIN(oldBuildup, newBuildup); newBuildup = MAX(0.0, newBuildup); - Subcatch[j].landFactor[i].buildup[p] = newBuildup; + Subcatch[subcatchIndex].landFactor[i].buildup[p] = newBuildup; // --- update mass balance totals massbal_updateLoadingTotals(SWEEPING_LOAD, p, @@ -192,24 +188,21 @@ void surfqual_sweepBuildup(int j, DateTime aDate) } } -//============================================================================= - -void surfqual_getWashoff(int j, double runoff, double tStep) -// -// Input: j = subcatchment index -// runoff = total subcatchment runoff before internal re-routing or -// LID controls (ft/sec) -// tStep = time step (sec) -// Output: none -// Purpose: computes new runoff quality for a subcatchment. -// -// Considers three pollutant generating streams that are combined together: -// 1. washoff of pollutant buildup as described by the project's land use -// washoff functions, -// 2. complete mix mass balance of pollutants in surface ponding on -// non-LID area due to runon, wet deposition, infiltration, & evaporation, -// 3. wet deposition and runon over LID areas. -// +/*! +* \brief Computes new runoff quality for a subcatchment. +* \param[in] subcatchIndex Subcatchment index +* \param[in] runoff Total subcatchment runoff before internal re-routing or +* LID controls (ft/sec) +* \param[in] tStep Time step (sec) +* \details +* Considers three pollutant generating streams that are combined together: +* 1. washoff of pollutant buildup as described by the project's land use +* washoff functions, +* 2. complete mix mass balance of pollutants in surface ponding on +* non-LID area due to runon, wet deposition, infiltration, & evaporation, +* 3. wet deposition and runon over LID areas. +*/ +void surfqual_getWashoff(int subcatchIndex, double runoff, double tStep) { int p; // pollutant index int hasOutflow; // TRUE if subcatchment has outflow @@ -223,24 +216,24 @@ void surfqual_getWashoff(int j, double runoff, double tStep) double area; // subcatchment area (ft2) // --- return if there is no area or no pollutants - area = Subcatch[j].area; + area = Subcatch[subcatchIndex].area; if ( Nobjects[POLLUT] == 0 || area == 0.0 ) return; // --- find contributions from washoff, runon and wet precip. to OutflowLoad for (p = 0; p < Nobjects[POLLUT]; p++) OutflowLoad[p] = 0.0; - findWashoffLoads(j, runoff); - findPondedLoads(j, tStep); - findLidLoads(j, tStep); + findWashoffLoads(subcatchIndex, runoff); + findPondedLoads(subcatchIndex, tStep); + findLidLoads(subcatchIndex, tStep); // --- contribution from direct rainfall on LID areas - vLidRain = Subcatch[j].rainfall * Subcatch[j].lidArea * tStep; + vLidRain = Subcatch[subcatchIndex].rainfall * Subcatch[subcatchIndex].lidArea * tStep; // --- contribution from upstream runon onto LID areas // (only if LIDs occupy full subcatchment) vLidRunon = 0.0; - if ( area == Subcatch[j].lidArea ) + if ( area == Subcatch[subcatchIndex].lidArea ) { - vLidRunon = Subcatch[j].runon * area * tStep; + vLidRunon = Subcatch[subcatchIndex].runon * area * tStep; } // --- runoff volume before LID treatment (ft3) @@ -251,7 +244,7 @@ void surfqual_getWashoff(int j, double runoff, double tStep) // --- surface runoff + LID drain flow volume leaving the subcatchment // (Subcatch.newRunoff, computed in subcatch_getRunoff, includes // any surface runoff reduction from LID treatment) - vSurfOut = Subcatch[j].newRunoff * tStep; + vSurfOut = Subcatch[subcatchIndex].newRunoff * tStep; vOut2 = vSurfOut + VlidDrain; // --- determine if subcatchment outflow is below a small cutoff @@ -266,7 +259,7 @@ void surfqual_getWashoff(int j, double runoff, double tStep) // --- assign any difference between pre- and post-LID // subcatchment outflow loads to BMP removal - if ( Subcatch[j].lidArea > 0.0 ) + if ( Subcatch[subcatchIndex].lidArea > 0.0 ) { massLoad = cOut * (vOut1 - vOut2) * Pollut[p].mcf; if (massLoad > 0.0) @@ -275,39 +268,37 @@ void surfqual_getWashoff(int j, double runoff, double tStep) // --- update subcatchment's cumulative runoff load in lbs (or kg) massLoad = cOut * vOut2 * Pollut[p].mcf; - Subcatch[j].totalLoad[p] += massLoad; + Subcatch[subcatchIndex].totalLoad[p] += massLoad; // --- update mass balance for surface runoff load routed to a // conveyance system node // (loads from LID drains are accounted for below since they // can go to different outlets than parent subcatchment) - if ( (Subcatch[j].outNode >= 0 || Subcatch[j].outSubcatch == j) ) + if ( (Subcatch[subcatchIndex].outNode >= 0 || Subcatch[subcatchIndex].outSubcatch == subcatchIndex) ) { massLoad = cOut * vSurfOut * Pollut[p].mcf; massbal_updateLoadingTotals(RUNOFF_LOAD, p, massLoad); } // --- save new washoff concentration - Subcatch[j].newQual[p] = cOut / LperFT3; + Subcatch[subcatchIndex].newQual[p] = cOut / LperFT3; } // --- add contribution of LID drain flows to mass balance - if ( Subcatch[j].lidArea > 0.0 ) + if ( Subcatch[subcatchIndex].lidArea > 0.0 ) { - lid_addDrainLoads(j, Subcatch[j].newQual, tStep); + lid_addDrainLoads(subcatchIndex, Subcatch[subcatchIndex].newQual, tStep); } } -//============================================================================= - +/*! +* \brief Finds wtd. combination of old and new washoff for a pollutant. +* \param[in] subcatchIndex Subcatchment index +* \param[in] pollutIndex Pollutant index +* \param[in] wt Weighting factor +* \return Returns pollutant washoff value +*/ double surfqual_getWtdWashoff(int j, int p, double f) -// -// Input: j = subcatchment index -// p = pollutant index -// f = weighting factor -// Output: returns pollutant washoff value -// Purpose: finds wtd. combination of old and new washoff for a pollutant. -// { return (1.0 - f) * Subcatch[j].oldRunoff * Subcatch[j].oldQual[p] + f * Subcatch[j].newRunoff *Subcatch[j].newQual[p]; diff --git a/src/solver/swmm5.c b/src/solver/swmm5.c index ddff40f07..beb557c7e 100644 --- a/src/solver/swmm5.c +++ b/src/solver/swmm5.c @@ -1,67 +1,85 @@ -//----------------------------------------------------------------------------- -// swmm5.c -// -// Project: EPA SWMM5 -// Version: 5.2 -// Date: 11/01/21 (Build 5.2.0) -// Author: L. Rossman -// -// This is the main module of the computational engine for Version 5 of -// the U.S. Environmental Protection Agency's Storm Water Management Model -// (SWMM). It contains functions that control the flow of computations. -// -// This engine should be compiled into a shared object library whose API -// functions are listed in swmm5.h. -// -// Update History -// ============== -// Build 5.1.008: -// - Support added for the MinGW compiler. -// - Reporting of project options moved to swmm_start. -// - Hot start file now read before routing system opened. -// - Final routing step adjusted so that total duration not exceeded. -// Build 5.1.011: -// - Made sure that MS exception handling only used with MS C compiler. -// - Added name of module handling an exception to error report. -// - Elapsed simulation time now saved to new global variable ElaspedTime. -// - Added swmm_getError() function that retrieves error code and message. -// - Changed WarningCode to Warnings (# warnings issued). -// - Added swmm_getWarnings() function to retrieve value of Warnings. -// - Fixed error code returned on swmm_xxx functions. -// Build 5.1.012: -// - #include only used when compiled for Windows. -// Build 5.1.013: -// - Support added for saving average results within a reporting period. -// - SWMM engine now always compiled to a shared object library. -// Build 5.1.015: -// - Fixes bug in summary statistics when Report Start date > Start Date. -// Build 5.2.0: -// - Added additional API functions. -// - Set max. number of open files to 8192. -// - Changed getElapsedTime function to use report start as base date/time. -// - Prevented possible infinite loop if swmm_step() called when ErrorCode > 0. -// - Prevented early exit from swmm_end() when ErrorCode > 0. -// - Support added for relative file names. -// Build 5.3.0: -// - Added support for saving hot start files at specific times. -// - Expanded SWMM api to save and use prescribed hotstart files. -// - Expansions to the SWMM API to include attributes of more objects and water quality. -//----------------------------------------------------------------------------- +/*! + * \file swmm5.c + * \brief Main module of the computational engine for Version 5 of the + * U.S. Environmental Protection Agency's Storm Water Management Model (SWMM). + * \author L. Rossman + * \date Created: 2021-11-01 + * \date Last edited: 2024-12-23 + * \version 5.3 + * \details This is the main module of the computational engine for Version 5 of + * the U.S. Environmental Protection Agency's Storm Water Management Model + * (SWMM). It contains functions that control the flow of computations. + * + * This engine should be compiled into a shared object library whose API + * functions are listed in swmm5.h. + * + * Update History + * ============== + * Build 5.1.008: + * - Support added for the MinGW compiler. + * - Reporting of project options moved to swmm_start. + * - Hot start file now read before routing system opened. + * - Final routing step adjusted so that total duration not exceeded. + * Build 5.1.011: + * - Made sure that MS exception handling only used with MS C compiler. + * - Added name of module handling an exception to error report. + * - Elapsed simulation time now saved to new global variable ElaspedTime. + * - Added swmm_getError() function that retrieves error code and message. + * - Changed WarningCode to Warnings (# warnings issued). + * - Added swmm_getWarnings() function to retrieve value of Warnings. + * - Fixed error code returned on swmm_xxx functions. + * Build 5.1.012: + * - #include only used when compiled for Windows. + * Build 5.1.013: + * - Support added for saving average results within a reporting period. + * - SWMM engine now always compiled to a shared object library. + * Build 5.1.015: + * - Fixes bug in summary statistics when Report Start date > Start Date. + * Build 5.2.0: + * - Added additional API functions. + * - Set max. number of open files to 8192. + * - Changed getElapsedTime function to use report start as base date/time. + * - Prevented possible infinite loop if swmm_step() called when ErrorCode > 0. + * - Prevented early exit from swmm_end() when ErrorCode > 0. + * - Support added for relative file names. + * Build 5.3.0: + * - Added support for saving hot start files at specific times. + * - Expanded SWMM api to save and use prescribed hotstart files. + * - Expansions to the SWMM API to include attributes of more objects and water quality. + */ + +/*! +* \def _CRT_SECURE_NO_DEPRECATE +* \brief Define to prevent deprecation warnings from MS Visual C++ compilers +*/ #define _CRT_SECURE_NO_DEPRECATE // --- define WINDOWS #undef WINDOWS #ifdef _WIN32 + +/*! + * \def WINDOWS + * \brief Define for Windows platform + */ #define WINDOWS #endif #ifdef __WIN32__ + +/*! + * \def WINDOWS + * \brief Define for Windows platform + */ #define WINDOWS #endif -// --- define EXH (MS Windows exception handling) #undef EXH // indicates if exception handling included #ifdef WINDOWS #ifdef _MSC_VER +/*! + * \def EXH + * \brief Define for MS Windows exception handling + */ #define EXH #endif #endif @@ -89,6 +107,10 @@ #if defined(_OPENMP) #include #else +/*! +* \brief Dummy function for getting threads available for OpenMP to prevent compiler errors. +* \return 1 +*/ int omp_get_max_threads(void) { return 1; } #endif @@ -100,7 +122,13 @@ int omp_get_max_threads(void) { return 1; } //----------------------------------------------------------------------------- #include "macros.h" // macros used throughout SWMM #include "objects.h" // definitions of SWMM's data objects + +/*! +* \def EXTERN +* \brief Define for 'extern' in headers.h +*/ #define EXTERN // defined as 'extern' in headers.h + #include "globals.h" // declaration of all global variables #include "funcs.h" // declaration of all global functions #include "error.h" // error message codes @@ -108,11 +136,23 @@ int omp_get_max_threads(void) { return 1; } #include "swmm5.h" // declaration of SWMM's API functions -#define MAX_EXCEPTIONS 100 // max. number of exceptions handled - -//----------------------------------------------------------------------------- -// Unit conversion factors -//----------------------------------------------------------------------------- +/*! +* \def MAX_EXCEPTIONS +* \brief Maximum number of exceptions handled +*/ +#define MAX_EXCEPTIONS 100 + +/*! +* \addtogroup UnitConversionFactors Unit Conversion Factors +* \brief Conversion factors for units used in SWMM. +* \ingroup SWMM_Constants +* \{ +*/ + +/*! +* \var Ucf +* \brief Unit conversion factors for different units used in SWMM. +*/ const double Ucf[10][2] = { // US SI @@ -127,87 +167,279 @@ const double Ucf[10][2] = {2.203e-6, 1.0e-6}, // MASS (lb, kg --> mg) {43560.0, 3048.0} // GWFLOW (cfs/ac, cms/ha --> ft/sec) }; + +/*! +* \var Qcf +* \brief Flow conversion factors for different flow units used in SWMM. +*/ const double Qcf[6] = // Flow Conversion Factors: {1.0, 448.831, 0.64632, // cfs, gpm, mgd --> cfs 0.02832, 28.317, 2.4466}; // cms, lps, mld --> cfs - -//----------------------------------------------------------------------------- -// Shared variables -//----------------------------------------------------------------------------- +/*! +* \} +*/ + +/*! +* \addtogroup SWMM_API_Shared_Variable SWMM Shared Variables +* \brief SWMM API shared variables. +* \ingroup SWMM_Constants +* \{ +*/ + +/*! +* \var IsOpenFlag +* \brief TRUE if a project has been opened. +*/ static int IsOpenFlag; // TRUE if a project has been opened + +/*! +* \var IsStartedFlag +* \brief TRUE if a simulation has been started. +*/ static int IsStartedFlag; // TRUE if a simulation has been started + +/*! +* \var SaveResultsFlag +* \brief TRUE if output to be saved to binary file. +*/ static int SaveResultsFlag; // TRUE if output to be saved to binary file + +/*! +* \var ExceptionCount +* \brief Number of exceptions handled. +*/ static int ExceptionCount; // number of exceptions handled + +/*! +* \var DoRunoff +* \brief TRUE if runoff is computed. +*/ static int DoRunoff; // TRUE if runoff is computed -static int DoRouting; // TRUE if flow routing is computed -static double RoutingDuration; // duration of a set of routing steps (msecs) -//----------------------------------------------------------------------------- -// External API functions (prototyped in swmm5.h) -//----------------------------------------------------------------------------- -// swmm_run -// swmm_open -// swmm_start -// swmm_step -// swmm_end -// swmm_report -// swmm_close -// swmm_getMassBalErr -// swmm_getVersion -// swmm_getError -// swmm_getWarnings -// swmm_getCount -// swmm_getIDname -// swmm_getIndex -// swmm_getStartNode -// swmm_getEndNode -// swmm_getValue -// swmm_setValue -// swmm_getSavedValue -// swmm_writeLine -// swmm_decodeDate +/*! +* \var DoRouting +* \brief TRUE if flow routing is computed. +*/ +static int DoRouting; // TRUE if flow routing is computed -//----------------------------------------------------------------------------- -// Local functions -//----------------------------------------------------------------------------- +/*! +* \var RoutingDuration +* \brief Duration of a set of routing steps (msecs). +*/ +static double RoutingDuration; // duration of a set of routing steps (msecs) +/*! +* \} +*/ + +/*! +* \addtogroup SWMM_API_Local_Functions SWMM Local Functions +* \brief Local functions used by the SWMM API functions. +* \ingroup SWMM_Constants +* \{ +*/ + +/*! +* \brief Routes flow and WQ through drainage system over a single time step. +*/ static void execRouting(void); + +/*! +* \brief Saves current results to binary output file. +*/ static void saveResults(void); + +/*! +* \brief Retrieve rain gage value given its index and property type. +* \param[in] index Object index +* \param[in] property Property type +* \return Property value +*/ static double getGageValue(int index, int property); + +/*! +* \brief Retrieve subcatchment value given its index, property type, and subindex. +* \param[in] index Object index +* \param[in] property Property type +* \param[in] subIndex Subindex +* \return Property value +*/ static double getSubcatchValue(int property, int index, int subIndex); + +/*! +* \brief Retrieve node value given its index, property type, and subindex. +* \param[in] index Object index +* \param[in] property Property type +* \param[in] subIndex Subindex +* \return Property value +*/ static double getNodeValue(int property, int index, int subIndex); + +/*! +* \brief Retrieve link value given its index, property type, and subindex. +* \param[in] index Object index +* \param[in] property Property type +* \param[in] subIndex Subindex +* \return Property value +*/ static double getLinkValue(int property, int index, int subIndex); + +/*! +* \brief Retrieve saved date value given a period. +* \param[in] period Period +* \return Date value +*/ static double getSavedDate(int period); + +/*! +* \brief Retrieve saved value of a subcatchment, node, or link given its index, property type, and period. +* \param[in] property Property type +* \param[in] index Object index +* \param[in] period Period +* \return Property value +*/ static double getSavedSubcatchValue(int property, int index, int period); + +/*! +* \brief Retrieve saved value of a node given its index, property type, and period. +* \param[in] property Property type +* \param[in] index Object index +* \param[in] period Period +* \return Property value +*/ static double getSavedNodeValue(int property, int index, int period); + +/*! +* \brief Retrieve saved value of a link given its index, property type, and period. +* \param[in] property Property type +* \param[in] index Object index +* \param[in] period Period +* \return Property value +*/ static double getSavedLinkValue(int property, int index, int period); + +/*! +* \brief Retrieve system value given its property type. +* \param[in] property Property type +* \return Property value +*/ static double getSystemValue(int property); + +/*! +* \brief Retrieve the maximum routing step. +* \return Maximum routing step +*/ static double getMaxRouteStep(); + +/*! +* \brief Set gage value given its property type, index, subindex, and value. +* \param[in] property Property type +* \param[in] index Object index +* \param[in] subIndex Subindex +* \param[in] value Property value +* \return Error code +*/ static int setGageValue(int property, int index, int subIndex, double value); + +/*! +* \brief Set subcatchment value given its property type, index, subindex, and value. +* \param[in] property Property type +* \param[in] index Object index +* \param[in] subIndex Subindex +* \param[in] value Property value +* \return Error code +*/ static int setSubcatchValue(int property, int index, int subIndex, double value); + +/*! +* \brief Set node value given its property type, index, subindex, and value. +* \param[in] property Property type +* \param[in] index Object index +* \param[in] subIndex Subindex +* \param[in] value Property value +* \return Error code +*/ static int setNodeValue(int property, int index, int subIndex, double value); + +/*! +* \brief Set link value given its property type, index, subindex, and value. +* \param[in] property Property type +* \param[in] index Object index +* \param[in] subIndex Subindex +* \param[in] value Property value +* \return Error code +*/ static int setLinkValue(int property, int index, int subIndex, double value); + +/*! +* \brief Set node lateral inflow value given its index and value. +* \param[in] index Object index +* \param[in] value Property value +* \return Error code +*/ static int setNodeLatFlow(int index, double value); + +/*! +* \brief Set outfall stage value given its index and value. +* \param[in] index Object index +* \param[in] value Property value +* \return Error code +*/ static int setOutfallStage(int index, double value); + +/*! +* \brief Set link setting value given its index and value. +* \param[in] index Object index +* \param[in] value Property value +* \return Error code +*/ static int setLinkSetting(int index, double value); + +/*! +* \brief Set routing step value. +* \param[in] value Property value +* \return Error code +*/ static int setRoutingStep(double value); + +/*! +* \brief Set system value given its property type and value. +* \param[in] property Property type +* \param[in] value Property value +* \return Error code +*/ static int setSystemValue(int property, double value); + +/*! +* \brief Get absolute path of a file. +* \param[in] fname File name +* \param[out] absPath Absolute path +* \param[in] size Size of the absPath array +*/ static void getAbsolutePath(const char *fname, char *absPath, size_t size); +/*! +* \} +*/ -// Exception filtering function #ifdef EXH +/*! +* \brief Exception filter function +* \param[in] xc Exception code +* \param[in] module Module name +* \param[in] elapsedTime Elapsed time +* \param[in] step Step +* \return Error code +*/ static int xfilter(int xc, char *module, double elapsedTime, long step); #endif -//============================================================================= - +/*! +* \brief Run a SWMM simulation with the given input file, report file, and output file. +* \param[in] inputFile Path to the input file +* \param[in] reportFile Path to the report file +* \param[in] outputFile Path to the output file +* \return Error code +*/ int DLLEXPORT swmm_run(const char *inputFile, const char *reportFile, const char *outputFile) -// -// Input: inputFile = name of input file -// reportFile = name of report file -// outputFile = name of binary output file -// Output: returns error code -// Purpose: runs a SWMM simulation. -// { long newHour, oldHour = 0; long theDay, theHour; @@ -268,17 +500,15 @@ int DLLEXPORT swmm_run(const char *inputFile, const char *reportFile, const char return ErrorCode; } -//============================================================================= - +/*! +* \brief Run a SWMM simulation with the given input file, report file, output file, and progress callback function. +* \param[in] inputFile Path to the input file +* \param[in] reportFile Path to the report file +* \param[in] outputFile Path to the output file +* \param[in] callback Progress callback function +* \return Error code +*/ int DLLEXPORT swmm_run_with_callback(const char *inputFile, const char *reportFile, const char *outputFile, progress_callback callback) -// -// Input: inputFile = name of input file -// reportFile = name of report file -// outputFile = name of binary output file -// callback = function pointer to a progress callback function -// Output: returns error code -// Purpose: runs a SWMM simulation. -// { double progress = 0.0, elapsedTime = 0.0; @@ -331,16 +561,14 @@ int DLLEXPORT swmm_run_with_callback(const char *inputFile, const char *reportFi return ErrorCode; } -//============================================================================= - +/*! +* \brief Open a SWMM project. +* \param[in] inputFile Path to the input file +* \param[in] reportFile Path to the report file +* \param[in] outputFile Path to the output file +* \return Error code +*/ int DLLEXPORT swmm_open(const char *inputFile, const char *reportFile, const char *outputFile) -// -// Input: inputFile = name of input file -// reportFile = name of report file -// outputFile = name of binary output file -// Output: returns error code -// Purpose: opens a SWMM project. -// { // --- to be safe, reset the state of the floating point unit #ifdef WINDOWS @@ -391,14 +619,12 @@ int DLLEXPORT swmm_open(const char *inputFile, const char *reportFile, const cha return ErrorCode; } -//============================================================================= - +/*! +* \brief Start a SWMM simulation. +* \param[in] saveResults TRUE if simulation results saved to binary file +* \return Error code +*/ int DLLEXPORT swmm_start(int saveResults) -// -// Input: saveResults = TRUE if simulation results saved to binary file -// Output: returns an error code -// Purpose: starts a SWMM simulation. -// { // --- check that a project is open & no run started if (ErrorCode) @@ -498,8 +724,11 @@ int DLLEXPORT swmm_start(int saveResults) return ErrorCode; } -//============================================================================= - +/*! +* \brief Perform a SWMM simulation step and return the elapsed time. +* \param[out] elapsedTime Elapsed time +* \return Error code +*/ int DLLEXPORT swmm_step(double *elapsedTime) // // Input: elapsedTime = current elapsed time in decimal days @@ -558,15 +787,13 @@ int DLLEXPORT swmm_step(double *elapsedTime) return ErrorCode; } -//============================================================================= - +/*! +* \brief Perform a SWMM simulation step with a stride step and return the elapsed time. +* \param[in] strideStep Stride step +* \param[out] elapsedTime Elapsed time +* \return Error code +*/ int DLLEXPORT swmm_stride(int strideStep, double *elapsedTime) -// -// Input: strideStep = number of seconds to advance the simulation -// elapsedTime = current elapsed time in decimal days -// Output: updated value of elapsedTime, -// returns error code -// Purpose: advances the simulation by a fixed number of seconds. { double realRouteStep = RouteStep; @@ -608,14 +835,14 @@ int DLLEXPORT swmm_stride(int strideStep, double *elapsedTime) return ErrorCode; } -//============================================================================= - +/*! +* \brief Set hotstart file for SWMM simulation. +* \details Sets the hotstart file to use for simulation. Errors does not terminate simulation unless +* there is a prior terminating error. +* \param[in] hotStartFile Path to the hotstart file +* \return Error code +*/ int DLLEXPORT swmm_useHotStart(const char *hotStartFile) -// -// Input: hotStartFile = filepath to hotstart file to use -// Output: returns error code -// Purpose: Sets the hotstart file to use for simulation. Errors does not terminate simulation unless -// there is a prior terminating error. { int errorCode = 0; int fileVersion = 0; @@ -645,13 +872,12 @@ int DLLEXPORT swmm_useHotStart(const char *hotStartFile) return errorCode; } -//============================================================================= - +/*! +* \brief Save hotstart file for SWMM simulation at current time. +* \param[in] hotStartFile Path to the hotstart file +* \return Error code +*/ int DLLEXPORT swmm_saveHotStart(const char *hotStartFile) -// -// Input: hotStartFile = filepath to hotstart file to save -// Output: returns error code -// Purpose: Saves hotstart file at the current simulation time { int errorCode = 0; @@ -667,14 +893,10 @@ int DLLEXPORT swmm_saveHotStart(const char *hotStartFile) return errorCode; } -//============================================================================= - +/*! +* \brief Routes flow and WQ through drainage system over a single time step. +*/ void execRouting() -// -// Input: none -// Output: none -// Purpose: routes flow & WQ through drainage system over a single time step. -// { double nextRoutingTime; // updated elapsed routing time (msec) double routingStep; // routing time step (sec) @@ -737,13 +959,10 @@ void execRouting() #endif } -//============================================================================= - +/*! +* \brief Saves current results to binary output file. +*/ void saveResults() -// -// Input: none -// Output: none -// Purpose: saves current results to binary output file. { if (NewRoutingTime >= ReportTime) { @@ -778,14 +997,11 @@ void saveResults() output_updateAvgResults(); } -//============================================================================= - +/*! +* \brief End a SWMM simulation. +* \return Error code +*/ int DLLEXPORT swmm_end(void) -// -// Input: none -// Output: none -// Purpose: ends a SWMM simulation. -// { // --- check that project opened and run started if (!IsOpenFlag) @@ -819,41 +1035,32 @@ int DLLEXPORT swmm_end(void) return ErrorCode; } -//============================================================================= - +/*! +* \brief Writes simulation results to the report file. +* \return Error code +*/ int DLLEXPORT swmm_report() -// -// Input: none -// Output: returns an error code -// Purpose: writes simulation results to the report file. -// { if (!ErrorCode) report_writeReport(); return ErrorCode; } -//============================================================================= - +/*! +* \brief Write a line of text to the SWMM report file. +* \param[in] line Line of text +*/ void DLLEXPORT swmm_writeLine(const char *line) -// -// Input: line = a character string -// Output: returns an error code -// Purpose: writes a line of text to the report file. -// { if (IsOpenFlag) report_writeLine(line); } -//============================================================================= - +/*! +* \brief Close a SWMM simulation. +* \return Error code +*/ int DLLEXPORT swmm_close() -// -// Input: none -// Output: returns an error code -// Purpose: closes a SWMM project. -// { if (Fout.file) output_close(); @@ -875,18 +1082,15 @@ int DLLEXPORT swmm_close() return 0; } -//============================================================================= - +/*! +* \brief Get the mass balance errors for a SWMM simulation. +* \param[out] runoffErr Runoff error (percent) +* \param[out] flowErr Flow error (percent) +* \param[out] qualErr Quality error (percent) +* \return Error code +*/ int DLLEXPORT swmm_getMassBalErr(float *runoffErr, float *flowErr, float *qualErr) -// -// Input: none -// Output: runoffErr = runoff mass balance error (percent) -// flowErr = flow routing mass balance error (percent) -// qualErr = quality routing mass balance error (percent) -// returns an error code -// Purpose: reports a simulation's mass balance errors. -// { *runoffErr = 0.0; *flowErr = 0.0; @@ -901,41 +1105,36 @@ int DLLEXPORT swmm_getMassBalErr(float *runoffErr, float *flowErr, return 0; } -//============================================================================= - +/*! +* \brief Gets the version of the SWMM engine. +* \details Retrieves the version number of the current SWMM engine which uses a +format of xyzzz where x = major version number, y = minor version number, and +zzz = build number. +* \note Each New Release should be updated in consts.h +* \return SWMM engine version number +*/ int DLLEXPORT swmm_getVersion() -// -// Input: none -// Output: returns SWMM engine version number -// Purpose: retrieves version number of current SWMM engine which -// uses a format of xyzzz where x = major version number, -// y = minor version number, and zzz = build number. -// -// NOTE: Each New Release should be updated in consts.h { return VERSION; } -//============================================================================= - +/*! +* \brief Gets the number of warnings issued during a simulation. +* \return Number of warning messages issued +*/ int DLLEXPORT swmm_getWarnings() -// -// Input: none -// Output: returns number of warning messages issued. -// Purpose: retrieves number of warning messages issued during an analysis. { return Warnings; } -//============================================================================= - +/*! +* \brief Retrieves the code number and text of the error condition that +* caused SWMM to abort its analysis. +* \param[out] errMsg Error message text +* \param[in] msgLen Maximum size of errMsg +* \return Error message code number +*/ int DLLEXPORT swmm_getError(char *errMsg, int msgLen) -// -// Input: errMsg = character array to hold error message text -// msgLen = maximum size of errMsg -// Output: returns error message code number and text of error message. -// Purpose: retrieves the code number and text of the error condition that -// caused SWMM to abort its analysis. { // --- copy text of last error message into errMsg if (ErrorCode > 0 && strlen(ErrorMsg) == 0) @@ -949,14 +1148,13 @@ int DLLEXPORT swmm_getError(char *errMsg, int msgLen) return ErrorCode; } -//============================================================================= - +/*! +* \brief Retrieves the text of the error message that corresponds to the error code number. +* \param[in] errorCode Error code number +* \param[out] outErrMsg Error message text +* \return Error code +*/ int DLLEXPORT swmm_getErrorFromCode(int errorCode, char *outErrMsg[1024]) -// -// Input: errorCode = error code number -// Output: outErrMsg = error message text -// Purpose: retrieves the text of the error message that corresponds to the -// error code number. { char err_msg[MAXMSG]; error_getMsg(errorCode, err_msg); @@ -965,13 +1163,12 @@ int DLLEXPORT swmm_getErrorFromCode(int errorCode, char *outErrMsg[1024]) return 0; } -//============================================================================= - +/*! +* \brief Retrieves the number of objects of a specific type. +* \param[in] objType Type of SWMM object +* \return Number of objects or error code +*/ int DLLEXPORT swmm_getCount(int objType) -// -// Input: objType = a type of SWMM object -// Output: returns the number of objects or error code; -// Purpose: retrieves the number of objects of a specific type. { if (!IsOpenFlag) return ERR_API_NOT_OPEN; @@ -980,17 +1177,15 @@ int DLLEXPORT swmm_getCount(int objType) return Nobjects[objType]; } -//============================================================================= - +/*! +* \brief Retrieves the ID name of an object. +* \param[in] objType Type of SWMM object +* \param[in] index Object index +* \param[out] name Object name +* \param[in] size Size of the name array +* \return Error code +*/ int DLLEXPORT swmm_getName(int objType, int index, char *name, int size) -// -// Input: objType = a type of SWMM object -// index = the object's index in the array of objects -// name = a character array -// size = size of the name array -// Output: name = the object's ID name; -// error code -// Purpose: retrieves the ID name of an object. { char *idName = NULL; @@ -1049,14 +1244,13 @@ int DLLEXPORT swmm_getName(int objType, int index, char *name, int size) return 0; } -//============================================================================= - +/*! +* \brief Retrieves the index of a named object. +* \param[in] objType Type of SWMM object +* \param[in] name Object name +* \return Object index or error code +*/ int DLLEXPORT swmm_getIndex(int objType, const char *name) -// -// Input: objType = a type of SWMM object -// name = the object's ID name -// Output: returns the object's position in the array of like objects or error code; -// Purpose: retrieves the index of a named object. { if (!IsOpenFlag) return ERR_API_NOT_OPEN; @@ -1065,16 +1259,15 @@ int DLLEXPORT swmm_getIndex(int objType, const char *name) return project_findObject(objType, name); } -//============================================================================= - +/*! +* \brief Get the value of a property for an object of a given property in the SWMM model. +* \param[in] property Property type +* \param[in] index Object index +* \return Property value +* \deprecated Use swmm_getValueExpanded instead. Function will be changed to +* swmm_getValueExpanded in future versions. +*/ double DLLEXPORT swmm_getValue(int property, int index) -// -// Input: property = an object's property code -// index = the object's index in the array of like objects -// -// Output: returns the property's current value -// Purpose: retrieves the value of an object's property. -// TODO: Will be deprecated in SWMM version 6.0. Use swmm_getValueExpanded instead. { if (!IsOpenFlag) return ERR_API_NOT_OPEN; @@ -1092,16 +1285,15 @@ double DLLEXPORT swmm_getValue(int property, int index) return ERR_API_PROPERTY_TYPE; } -//============================================================================= - +/*! +* \brief Get the value of a property for an object of a given property in the SWMM model. +* \param[in] objType Type of SWMM object +* \param[in] property Property type +* \param[in] index Object index in the array of like objects +* \param[in] subIndex Subindex +* \return Property value +*/ double DLLEXPORT swmm_getValueExpanded(int objType, int property, int index, int subIndex) -// -// Input: property = an object's property code -// index = the object's index in the array of like objects -// subIndex = an index of the object's property -// -// Output: returns the property's current value -// Purpose: retrieves the value of an object's property. { if (!IsOpenFlag) return ERR_API_NOT_OPEN; @@ -1123,17 +1315,16 @@ double DLLEXPORT swmm_getValueExpanded(int objType, int property, int index, int } } -//============================================================================= - +/*! +* \brief Set the value of a property for an object of a given property in the SWMM model. +* \param[in] property Property type +* \param[in] index Object index in the array of like objects +* \param[in] value Property value +* \return Error code +* \deprecated Use swmm_setValueExpanded instead. Function will be changed to +* swmm_setValueExpanded in future versions. +*/ int DLLEXPORT swmm_setValue(int property, int index, double value) -// -// Input: property = an object's property code -// index = the object's index in the array of like objects -// value = the property's new value -// Output: returns error code -// Purpose: sets the value of an object's property. -// TODO: Will be deprecated in SWMM version 6.0. Use swmm_setValueExpanded instead, -// which will be renamed to swmm_setValue. { if (!IsOpenFlag) diff --git a/src/solver/table.c b/src/solver/table.c index 3215f4c8a..557d5521a 100644 --- a/src/solver/table.c +++ b/src/solver/table.c @@ -303,11 +303,17 @@ int table_validate(TTable *table) if ( table->file.file == NULL ) return ERR_TABLE_FILE_OPEN; } + if (strcomp("CVG", table->ID)) + { + printf("Test"); + } + // --- retrieve the first data entry in the table result = table_getFirstEntry(table, &x1, &y1); // --- return error condition if external file has no valid data - if ( !result && table->file.mode == USE_FILE ) return ERR_TABLE_FILE_READ; + if ( !result && table->file.mode == USE_FILE ) + return ERR_TABLE_FILE_READ; // --- retrieve successive table entries and check for non-increasing x-values while ( table_getNextEntry(table, &x2, &y2) ) @@ -845,10 +851,11 @@ int table_parseFileLine(char* line, TTable* table, double* x, double* y) // Purpose: parses a line of time series data from an external file. // { - int n; - char s1[50], - s2[50], - s3[50]; + int n = 0; + char *s1, + *s2, + *s3; + char* tStr; // time as string char* yStr; // value as string double yy; // value as double @@ -856,11 +863,31 @@ int table_parseFileLine(char* line, TTable* table, double* x, double* y) DateTime t; // time portion of date/time value // --- return if line is blank or is a comment - tStr = strtok(line, SEPSTR); + tStr = strtok(line, TBLSEPSTR); if ( tStr == NULL || *tStr == ';' ) return -1; - // --- get 3 string tokens from line and check if its a comment - n = sscanf(line, "%s %s %s", s1, s2, s3); + + // --- get 3 string tokens from line + while (tStr != NULL) + { + switch (n) + { + case 0: + s1 = tStr; + n++; + break; + case 1: + s2 = tStr; + n++; + break; + case 2: + s3 = tStr; + n++; + break; + } + + tStr = strtok(NULL, TBLSEPSTR); + } // --- line only has a time and a value if ( n == 2 ) diff --git a/src/solver/toposort.c b/src/solver/toposort.c index d6c9116cd..b6e3fe995 100644 --- a/src/solver/toposort.c +++ b/src/solver/toposort.c @@ -52,14 +52,12 @@ static void findSpanningTree(int startNode); static void evalLoop(int startLink); static int traceLoop(int i1, int i2, int k); static void checkDummyLinks(void); -//============================================================================= +/*! +* \brief Sorts links in topological order for flow routing. +* \param[in, out] sortedLinks Array of link indices +*/ void toposort_sortLinks(int sortedLinks[]) -// -// Input: none -// Output: sortedLinks = array of link indexes in sorted order -// Purpose: sorts links from upstream to downstream. -// { int i, n = 0; diff --git a/src/solver/treatmnt.c b/src/solver/treatmnt.c index f47a1a536..3638cd31c 100644 --- a/src/solver/treatmnt.c +++ b/src/solver/treatmnt.c @@ -61,14 +61,11 @@ static int getVariableIndex(char* s); static double getVariableValue(int varCode); -//============================================================================= - +/*! +* \brief Allocates memory for computing pollutant removals by treatment. +* \return Returns True if successful, False if not +*/ int treatmnt_open(void) -// -// Input: none -// Output: returns TRUE if successful, FALSE if not -// Purpose: allocates memory for computing pollutant removals by treatment. -// { R = NULL; Cin = NULL; @@ -85,28 +82,22 @@ int treatmnt_open(void) return TRUE; } -//============================================================================= - +/*! +* \brief Frees memory used for computing pollutant removals by treatment. +*/ void treatmnt_close(void) -// -// Input: none -// Output: returns an error code -// Purpose: frees memory used for computing pollutant removals by treatment. -// { FREE(R); FREE(Cin); } -//============================================================================= - +/*! +* \brief Reads a treatment expression from a tokenized line of input. +* \param[in] tok Array of string tokens +* \param[in] ntoks Number of tokens +* \return Error code +*/ int treatmnt_readExpression(char* tok[], int ntoks) -// -// Input: tok[] = array of string tokens -// ntoks = number of tokens -// Output: returns an error code -// Purpose: reads a treatment expression from a tokenized line of input. -// { char s[MAXLINE+1]; char* expr; @@ -160,9 +151,11 @@ int treatmnt_readExpression(char* tok[], int ntoks) return 0; } -//============================================================================= - -void treatmnt_delete(int j) +/*! +* \brief Deletes the treatment objects for each pollutant at a node. +* \param[in] nodeIndex Node index +*/ +void treatmnt_delete(int nodeIndex) // // Input: j = node index // Output: none @@ -170,25 +163,21 @@ void treatmnt_delete(int j) // { int p; - if ( Node[j].treatment ) + if ( Node[nodeIndex].treatment ) { for (p=0; p 0.0 ) @@ -197,17 +186,14 @@ void treatmnt_setInflow(double qIn, double wIn[]) for (p = 0; p < Nobjects[POLLUT]; p++) Cin[p] = 0.0; } -//============================================================================= - +/*! +* \brief Updates pollutant concentrations at a node after treatment. +* \param[in] nodeIndex Node index +* \param[in] q Inflow to node (cfs) +* \param[in] v Volume of node (ft3) +* \param[in] tStep Routing time step (sec) +*/ void treatmnt_treat(int j, double q, double v, double tStep) -// -// Input: j = node index -// q = inflow to node (cfs) -// v = volume of node (ft3) -// tStep = routing time step (sec) -// Output: none -// Purpose: updates pollutant concentrations at a node after treatment. -// { int p; // pollutant index double cOut; // concentration after treatment