Feature wrapper (#136)
this quite sizable commit does several things, but is primarily focussed on building a toolkit that can run simultaneous simulations/analyses within a shared memory space. Versions <=2.1 use a long list of global variables that prevent multiple instantiations on linux systems without resorting to compilation tricks (like duplicate binaries or similar via static linkage). This version uses a single "Project" pointer to encapsulate the network and analysis data. There are no changes to existing algo implementations other than to accomodate dereferencing of the passed-in pointers. A more detailed list of major changes below: - mirrors all “ENxxxx” function calls with “EN_xxxx” versions (note the underscore) that take an extra first parameter: a pointer to an EN_Project struct, which contains all network, hydraulic, quality, and associated data. - tweaks some code formatting to make it more readable - removes some deprecated/commented code that was sufficiently old - fixes implicit type-cast warnings * Added ENaddnode and ENaddlink functions * More memory reallocations * Added ENInit, ENsetheadcurveindex * Added ENdeletelink and ENdeletenode * restored default behavior for float types * fixed type * Added docstrings for ENinit * cleanup change * moves global rule variables to vars.h * migrates rule structs to typedefs for better readability * char types to proper enums fixes #93 * Change some variable declarations for compatibility Changes to keep compatibility with C89 compilers: variables must be declared at the top of the functions. Remove the use of EN_LinkType in function call as it is not compatible with ENgetnodetype. * Moved declaration of idstodelete to top of function * Updated ENinit function and headers Updated header files with new functions Updated def file with new functions For ENinit changes names of parameters #98 Added enum for headloss formula * Missed these files in 1a033fc * migrates char types to enums fixes #93, supports unified link/node type enums, rather than public/private redefinitions * removing links in reverse-index order maintains proper indexing fixes #96 * style * clarifies curve getter units issue (dox) closes #95 * fixes link/node confusion in ENsetlinktype partially reverts a3bce95dc330a5a297634a303d438e2e1bc41cc9 * partial compilation fix * fixes dox issue * fixes allocation issues with enums - updates style in various places - introduces FlowDirection enum - use snprintf to prevent overflow * fixes enum type cast * updated mac project settings * Use of _snprintf on Windows and remove DLLEXPORT from mempool.h snprintf it not compatible on Windows so we use _snprintf mempool gave starnge compilation errors while removing DLLEXPORT worked. Not sure why these functions needed to be exposed in the DLL? * Revert "Use of _snprintf on Windows and remove DLLEXPORT from mempool.h" This reverts commit 6238f77d47fa0feaabe5836043c006937de433a2. * use of _snprintf instead of snprintf on Windows and removed DLLEXPORT from mempool.h Had compilation errors on mempool.h. Removed DLLEXPORT so solve it. Not sure why there was a need to expose these functions? * Shift indices for Links in ENaddnode Need to shift indices for Links not just Pipes since a pump could be connected directly to a reservoir. Also set the defult base demand to zero (was 5). * Set defualts for madatory link properties in ENaddlink and small fix for ENsetheadcurveindex Relates to #102 and closes #103 * wraps globals into structs, duplicates api functions with objective versions * parse and serialize Comment field for network elements related to #47 * adds getter for head/efficiency curve in EN_getlinkvalue * adds getter for event node index … to return the index of the junction (tank) that triggered the event. * fixes edge case in parsing … where inp files without demands in [JUNCTIONS] and without any [DEMAND] categories will fail. * adds freeing function for project pointer * removes redundant string literals, fixes overrun issue in error message getter function * check for hydraulics already closed * moving error definitions to data file * deprecates ENR err message getter (unused) * updates location of errors data file also begins to expose blind structs to curves and patterns, anticipating buildout of APIs for those. * updates CLI output to reflect executable name as invoked relates to #109 * Feature nrtest (#131) * Initial commit EPANET testing tools. * Initial commit for epanet-nrtestsuite * SWIG wrapper for EPANET outputapi (#118) * Removed pervious version of outputapi and wrapper * SWIG wrapper for EPANET outputapi * Patching cmake build script fixed target for outputapi * Build failing on deprecated test script * Minor changes. Responding to review comments. * Feature nrtest (#121) * Configured python setup to automatically build nrtest tools. * Working on build / testing automation * Adding EPANET 2.0.12 benchmark * Updated Travis yml to run nrtest * Fixing InsecurePlatformWarning * Fixing InsecurePlatformWarning again * Fixing InsecurePlatformWarning * Fixing InsecurePlatformWarning * Fixing InsecurePlatformWarning * Update .travis.yml * Update .travis.yml * Update .travis.yml * Update .travis.yml * Working on configuring python environment and building test tools under Travis CI. * Making gen-config.sh and run-nrtest.sh executable * Debugging .travis.yml * Debugging .travis.yml * Debugging .travis.yml again * Debugging .travis.yml again * debugging travis setup * debugging Travis setup * debugging Travis setup * debugging Travis setup * debugging Travis setup * debugging Travis setup * debugging Travis setup * debugging Travis setup * debugging Travis setup * debugging Travis setup * debugging Travis setup * Fixing bug with __strncpy_chk destlen < len * nrtesting clean up * re-implements fixes from:5eead5ae403c788567a4* removes extraneous build files, moves cmake and updates travis * mirror of 9b37035560f9683f1514b439f7586a5c17bca5bf * Move some variable declarations * More variable declarations * Fix TmpDir * Allocate _defaultModel * Fix EN_addcurve funcrion * Fix for inpfile * Fix writeRuleinInp call * Set MAXMSG to 79 chars * Fix for flow direction * Refactoring testing related python packages and SWIG wrapper bug fix (#139) * Eliminated epanet-reader package. Removed numpy dependency from epanet-output. Fixed reference counting bug in SWIG wrapper. Added error checking to run_nrtest.sh. Added nrtest package to requirements file. * changing buildhome directory * Fixing bug related to preprocessor definition of PI
This commit is contained in:
27
tools/.gitignore
vendored
Normal file
27
tools/.gitignore
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
# Python compiler files
|
||||
*.py[cd]
|
||||
|
||||
# Python distribution and packaging
|
||||
build/
|
||||
dist/
|
||||
temp/
|
||||
*.cfg
|
||||
*.egg-info/
|
||||
*.whl
|
||||
|
||||
# SWIG generated files
|
||||
epanet_output_wrap.c
|
||||
epanet_output.py
|
||||
|
||||
# C compiler
|
||||
*.o
|
||||
*.dll
|
||||
*.exe
|
||||
|
||||
# Eclipse project files and directories
|
||||
.metadata/
|
||||
.settings/
|
||||
Release/
|
||||
.project
|
||||
.cproject
|
||||
.pydevproject
|
||||
39
tools/epanet-output/setup.py
Normal file
39
tools/epanet-output/setup.py
Normal file
@@ -0,0 +1,39 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# setup.py
|
||||
#
|
||||
# Created: 9/20/2017
|
||||
# Author: Michael E. Tryby
|
||||
# US EPA - ORD/NRMRL
|
||||
#
|
||||
# Setup up script for en_outputapi python extension
|
||||
#
|
||||
# Requires:
|
||||
# Platform C language compiler
|
||||
# Python packages: numpy
|
||||
#
|
||||
|
||||
try:
|
||||
from setuptools import setup, Extension
|
||||
from setuptools.command.build_ext import build_ext
|
||||
except ImportError:
|
||||
from distutils.core import setup, Extension
|
||||
from distutils.command.build_ext import build_ext
|
||||
|
||||
setup(
|
||||
name = "epanet-output",
|
||||
version = "1.0",
|
||||
ext_modules = [
|
||||
Extension("_epanet_output",
|
||||
sources = ['src/epanet_output.i', 'src/epanet_output.c', 'src/errormanager.c'],
|
||||
swig_opts=['-modern'],
|
||||
language = 'C'
|
||||
)
|
||||
],
|
||||
package_dir = {'':'src'},
|
||||
py_modules = ['epanet_output'],
|
||||
|
||||
install_requires = [
|
||||
'enum34'
|
||||
]
|
||||
)
|
||||
969
tools/epanet-output/src/epanet_output.c
Normal file
969
tools/epanet-output/src/epanet_output.c
Normal file
@@ -0,0 +1,969 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// epanet_output.c -- API for reading results from EPANET binary output file
|
||||
//
|
||||
// Version: 0.30
|
||||
// Date 09/06/2017
|
||||
// 06/17/2016
|
||||
// 08/05/2014
|
||||
// 05/21/2014
|
||||
//
|
||||
// Author: Michael E. Tryby
|
||||
// US EPA - ORD/NRMRL
|
||||
//
|
||||
// Modified: Maurizio Cingi
|
||||
// University of Modena
|
||||
//
|
||||
// Purpose: Output API provides an interface for retrieving results from an
|
||||
// EPANET binary output file.
|
||||
//
|
||||
// Output data in the binary file are aligned on a 4 byte word size.
|
||||
// Therefore all values both integers and reals are 32 bits in length.
|
||||
//
|
||||
// All values returned by the output API are indexed from 0 to n-1. This
|
||||
// differs from how node and link elements are indexed by the binary file
|
||||
// writer found in EPANET. Times correspond to reporting periods are indexed
|
||||
// from 0 to number of reporting periods minus one. Node and link elements
|
||||
// are indexed from 0 to nodeCount minus one and 0 to linkCount minus one
|
||||
// respectively.
|
||||
//
|
||||
// The Output API functions provide a convenient way to select "slices" of
|
||||
// data from the output file. As such they return arrays of data. The API
|
||||
// functions automatically allocate memory for the array to be returned. The
|
||||
// caller is responsible for deallocating memory. The function ENR_free() is
|
||||
// provided to deallocate memory.
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "epanet_output.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "errormanager.h"
|
||||
#include "messages.h"
|
||||
|
||||
// NOTE: These depend on machine data model and may change when porting
|
||||
// F_OFF Must be a 8 byte / 64 bit integer for large file support
|
||||
#ifdef _WIN32 // Windows (32-bit and 64-bit)
|
||||
#define F_OFF __int64
|
||||
#else // Other platforms
|
||||
#define F_OFF off_t
|
||||
#endif
|
||||
#define INT4 int // Must be a 4 byte / 32 bit integer type
|
||||
#define REAL4 float // Must be a 4 byte / 32 bit real type
|
||||
#define WORDSIZE 4 // Memory alignment 4 byte word size for both int and real
|
||||
|
||||
#define MINNREC 14 // Minimum allowable number of records
|
||||
#define PROLOGUE 884 // Preliminary fixed length section of header
|
||||
#define MAXID_P1 32 // Max. # characters in ID name
|
||||
|
||||
#define NELEMENTTYPES 5 // Number of element types
|
||||
#define NENERGYRESULTS 6 // Number of energy results
|
||||
#define NNODERESULTS 4 // number of result fields for nodes
|
||||
#define NLINKRESULTS 8 // number of result fields for links
|
||||
#define NREACTRESULTS 4 // number of net reaction results
|
||||
|
||||
#define MEMCHECK(x) (((x) == NULL) ? 411 : 0 )
|
||||
|
||||
// Typedefs for opaque pointer
|
||||
typedef struct data_s {
|
||||
char name[MAXFNAME+1]; // file path/name
|
||||
FILE* file; // FILE structure pointer
|
||||
INT4 nodeCount, tankCount, linkCount, pumpCount, valveCount, nPeriods;
|
||||
F_OFF outputStartPos; // starting file position of output data
|
||||
F_OFF bytesPerPeriod; // bytes saved per simulation time period
|
||||
|
||||
error_handle_t* error_handle;
|
||||
} data_t;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Local functions
|
||||
//-----------------------------------------------------------------------------
|
||||
void errorLookup(int errcode, char* errmsg, int length);
|
||||
int validateFile(ENR_Handle);
|
||||
float getNodeValue(ENR_Handle, int, int, int);
|
||||
float getLinkValue(ENR_Handle, int, int, int);
|
||||
|
||||
int _fopen(FILE **f, const char *name, const char *mode);
|
||||
int _fseek(FILE* stream, F_OFF offset, int whence);
|
||||
F_OFF _ftell(FILE* stream);
|
||||
|
||||
float* newFloatArray(int n);
|
||||
int* newIntArray(int n);
|
||||
char* newCharArray(int n);
|
||||
|
||||
|
||||
int DLLEXPORT ENR_init(ENR_Handle* dp_handle)
|
||||
// Purpose: Initialized pointer for the opaque ENR_Handle.
|
||||
//
|
||||
// Returns: Error code 0 on success, -1 on failure
|
||||
//
|
||||
// Note: The existence of this function has been carefully considered.
|
||||
// Don't change it.
|
||||
//
|
||||
{
|
||||
int errorcode = 0;
|
||||
data_t* p_data;
|
||||
|
||||
// Allocate memory for private data
|
||||
p_data = (data_t*)calloc(1, sizeof(data_t));
|
||||
|
||||
if (p_data != NULL){
|
||||
p_data->error_handle = new_errormanager(&errorLookup);
|
||||
*dp_handle = p_data;
|
||||
}
|
||||
else
|
||||
errorcode = -1;
|
||||
|
||||
// TODO: Need to handle errors during initialization better.
|
||||
return errorcode;
|
||||
}
|
||||
|
||||
int DLLEXPORT ENR_close(ENR_Handle* p_handle)
|
||||
/*------------------------------------------------------------------------
|
||||
** Input: *p_handle = pointer to ENR_Handle struct
|
||||
**
|
||||
** Returns: Error code 0 on success, -1 on failure
|
||||
**
|
||||
** Purpose: Close the output binary file, dellocate ENR_Handle struc
|
||||
** and nullify pointer to ENR_Handle struct
|
||||
**
|
||||
** NOTE: ENR_close must be called before program end
|
||||
** after calling ENR_close data in ENR_Handle struct are no more
|
||||
** accessible
|
||||
**-------------------------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
data_t* p_data;
|
||||
int errorcode = 0;
|
||||
|
||||
p_data = (data_t*)(*p_handle);
|
||||
|
||||
if (p_data == NULL || p_data->file == NULL)
|
||||
errorcode = -1;
|
||||
|
||||
else
|
||||
{
|
||||
dst_errormanager(p_data->error_handle);
|
||||
fclose(p_data->file);
|
||||
free(p_data);
|
||||
|
||||
*p_handle = NULL;
|
||||
}
|
||||
|
||||
return errorcode;
|
||||
}
|
||||
|
||||
int DLLEXPORT ENR_open(ENR_Handle p_handle, const char* path)
|
||||
/*------------------------------------------------------------------------
|
||||
** Input: path
|
||||
** Output: p_handle = pointer to ENR_Handle struct
|
||||
** Returns: warning / error code
|
||||
** Purpose: Opens the output binary file and reads prologue and epilogue
|
||||
**
|
||||
** NOTE: ENR_init must be called before anyother ENR_* functions
|
||||
**-------------------------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
int err, errorcode = 0;
|
||||
F_OFF bytecount;
|
||||
data_t* p_data;
|
||||
|
||||
p_data = (data_t*)p_handle;
|
||||
|
||||
if (p_data == NULL) return -1;
|
||||
else
|
||||
{
|
||||
strncpy(p_data->name, path, MAXFNAME);
|
||||
// Attempt to open binary output file for reading only
|
||||
if ((_fopen(&(p_data->file), path, "rb")) != 0) errorcode = 434;
|
||||
|
||||
// Perform checks to insure the file is valid
|
||||
else if ((err = validateFile(p_data)) != 0) errorcode = err;
|
||||
|
||||
// If a warning is encountered read file header
|
||||
if (errorcode < 400 ) {
|
||||
|
||||
// read network size
|
||||
fseek(p_data->file, 2*WORDSIZE, SEEK_SET);
|
||||
fread(&(p_data->nodeCount), WORDSIZE, 1, p_data->file);
|
||||
fread(&(p_data->tankCount), WORDSIZE, 1, p_data->file);
|
||||
fread(&(p_data->linkCount), WORDSIZE, 1, p_data->file);
|
||||
fread(&(p_data->pumpCount), WORDSIZE, 1, p_data->file);
|
||||
fread(&(p_data->valveCount), WORDSIZE, 1, p_data->file);
|
||||
|
||||
// Compute positions and offsets for retrieving data
|
||||
// fixed portion of header + title section + filenames + chem names
|
||||
bytecount = PROLOGUE;
|
||||
// node names + link names
|
||||
bytecount += MAXID_P1*p_data->nodeCount + MAXID_P1*p_data->linkCount;
|
||||
// network connectivity + tank nodes + tank areas
|
||||
bytecount += 3*WORDSIZE*p_data->linkCount + 2*WORDSIZE*p_data->tankCount;
|
||||
// node elevations + link lengths and link diameters
|
||||
bytecount += WORDSIZE*p_data->nodeCount + 2*WORDSIZE*p_data->linkCount;
|
||||
// pump energy summary
|
||||
bytecount += 7*WORDSIZE*p_data->pumpCount + WORDSIZE;
|
||||
p_data->outputStartPos= bytecount;
|
||||
|
||||
p_data->bytesPerPeriod = NNODERESULTS*WORDSIZE*p_data->nodeCount +
|
||||
NLINKRESULTS*WORDSIZE*p_data->linkCount;
|
||||
}
|
||||
}
|
||||
// If error close the binary file
|
||||
if (errorcode > 400) {
|
||||
set_error(p_data->error_handle, errorcode);
|
||||
ENR_close(&p_handle);
|
||||
}
|
||||
|
||||
return errorcode;
|
||||
}
|
||||
|
||||
int DLLEXPORT ENR_getVersion(ENR_Handle p_handle, int* version)
|
||||
/*------------------------------------------------------------------------
|
||||
** Input: p_handle = pointer to ENR_Handle struct
|
||||
** Output: version Epanet version
|
||||
** Returns: error code
|
||||
**
|
||||
** Purpose: Returns Epanet version that wrote EBOFile
|
||||
**--------------element codes-------------------------------------------
|
||||
*/
|
||||
{
|
||||
int errorcode = 0;
|
||||
data_t* p_data;
|
||||
|
||||
p_data = (data_t*)p_handle;
|
||||
|
||||
if (p_data == NULL) return -1;
|
||||
else
|
||||
{
|
||||
fseek(p_data->file, 1*WORDSIZE, SEEK_SET);
|
||||
if (fread(version, WORDSIZE, 1, p_data->file) != 1)
|
||||
errorcode = 436;
|
||||
}
|
||||
|
||||
return set_error(p_data->error_handle, errorcode);
|
||||
}
|
||||
|
||||
int DLLEXPORT ENR_getNetSize(ENR_Handle p_handle, int** elementCount, int* length)
|
||||
/*------------------------------------------------------------------------
|
||||
** Input: p_handle = pointer to ENR_Handle struct
|
||||
** Output: array of element counts (nodes, tanks, links, pumps, valves)
|
||||
** Returns: error code
|
||||
** Purpose: Returns an array of count values
|
||||
**-------------------------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
int errorcode = 0;
|
||||
int* temp = newIntArray(NELEMENTTYPES);
|
||||
data_t* p_data;
|
||||
|
||||
p_data = (data_t*)p_handle;
|
||||
|
||||
if (p_data == NULL) return -1;
|
||||
else
|
||||
{
|
||||
temp[0] = p_data->nodeCount;
|
||||
temp[1] = p_data->tankCount;
|
||||
temp[2] = p_data->linkCount;
|
||||
temp[3] = p_data->pumpCount;
|
||||
temp[4] = p_data->valveCount;
|
||||
|
||||
*elementCount = temp;
|
||||
*length = NELEMENTTYPES;
|
||||
}
|
||||
|
||||
return set_error(p_data->error_handle, errorcode);
|
||||
}
|
||||
|
||||
int DLLEXPORT ENR_getUnits(ENR_Handle p_handle, ENR_Units code, int* unitFlag)
|
||||
/*------------------------------------------------------------------------
|
||||
** Input: p_handle = pointer to ENR_Handle struct
|
||||
** code
|
||||
** Output: count
|
||||
** Returns: unitFlag
|
||||
** Purpose: Returns pressure or flow unit flag
|
||||
**--------------pressure unit flags----------------------------------------
|
||||
** 0 = psi
|
||||
** 1 = meters
|
||||
** 2 = kPa
|
||||
**------------------flow unit flags----------------------------------------
|
||||
** 0 = cubic feet/second
|
||||
** 1 = gallons/minute
|
||||
** 2 = million gallons/day
|
||||
** 3 = Imperial million gallons/day
|
||||
** 4 = acre-ft/day
|
||||
** 5 = liters/second
|
||||
** 6 = liters/minute
|
||||
** 7 = megaliters/day
|
||||
** 8 = cubic meters/hour
|
||||
** 9 = cubic meters/day
|
||||
**-------------------------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
int errorcode = 0;
|
||||
data_t* p_data;
|
||||
|
||||
*unitFlag = -1;
|
||||
|
||||
p_data = (data_t*)p_handle;
|
||||
|
||||
if (p_data == NULL) return -1;
|
||||
else
|
||||
{
|
||||
switch (code)
|
||||
{
|
||||
case ENR_flowUnits:
|
||||
fseek(p_data->file, 9*WORDSIZE, SEEK_SET);
|
||||
fread(unitFlag, WORDSIZE, 1, p_data->file);
|
||||
break;
|
||||
|
||||
case ENR_pressUnits:
|
||||
fseek(p_data->file, 10*WORDSIZE, SEEK_SET);
|
||||
fread(unitFlag, WORDSIZE, 1, p_data->file);
|
||||
break;
|
||||
|
||||
default: errorcode = 421;
|
||||
}
|
||||
}
|
||||
return set_error(p_data->error_handle, errorcode);
|
||||
}
|
||||
|
||||
int DLLEXPORT ENR_getTimes(ENR_Handle p_handle, ENR_Time code, int* time)
|
||||
/*------------------------------------------------------------------------
|
||||
** Input: p_handle = pointer to ENR_Handle struct
|
||||
** code = element code
|
||||
** Output: time
|
||||
** Returns: error code
|
||||
** Purpose: Returns report and simulation time related parameters.
|
||||
**-------------------------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
int errorcode = 0;
|
||||
data_t* p_data;
|
||||
|
||||
*time = -1;
|
||||
|
||||
p_data = (data_t*)p_handle;
|
||||
|
||||
if (p_data == NULL) return -1;
|
||||
else
|
||||
{
|
||||
switch (code)
|
||||
{
|
||||
case ENR_reportStart:
|
||||
fseek(p_data->file, 12*WORDSIZE, SEEK_SET);
|
||||
fread(time, WORDSIZE, 1, p_data->file);
|
||||
break;
|
||||
|
||||
case ENR_reportStep:
|
||||
fseek(p_data->file, 13*WORDSIZE, SEEK_SET);
|
||||
fread(time, WORDSIZE, 1, p_data->file);
|
||||
break;
|
||||
|
||||
case ENR_simDuration:
|
||||
fseek(p_data->file, 14*WORDSIZE, SEEK_SET);
|
||||
fread(time, WORDSIZE, 1, p_data->file);
|
||||
break;
|
||||
|
||||
case ENR_numPeriods:
|
||||
*time = p_data->nPeriods;
|
||||
break;
|
||||
|
||||
default:
|
||||
errorcode = 421;
|
||||
}
|
||||
}
|
||||
return set_error(p_data->error_handle, errorcode);
|
||||
}
|
||||
|
||||
int DLLEXPORT ENR_getElementName(ENR_Handle p_handle, ENR_ElementType type,
|
||||
int elementIndex, char** name, int* length)
|
||||
/*------------------------------------------------------------------------
|
||||
** Input: p_handle = pointer to ENR_Handle struct
|
||||
** type = ENR_node or ENR_link
|
||||
** elementIndex from 1 to nodeCount or 1 to linkCount
|
||||
** Output: name = elementName
|
||||
** Returns: error code
|
||||
** Purpose: Retrieves Name of a specified node or link element
|
||||
** NOTE: 'name' must be able to hold MAXID characters
|
||||
** TODO: Takes EPANET indexing from 1 to n not 0 to n-1
|
||||
**-------------------------------------------------------------------------
|
||||
*/
|
||||
{
|
||||
F_OFF offset;
|
||||
int errorcode = 0;
|
||||
char* temp;
|
||||
data_t* p_data;
|
||||
|
||||
p_data = (data_t*)p_handle;
|
||||
|
||||
if (p_data == NULL) return -1;
|
||||
/* Allocate memory for name */
|
||||
else if MEMCHECK(temp = newCharArray(MAXID_P1)) errorcode = 411;
|
||||
|
||||
else
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case ENR_node:
|
||||
if (elementIndex < 1 || elementIndex > p_data->nodeCount)
|
||||
errorcode = 423;
|
||||
else offset = PROLOGUE + (elementIndex - 1)*MAXID_P1;
|
||||
break;
|
||||
|
||||
case ENR_link:
|
||||
if (elementIndex < 1 || elementIndex > p_data->linkCount)
|
||||
errorcode = 423;
|
||||
else
|
||||
offset = PROLOGUE + p_data->nodeCount*MAXID_P1 +
|
||||
(elementIndex - 1)*MAXID_P1;
|
||||
break;
|
||||
|
||||
default:
|
||||
errorcode = 421;
|
||||
}
|
||||
|
||||
if (!errorcode)
|
||||
{
|
||||
_fseek(p_data->file, offset, SEEK_SET);
|
||||
fread(temp, 1, MAXID_P1, p_data->file);
|
||||
|
||||
*name = temp;
|
||||
*length = MAXID_P1;
|
||||
}
|
||||
}
|
||||
|
||||
return set_error(p_data->error_handle, errorcode);
|
||||
}
|
||||
|
||||
int DLLEXPORT ENR_getEnergyUsage(ENR_Handle p_handle, int pumpIndex,
|
||||
int* linkIndex, float** outValues, int* length)
|
||||
/*
|
||||
* Purpose: Returns pump energy usage statistics.
|
||||
*
|
||||
* Energy usage statistics:
|
||||
* 0 = pump utilization
|
||||
* 1 = avg. efficiency
|
||||
* 2 = avg. kW/flow
|
||||
* 3 = avg. kwatts
|
||||
* 4 = peak kwatts
|
||||
* 5 = cost/day
|
||||
*/
|
||||
{
|
||||
F_OFF offset;
|
||||
int errorcode = 0;
|
||||
float* temp;
|
||||
data_t* p_data;
|
||||
|
||||
p_data = (data_t*)p_handle;
|
||||
|
||||
if (p_data == NULL) return -1;
|
||||
// Check for valid pump index
|
||||
else if (pumpIndex < 1 || pumpIndex > p_data->pumpCount) errorcode = 423;
|
||||
// Check memory for outValues
|
||||
else if MEMCHECK(temp = newFloatArray(NENERGYRESULTS)) errorcode = 411;
|
||||
|
||||
else
|
||||
{
|
||||
// Position offset to start of pump energy summary
|
||||
offset = p_data->outputStartPos - (p_data->pumpCount*(WORDSIZE + 6*WORDSIZE) + WORDSIZE);
|
||||
// Adjust offset by pump index
|
||||
offset += (pumpIndex - 1)*(WORDSIZE + 6*WORDSIZE);
|
||||
|
||||
// Power summary is 1 int and 6 floats for each pump
|
||||
_fseek(p_data->file, offset, SEEK_SET);
|
||||
fread(linkIndex, WORDSIZE, 1, p_data->file);
|
||||
fread(temp, WORDSIZE, 6, p_data->file);
|
||||
|
||||
*outValues = temp;
|
||||
*length = NENERGYRESULTS;
|
||||
}
|
||||
return set_error(p_data->error_handle, errorcode);
|
||||
}
|
||||
|
||||
int DLLEXPORT ENR_getNetReacts(ENR_Handle p_handle, float** outValues, int* length)
|
||||
/*
|
||||
* Purpose: Returns network wide average reaction rates and average
|
||||
* source mass inflow:
|
||||
* 0 = bulk
|
||||
* 1 = wall
|
||||
* 2 = tank
|
||||
* 3 = source
|
||||
*/
|
||||
{
|
||||
F_OFF offset;
|
||||
int errorcode = 0;
|
||||
float* temp;
|
||||
data_t* p_data;
|
||||
|
||||
p_data = (data_t*)p_handle;
|
||||
|
||||
if (p_data == NULL) return -1;
|
||||
// Check memory for outValues
|
||||
else if MEMCHECK(temp = newFloatArray(NREACTRESULTS)) errorcode = 411;
|
||||
|
||||
else
|
||||
{
|
||||
// Reaction summary is 4 floats located right before epilogue.
|
||||
// This offset is relative to the end of the file.
|
||||
offset = - 3*WORDSIZE - 4*WORDSIZE;
|
||||
_fseek(p_data->file, offset, SEEK_END);
|
||||
fread(temp, WORDSIZE, 4, p_data->file);
|
||||
|
||||
*outValues = temp;
|
||||
*length = NREACTRESULTS;
|
||||
}
|
||||
return set_error(p_data->error_handle, errorcode);
|
||||
}
|
||||
|
||||
void DLLEXPORT ENR_free(void** array)
|
||||
//
|
||||
// Purpose: Frees memory allocated by API calls
|
||||
//
|
||||
{
|
||||
if (array != NULL) {
|
||||
free(*array);
|
||||
*array = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int DLLEXPORT ENR_getNodeSeries(ENR_Handle p_handle, int nodeIndex, ENR_NodeAttribute attr,
|
||||
int startPeriod, int endPeriod, float** outValueSeries, int* dim)
|
||||
//
|
||||
// Purpose: Get time series results for particular attribute. Specify series
|
||||
// start and length using seriesStart and seriesLength respectively.
|
||||
//
|
||||
// NOTE: The node index argument corresponds to the EPANET node index from 1 to
|
||||
// nnodes. The series returned is indexed from 0 to nperiods - 1.
|
||||
//
|
||||
{
|
||||
int k, length, errorcode = 0;
|
||||
float* temp;
|
||||
data_t* p_data;
|
||||
|
||||
p_data = (data_t*)p_handle;
|
||||
|
||||
if (p_data == NULL) return -1;
|
||||
else if (nodeIndex < 1 || nodeIndex > p_data->nodeCount) errorcode = 423;
|
||||
else if (startPeriod < 0 || endPeriod >= p_data->nPeriods ||
|
||||
endPeriod <= startPeriod) errorcode = 422;
|
||||
// Check memory for outValues
|
||||
else if MEMCHECK(temp = newFloatArray(length = endPeriod - startPeriod)) errorcode = 411;
|
||||
else
|
||||
{
|
||||
// loop over and build time series
|
||||
for (k = 0; k < length; k++)
|
||||
temp[k] = getNodeValue(p_handle, startPeriod + k,
|
||||
nodeIndex, attr);
|
||||
|
||||
*outValueSeries = temp;
|
||||
*dim = length;
|
||||
}
|
||||
return set_error(p_data->error_handle, errorcode);
|
||||
}
|
||||
|
||||
int DLLEXPORT ENR_getLinkSeries(ENR_Handle p_handle, int linkIndex, ENR_LinkAttribute attr,
|
||||
int startPeriod, int endPeriod, float** outValueSeries, int* dim)
|
||||
//
|
||||
// Purpose: Get time series results for particular attribute. Specify series
|
||||
// start and length using seriesStart and seriesLength respectively.
|
||||
//
|
||||
// NOTE:
|
||||
// The link index argument corresponds to the EPANET link index from 1 to
|
||||
// nlinks. The series returned is indexed from 0 to nperiods - 1.
|
||||
//
|
||||
{
|
||||
int k, length, errorcode = 0;
|
||||
float* temp;
|
||||
data_t* p_data;
|
||||
|
||||
p_data = (data_t*)p_handle;
|
||||
|
||||
if (p_data == NULL) return -1;
|
||||
else if (linkIndex < 1 || linkIndex > p_data->linkCount) errorcode = 423;
|
||||
else if (startPeriod < 0 || endPeriod >= p_data->nPeriods ||
|
||||
endPeriod <= startPeriod) errorcode = 422;
|
||||
// Check memory for outValues
|
||||
else if MEMCHECK(temp = newFloatArray(length = endPeriod - startPeriod)) errorcode = 411;
|
||||
else
|
||||
{
|
||||
// loop over and build time series
|
||||
for (k = 0; k < length; k++)
|
||||
temp[k] = getLinkValue(p_handle, startPeriod + k, linkIndex, attr);
|
||||
|
||||
*outValueSeries = temp;
|
||||
*dim = length;
|
||||
}
|
||||
return set_error(p_data->error_handle, errorcode);
|
||||
}
|
||||
|
||||
int DLLEXPORT ENR_getNodeAttribute(ENR_Handle p_handle, int periodIndex,
|
||||
ENR_NodeAttribute attr, float** outValueArray, int* length)
|
||||
//
|
||||
// Purpose:
|
||||
// For all nodes at given time, get a particular attribute
|
||||
//
|
||||
// Returns:
|
||||
// Error code
|
||||
// OutValueArray of results is indexed from 0 to nodeCount
|
||||
//
|
||||
// Warning:
|
||||
// Caller must free memory allocated for outValueArray
|
||||
//
|
||||
// NOTE:
|
||||
// The array returned is indexed from 0 to nnodes - 1. So to access
|
||||
// node values by their EPANET index, the index value must be
|
||||
// decremented by one.
|
||||
//
|
||||
{
|
||||
F_OFF offset;
|
||||
int errorcode = 0;
|
||||
float * temp;
|
||||
data_t* p_data;
|
||||
|
||||
p_data = (data_t*)p_handle;
|
||||
|
||||
if (p_data == NULL) return -1;
|
||||
// if the time index is out of range return an error
|
||||
else if (periodIndex < 0 || periodIndex >= p_data->nPeriods) errorcode = 422;
|
||||
// Check memory for outValues
|
||||
else if MEMCHECK(temp = newFloatArray(p_data->nodeCount)) errorcode = 411;
|
||||
|
||||
else
|
||||
{
|
||||
// calculate byte offset to start time for series
|
||||
offset = p_data->outputStartPos + (periodIndex)*p_data->bytesPerPeriod;
|
||||
// add offset for node and attribute
|
||||
offset += ((attr - 1)*p_data->nodeCount)*WORDSIZE;
|
||||
|
||||
_fseek(p_data->file, offset, SEEK_SET);
|
||||
fread(temp, WORDSIZE, p_data->nodeCount, p_data->file);
|
||||
|
||||
*outValueArray = temp;
|
||||
*length = p_data->nodeCount;
|
||||
}
|
||||
|
||||
return set_error(p_data->error_handle, errorcode);
|
||||
}
|
||||
|
||||
int DLLEXPORT ENR_getLinkAttribute(ENR_Handle p_handle, int periodIndex,
|
||||
ENR_LinkAttribute attr, float** outValueArray, int* length)
|
||||
//
|
||||
// Purpose:
|
||||
// For all links at given time, get a particular attribute
|
||||
//
|
||||
// Returns:
|
||||
// Error code
|
||||
// OutValueArray of results is indexed from 0 to linkCount
|
||||
//
|
||||
// Warning:
|
||||
// Caller must free memory allocated for outValueArray
|
||||
//
|
||||
// NOTE:
|
||||
// The array returned is indexed from 0 to nlinks - 1. So to access
|
||||
// link values by their EPANET index, the index value must be
|
||||
// decremented by one.
|
||||
//
|
||||
{
|
||||
F_OFF offset;
|
||||
int errorcode = 0;
|
||||
float* temp;
|
||||
data_t* p_data;
|
||||
|
||||
p_data = (data_t*)p_handle;
|
||||
|
||||
if (p_data == NULL) return -1;
|
||||
// if the time index is out of range return an error
|
||||
else if (periodIndex < 0 || periodIndex >= p_data->nPeriods) errorcode = 422;
|
||||
// Check memory for outValues
|
||||
else if MEMCHECK(temp = newFloatArray(p_data->linkCount)) errorcode = 411;
|
||||
|
||||
else
|
||||
{
|
||||
// calculate byte offset to start time for series
|
||||
offset = p_data->outputStartPos + (periodIndex)*p_data->bytesPerPeriod
|
||||
+ (NNODERESULTS*p_data->nodeCount)*WORDSIZE;
|
||||
// add offset for link and attribute
|
||||
offset += ((attr - 1)*p_data->linkCount)*WORDSIZE;
|
||||
|
||||
_fseek(p_data->file, offset, SEEK_SET);
|
||||
fread(temp, WORDSIZE, p_data->linkCount, p_data->file);
|
||||
|
||||
*outValueArray = temp;
|
||||
*length = p_data->linkCount;
|
||||
}
|
||||
|
||||
return set_error(p_data->error_handle, errorcode);
|
||||
}
|
||||
|
||||
int DLLEXPORT ENR_getNodeResult(ENR_Handle p_handle, int periodIndex,
|
||||
int nodeIndex, float** outValueArray, int* length)
|
||||
//
|
||||
// Purpose: For a node at given time, get all attributes.
|
||||
//
|
||||
// NOTE:
|
||||
//
|
||||
{
|
||||
int j, errorcode = 0;
|
||||
float* temp;
|
||||
data_t* p_data;
|
||||
|
||||
p_data = (data_t*)p_handle;
|
||||
|
||||
if (p_data == NULL) return -1;
|
||||
else if (periodIndex < 0 || periodIndex >= p_data->nPeriods) errorcode = 422;
|
||||
else if (nodeIndex < 1 || nodeIndex > p_data->nodeCount) errorcode = 423;
|
||||
else if MEMCHECK(temp = newFloatArray(NNODERESULTS)) errorcode = 411;
|
||||
else
|
||||
{
|
||||
for (j = 0; j < NNODERESULTS; j++)
|
||||
temp[j] = getNodeValue(p_handle, periodIndex, nodeIndex, j);
|
||||
|
||||
*outValueArray = temp;
|
||||
*length = NNODERESULTS;
|
||||
}
|
||||
|
||||
return set_error(p_data->error_handle, errorcode);
|
||||
}
|
||||
|
||||
int DLLEXPORT ENR_getLinkResult(ENR_Handle p_handle, int periodIndex,
|
||||
int linkIndex, float** outValueArray, int* length)
|
||||
//
|
||||
// Purpose: For a link at given time, get all attributes
|
||||
//
|
||||
{
|
||||
int j, errorcode = 0;
|
||||
float* temp;
|
||||
data_t* p_data;
|
||||
|
||||
p_data = (data_t*)p_handle;
|
||||
|
||||
if (p_data == NULL) return -1;
|
||||
else if (periodIndex < 0 || periodIndex >= p_data->nPeriods) errorcode = 422;
|
||||
else if (linkIndex < 1 || linkIndex > p_data->linkCount) errorcode = 423;
|
||||
else if MEMCHECK(temp = newFloatArray(NLINKRESULTS)) errorcode = 411;
|
||||
else
|
||||
{
|
||||
for (j = 0; j < NLINKRESULTS; j++)
|
||||
temp[j] = getLinkValue(p_handle, periodIndex, linkIndex, j);
|
||||
|
||||
*outValueArray = temp;
|
||||
*length = NLINKRESULTS;
|
||||
}
|
||||
return set_error(p_data->error_handle, errorcode);
|
||||
}
|
||||
|
||||
void DLLEXPORT ENR_clearError(ENR_Handle p_handle)
|
||||
{
|
||||
data_t* p_data;
|
||||
|
||||
p_data = (data_t*)p_handle;
|
||||
clear_error(p_data->error_handle);
|
||||
}
|
||||
|
||||
int DLLEXPORT ENR_checkError(ENR_Handle p_handle, char** msg_buffer)
|
||||
{
|
||||
int errorcode = 0;
|
||||
char *temp = NULL;
|
||||
data_t* p_data;
|
||||
|
||||
p_data = (data_t*)p_handle;
|
||||
|
||||
if (p_data == NULL) return -1;
|
||||
else
|
||||
{
|
||||
errorcode = p_data->error_handle->error_status;
|
||||
if (errorcode)
|
||||
temp = check_error(p_data->error_handle);
|
||||
|
||||
*msg_buffer = temp;
|
||||
}
|
||||
|
||||
return errorcode;
|
||||
}
|
||||
|
||||
|
||||
void errorLookup(int errcode, char* dest_msg, int dest_len)
|
||||
//
|
||||
// Purpose: takes error code returns error message
|
||||
//
|
||||
{
|
||||
const char* msg;
|
||||
|
||||
switch (errcode)
|
||||
{
|
||||
case 10: msg = WARN10;
|
||||
break;
|
||||
case 411: msg = ERR411;
|
||||
break;
|
||||
case 412: msg = ERR412;
|
||||
break;
|
||||
case 421: msg = ERR421;
|
||||
break;
|
||||
case 422: msg = ERR422;
|
||||
break;
|
||||
case 423: msg = ERR423;
|
||||
break;
|
||||
case 434: msg = ERR434;
|
||||
break;
|
||||
case 435: msg = ERR435;
|
||||
break;
|
||||
case 436: msg = ERR436;
|
||||
break;
|
||||
default: msg = ERRERR;
|
||||
}
|
||||
|
||||
strncpy(dest_msg, msg, MAXMSG);
|
||||
}
|
||||
|
||||
int validateFile(ENR_Handle p_handle)
|
||||
// Returns:
|
||||
// Error code: 435, 436
|
||||
// Warning code: 10
|
||||
{
|
||||
INT4 magic1, magic2, hydcode;
|
||||
int errorcode = 0;
|
||||
F_OFF filepos;
|
||||
data_t* p_data;
|
||||
|
||||
p_data = (data_t*)p_handle;
|
||||
|
||||
// Read magic number from beginning of file
|
||||
fseek(p_data->file, 0L, SEEK_SET);
|
||||
fread(&magic1, WORDSIZE, 1, p_data->file);
|
||||
|
||||
// Fast forward to end and read file epilogue
|
||||
fseek(p_data->file, -3*WORDSIZE, SEEK_END);
|
||||
fread(&(p_data->nPeriods), WORDSIZE, 1, p_data->file);
|
||||
fread(&hydcode, WORDSIZE, 1, p_data->file);
|
||||
fread(&magic2, WORDSIZE, 1, p_data->file);
|
||||
|
||||
filepos = _ftell(p_data->file);
|
||||
|
||||
// Is the file an EPANET binary file?
|
||||
if (magic1 != magic2) errorcode = 435;
|
||||
// Does the binary file contain results?
|
||||
else if (filepos < MINNREC*WORDSIZE || p_data->nPeriods == 0)
|
||||
errorcode = 436;
|
||||
// Issue warning if there were problems with the model run.
|
||||
else if (hydcode != 0) errorcode = 10;
|
||||
|
||||
return errorcode;
|
||||
}
|
||||
|
||||
float getNodeValue(ENR_Handle p_handle, int periodIndex, int nodeIndex,
|
||||
int attr)
|
||||
//
|
||||
// Purpose: Retrieves an attribute value at a specified node and time
|
||||
//
|
||||
{
|
||||
F_OFF offset;
|
||||
REAL4 y;
|
||||
data_t* p_data;
|
||||
|
||||
p_data = (data_t*)p_handle;
|
||||
|
||||
// calculate byte offset to start time for series
|
||||
offset = p_data->outputStartPos + periodIndex*p_data->bytesPerPeriod;
|
||||
// add byte position for attribute and node
|
||||
offset += ((attr - 1)*p_data->nodeCount + (nodeIndex - 1))*WORDSIZE;
|
||||
|
||||
_fseek(p_data->file, offset, SEEK_SET);
|
||||
fread(&y, WORDSIZE, 1, p_data->file);
|
||||
|
||||
return y;
|
||||
}
|
||||
|
||||
float getLinkValue(ENR_Handle p_handle, int periodIndex, int linkIndex,
|
||||
int attr)
|
||||
//
|
||||
// Purpose: Retrieves an attribute value at a specified link and time
|
||||
//
|
||||
{
|
||||
F_OFF offset;
|
||||
REAL4 y;
|
||||
data_t* p_data;
|
||||
|
||||
p_data = (data_t*)p_handle;
|
||||
|
||||
// Calculate byte offset to start time for series
|
||||
offset = p_data->outputStartPos + periodIndex*p_data->bytesPerPeriod
|
||||
+ (NNODERESULTS*p_data->nodeCount)*WORDSIZE;
|
||||
// add byte position for attribute and link
|
||||
offset += ((attr - 1)*p_data->linkCount + (linkIndex - 1))*WORDSIZE;
|
||||
|
||||
_fseek(p_data->file, offset, SEEK_SET);
|
||||
fread(&y, WORDSIZE, 1, p_data->file);
|
||||
|
||||
return y;
|
||||
}
|
||||
|
||||
int _fopen(FILE **f, const char *name, const char *mode) {
|
||||
//
|
||||
// Purpose: Substitute for fopen_s on platforms where it doesn't exist
|
||||
// Note: fopen_s is part of C++11 standard
|
||||
//
|
||||
int ret = 0;
|
||||
#ifdef _WIN32
|
||||
ret = (int)fopen_s(f, name, mode);
|
||||
#else
|
||||
*f = fopen(name, mode);
|
||||
if (!*f)
|
||||
ret = -1;
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
int _fseek(FILE* stream, F_OFF offset, int whence)
|
||||
//
|
||||
// Purpose: Selects platform fseek() for large file support
|
||||
//
|
||||
{
|
||||
#ifdef _WIN32 // Windows (32-bit and 64-bit)
|
||||
#define FSEEK64 _fseeki64
|
||||
#else // Other platforms
|
||||
#define FSEEK64 fseeko
|
||||
#endif
|
||||
|
||||
return FSEEK64(stream, offset, whence);
|
||||
}
|
||||
|
||||
F_OFF _ftell(FILE* stream)
|
||||
//
|
||||
// Purpose: Selects platform ftell() for large file support
|
||||
//
|
||||
{
|
||||
#ifdef _WIN32 // Windows (32-bit and 64-bit)
|
||||
#define FTELL64 _ftelli64
|
||||
#else // Other platforms
|
||||
#define FTELL64 ftello
|
||||
#endif
|
||||
|
||||
return FTELL64(stream);
|
||||
}
|
||||
|
||||
float* newFloatArray(int n)
|
||||
//
|
||||
// Warning: Caller must free memory allocated by this function.
|
||||
//
|
||||
{
|
||||
return (float*) malloc((n)*sizeof(float));
|
||||
}
|
||||
|
||||
int* newIntArray(int n)
|
||||
//
|
||||
// Warning: Caller must free memory allocated by this function.
|
||||
//
|
||||
{
|
||||
return (int*) malloc((n)*sizeof(int));
|
||||
}
|
||||
|
||||
char* newCharArray(int n)
|
||||
//
|
||||
// Warning: Caller must free memory allocated by this function.
|
||||
//
|
||||
{
|
||||
return (char*) malloc((n)*sizeof(char));
|
||||
}
|
||||
129
tools/epanet-output/src/epanet_output.h
Normal file
129
tools/epanet-output/src/epanet_output.h
Normal file
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* epanet_output.h - EPANET Output API
|
||||
*
|
||||
* Created on: Jun 4, 2014
|
||||
*
|
||||
* Author: Michael E. Tryby
|
||||
* US EPA - ORD/NRMRL
|
||||
*/
|
||||
|
||||
#ifndef EPANET_OUTPUT_H_
|
||||
#define EPANET_OUTPUT_H_
|
||||
/* Epanet Results binary file API */
|
||||
|
||||
#define MAXFNAME 259 // Max characters in file name
|
||||
#define MAXID 31 // Max characters in ID name
|
||||
|
||||
// This is an opaque pointer to struct. Do not access variables.
|
||||
typedef void* ENR_Handle;
|
||||
|
||||
typedef enum {
|
||||
ENR_node = 1,
|
||||
ENR_link = 2
|
||||
} ENR_ElementType;
|
||||
|
||||
typedef enum {
|
||||
ENR_getSeries = 1,
|
||||
ENR_getAttribute = 2,
|
||||
ENR_getResult = 3,
|
||||
ENR_getReacts = 4,
|
||||
ENR_getEnergy = 5
|
||||
} ENR_ApiFunction;
|
||||
|
||||
typedef enum {
|
||||
ENR_flowUnits = 1,
|
||||
ENR_pressUnits = 2
|
||||
} ENR_Units;
|
||||
|
||||
typedef enum {
|
||||
ENR_reportStart = 1,
|
||||
ENR_reportStep = 2,
|
||||
ENR_simDuration = 3,
|
||||
ENR_numPeriods = 4
|
||||
}ENR_Time;
|
||||
|
||||
typedef enum {
|
||||
ENR_demand = 1,
|
||||
ENR_head = 2,
|
||||
ENR_pressure = 3,
|
||||
ENR_quality = 4
|
||||
} ENR_NodeAttribute;
|
||||
|
||||
typedef enum {
|
||||
ENR_flow = 1,
|
||||
ENR_velocity = 2,
|
||||
ENR_headloss = 3,
|
||||
ENR_avgQuality = 4,
|
||||
ENR_status = 5,
|
||||
ENR_setting = 6,
|
||||
ENR_rxRate = 7,
|
||||
ENR_frctnFctr = 8
|
||||
} ENR_LinkAttribute;
|
||||
|
||||
|
||||
#ifdef WINDOWS
|
||||
#ifdef __cplusplus
|
||||
#define DLLEXPORT __declspec(dllexport) __cdecl
|
||||
#else
|
||||
#define DLLEXPORT __declspec(dllexport) __stdcall
|
||||
#endif
|
||||
#else
|
||||
#define DLLEXPORT
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int DLLEXPORT ENR_init(ENR_Handle* p_handle_out);
|
||||
|
||||
int DLLEXPORT ENR_open(ENR_Handle p_handle_in, const char* path);
|
||||
|
||||
int DLLEXPORT ENR_getVersion(ENR_Handle p_handle_in, int* int_out);
|
||||
|
||||
int DLLEXPORT ENR_getNetSize(ENR_Handle p_handle_in, int** int_out, int* int_dim);
|
||||
|
||||
int DLLEXPORT ENR_getUnits(ENR_Handle p_handle_in, ENR_Units t_enum, int* int_out);
|
||||
|
||||
int DLLEXPORT ENR_getTimes(ENR_Handle p_handle_in, ENR_Time t_enum, int* int_out);
|
||||
|
||||
int DLLEXPORT ENR_getElementName(ENR_Handle p_handle_in, ENR_ElementType t_enum,
|
||||
int elementIndex, char** string_out, int* slen);
|
||||
|
||||
int DLLEXPORT ENR_getEnergyUsage(ENR_Handle p_handle_in, int pumpIndex,
|
||||
int* int_out, float** float_out, int* int_dim);
|
||||
|
||||
int DLLEXPORT ENR_getNetReacts(ENR_Handle p_handle_in, float** float_out, int* int_dim);
|
||||
|
||||
|
||||
int DLLEXPORT ENR_getNodeSeries(ENR_Handle p_handle_in, int nodeIndex, ENR_NodeAttribute t_enum,
|
||||
int startPeriod, int endPeriod, float** outValueSeries, int* dim);
|
||||
|
||||
int DLLEXPORT ENR_getLinkSeries(ENR_Handle p_handle_in, int linkIndex, ENR_LinkAttribute t_enum,
|
||||
int startPeriod, int endPeriod, float** outValueSeries, int* dim);
|
||||
|
||||
int DLLEXPORT ENR_getNodeAttribute(ENR_Handle p_handle_in, int periodIndex,
|
||||
ENR_NodeAttribute t_enum, float** outValueArray, int* dim);
|
||||
|
||||
int DLLEXPORT ENR_getLinkAttribute(ENR_Handle p_handle_in, int periodIndex,
|
||||
ENR_LinkAttribute t_enum, float** outValueArray, int* dim);
|
||||
|
||||
int DLLEXPORT ENR_getNodeResult(ENR_Handle p_handle_in, int periodIndex, int nodeIndex,
|
||||
float** float_out, int* int_dim);
|
||||
|
||||
int DLLEXPORT ENR_getLinkResult(ENR_Handle p_handle_in, int periodIndex, int linkIndex,
|
||||
float** float_out, int* int_dim);
|
||||
|
||||
int DLLEXPORT ENR_close(ENR_Handle* p_handle_out);
|
||||
|
||||
void DLLEXPORT ENR_free(void** array);
|
||||
|
||||
void DLLEXPORT ENR_clearError(ENR_Handle p_handle_in);
|
||||
|
||||
int DLLEXPORT ENR_checkError(ENR_Handle p_handle_in, char** msg_buffer);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* EPANET_OUTPUT_H_ */
|
||||
254
tools/epanet-output/src/epanet_output.i
Normal file
254
tools/epanet-output/src/epanet_output.i
Normal file
@@ -0,0 +1,254 @@
|
||||
/*
|
||||
* epanet_output.i - SWIG interface description file for EPANET Output API
|
||||
*
|
||||
* Created: 9/20/2017
|
||||
*
|
||||
* Author: Michael E. Tryby
|
||||
* US EPA - ORD/NRMRL
|
||||
*
|
||||
*/
|
||||
%module epanet_output
|
||||
%{
|
||||
#include "errormanager.h"
|
||||
#include "messages.h"
|
||||
#include "epanet_output.h"
|
||||
|
||||
#define SWIG_FILE_WITH_INIT
|
||||
%}
|
||||
|
||||
%include "typemaps.i"
|
||||
|
||||
/* DEFINE AND TYPEDEF MUST BE INCLUDED */
|
||||
#define MAXMSG 53
|
||||
|
||||
typedef void* ENR_Handle;
|
||||
|
||||
typedef enum {
|
||||
ENR_node = 1,
|
||||
ENR_link = 2
|
||||
} ENR_ElementType;
|
||||
|
||||
/*
|
||||
typedef enum {
|
||||
ENR_nodeCount = 1,
|
||||
ENR_tankCount = 2,
|
||||
ENR_linkCount = 3,
|
||||
ENR_pumpCount = 4,
|
||||
ENR_valveCount = 5
|
||||
} ENR_ElementCount;
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
ENR_flowUnits = 1,
|
||||
ENR_pressUnits = 2
|
||||
} ENR_Units;
|
||||
|
||||
typedef enum {
|
||||
ENR_reportStart = 1,
|
||||
ENR_reportStep = 2,
|
||||
ENR_simDuration = 3,
|
||||
ENR_numPeriods = 4
|
||||
}ENR_Time;
|
||||
|
||||
typedef enum {
|
||||
ENR_demand = 1,
|
||||
ENR_head = 2,
|
||||
ENR_pressure = 3,
|
||||
ENR_quality = 4
|
||||
} ENR_NodeAttribute;
|
||||
|
||||
typedef enum {
|
||||
ENR_flow = 1,
|
||||
ENR_velocity = 2,
|
||||
ENR_headloss = 3,
|
||||
ENR_avgQuality = 4,
|
||||
ENR_status = 5,
|
||||
ENR_setting = 6,
|
||||
ENR_rxRate = 7,
|
||||
ENR_frctnFctr = 8
|
||||
} ENR_LinkAttribute;
|
||||
|
||||
#ifdef WINDOWS
|
||||
#ifdef __cplusplus
|
||||
#define DLLEXPORT __declspec(dllexport) __cdecl
|
||||
#else
|
||||
#define DLLEXPORT __declspec(dllexport) __stdcall
|
||||
#endif
|
||||
#else
|
||||
#define DLLEXPORT
|
||||
#endif
|
||||
|
||||
/* TYPEMAPS FOR OPAQUE POINTER */
|
||||
/* Used for functions that output a new opaque pointer */
|
||||
%typemap(in, numinputs=0) ENR_Handle* p_handle_out (ENR_Handle retval)
|
||||
{
|
||||
/* OUTPUT in */
|
||||
retval = NULL;
|
||||
$1 = &retval;
|
||||
}
|
||||
/* used for functions that take in an opaque pointer (or NULL)
|
||||
and return a (possibly) different pointer */
|
||||
%typemap(argout) ENR_Handle* p_handle_out
|
||||
{
|
||||
/* OUTPUT argout */
|
||||
%append_output(SWIG_NewPointerObj(SWIG_as_voidptr(retval$argnum), $1_descriptor, 0));
|
||||
}
|
||||
/* No need for special IN typemap for opaque pointers, it works anyway */
|
||||
|
||||
|
||||
/* TYPEMAP FOR IGNORING INT ERROR CODE RETURN VALUE */
|
||||
%typemap(out) int {
|
||||
$result = Py_None;
|
||||
Py_INCREF($result);
|
||||
}
|
||||
|
||||
/* TYPEMAPS FOR INT ARGUMENT AS RETURN VALUE */
|
||||
%typemap(in, numinputs=0) int* int_out (int temp) {
|
||||
$1 = &temp;
|
||||
}
|
||||
%typemap(argout) int* int_out {
|
||||
%append_output(PyInt_FromLong(*$1));
|
||||
}
|
||||
|
||||
/* TYPEMAP FOR MEMORY MANAGEMENT AND ENCODING OF STRINGS */
|
||||
%typemap(in, numinputs=0)char** string_out (char* temp), int* slen (int temp){
|
||||
$1 = &temp;
|
||||
}
|
||||
%typemap(argout)(char** string_out, int* slen) {
|
||||
if (*$1) {
|
||||
PyObject* o;
|
||||
o = PyUnicode_FromStringAndSize(*$1, *$2);
|
||||
|
||||
$result = SWIG_Python_AppendOutput($result, o);
|
||||
free(*$1);
|
||||
}
|
||||
}
|
||||
|
||||
/* TYPEMAPS FOR MEMORY MANAGEMNET OF FLOAT ARRAYS */
|
||||
%typemap(in, numinputs=0)float** float_out (float* temp), int* int_dim (int temp){
|
||||
$1 = &temp;
|
||||
}
|
||||
%typemap(argout) (float** float_out, int* int_dim) {
|
||||
if (*$1) {
|
||||
PyObject *o = PyList_New(*$2);
|
||||
int i;
|
||||
float* temp = *$1;
|
||||
for(i=0; i<*$2; i++) {
|
||||
PyList_SetItem(o, i, PyFloat_FromDouble((double)temp[i]));
|
||||
}
|
||||
$result = SWIG_Python_AppendOutput($result, o);
|
||||
free(*$1);
|
||||
}
|
||||
}
|
||||
|
||||
/* TYPEMAPS FOR MEMORY MANAGEMENT OF INT ARRAYS */
|
||||
%typemap(in, numinputs=0)int** int_out (long* temp), int* int_dim (int temp){
|
||||
$1 = &temp;
|
||||
}
|
||||
%typemap(argout) (int** int_out, int* int_dim) {
|
||||
if (*$1) {
|
||||
PyObject *o = PyList_New(*$2);
|
||||
int i;
|
||||
long* temp = *$1;
|
||||
for(i=0; i<*$2; i++) {
|
||||
PyList_SetItem(o, i, PyInt_FromLong(temp[i]));
|
||||
}
|
||||
$result = SWIG_Python_AppendOutput($result, o);
|
||||
free(*$1);
|
||||
}
|
||||
}
|
||||
|
||||
/* TYPEMAP FOR ENUMERATED TYPES */
|
||||
%typemap(in) EnumeratedType (int val, int ecode = 0) {
|
||||
if (PyObject_HasAttrString($input,"value")) {
|
||||
PyObject* o;
|
||||
o = PyObject_GetAttrString($input, "value");
|
||||
ecode = SWIG_AsVal_int(o, &val);
|
||||
}
|
||||
else {
|
||||
SWIG_exception_fail(SWIG_ArgError(ecode), "in method '" "$symname" "', argument " "$argnum"" of type '" "$ltype""'");
|
||||
}
|
||||
|
||||
$1 = ($1_type)(val);
|
||||
}
|
||||
%apply EnumeratedType {ENR_ElementType, ENR_Units, ENR_Time, ENR_NodeAttribute, ENR_LinkAttribute}
|
||||
|
||||
|
||||
/* RENAME FUNCTIONS PYTHON STYLE */
|
||||
%rename("%(undercase)s") "";
|
||||
|
||||
/* INSERTS CUSTOM EXCEPTION HANDLING IN WRAPPER */
|
||||
%exception
|
||||
{
|
||||
char* err_msg;
|
||||
ENR_clearError(arg1);
|
||||
$function
|
||||
if (ENR_checkError(arg1, &err_msg))
|
||||
{
|
||||
PyErr_SetString(PyExc_Exception, err_msg);
|
||||
SWIG_fail;
|
||||
}
|
||||
}
|
||||
/* INSERT EXCEPTION HANDLING FOR THESE FUNCTIONS */
|
||||
int DLLEXPORT ENR_open(ENR_Handle p_handle, const char* path);
|
||||
|
||||
int DLLEXPORT ENR_getVersion(ENR_Handle p_handle, int* int_out);
|
||||
int DLLEXPORT ENR_getNetSize(ENR_Handle p_handle, int** int_out, int* int_dim);
|
||||
int DLLEXPORT ENR_getUnits(ENR_Handle p_handle, ENR_Units t_enum, int* int_out);
|
||||
int DLLEXPORT ENR_getTimes(ENR_Handle p_handle, ENR_Time t_enum, int* int_out);
|
||||
int DLLEXPORT ENR_getElementName(ENR_Handle p_handle, ENR_ElementType t_enum,
|
||||
int elementIndex, char** string_out, int* slen);
|
||||
int DLLEXPORT ENR_getEnergyUsage(ENR_Handle p_handle, int pumpIndex,
|
||||
int* int_out, float** float_out, int* int_dim);
|
||||
int DLLEXPORT ENR_getNetReacts(ENR_Handle p_handle, float** float_out, int* int_dim);
|
||||
|
||||
|
||||
int DLLEXPORT ENR_getNodeAttribute(ENR_Handle p_handle, int periodIndex,
|
||||
ENR_NodeAttribute t_enum, float** float_out, int* int_dim);
|
||||
int DLLEXPORT ENR_getLinkAttribute(ENR_Handle p_handle, int periodIndex,
|
||||
ENR_LinkAttribute t_enum, float** float_out, int* int_dim);
|
||||
%exception;
|
||||
|
||||
/* NO EXCEPTION HANDLING FOR THESE FUNCTIONS */
|
||||
int DLLEXPORT ENR_init(ENR_Handle* p_handle_out);
|
||||
int DLLEXPORT ENR_close(ENR_Handle* p_handle_out);
|
||||
void DLLEXPORT ENR_free(void** array);
|
||||
|
||||
void DLLEXPORT ENR_clearError(ENR_Handle p_handle);
|
||||
int DLLEXPORT ENR_checkError(ENR_Handle p_handle, char** msg_buffer);
|
||||
|
||||
|
||||
/* CODE ADDED DIRECTLY TO SWIGGED INTERFACE MODULE */
|
||||
%pythoncode%{
|
||||
import enum
|
||||
|
||||
class ElementType(enum.Enum):
|
||||
NODE = ENR_node
|
||||
LINK = ENR_link
|
||||
|
||||
class Units(enum.Enum):
|
||||
FLOW_UNIT = ENR_flowUnits
|
||||
PRESS_UNIT = ENR_pressUnits
|
||||
|
||||
class Time(enum.Enum):
|
||||
REPORT_START = ENR_reportStart
|
||||
REPORT_STEP = ENR_reportStep
|
||||
SIM_DURATION = ENR_simDuration
|
||||
NUM_PERIODS = ENR_numPeriods
|
||||
|
||||
class NodeAttribute(enum.Enum):
|
||||
DEMAND = ENR_demand
|
||||
HEAD = ENR_head
|
||||
PRESSURE = ENR_pressure
|
||||
QUALITY = ENR_quality
|
||||
|
||||
class LinkAttribute(enum.Enum):
|
||||
FLOW = ENR_flow
|
||||
VELOCITY = ENR_velocity
|
||||
HEADLOSS = ENR_headloss
|
||||
AVG_QUALITY = ENR_avgQuality
|
||||
STATUS = ENR_status
|
||||
SETTING = ENR_setting
|
||||
RX_RATE = ENR_rxRate
|
||||
FRCTN_FCTR = ENR_frctnFctr
|
||||
%}
|
||||
74
tools/epanet-output/src/errormanager.c
Normal file
74
tools/epanet-output/src/errormanager.c
Normal file
@@ -0,0 +1,74 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// errormanager.c
|
||||
//
|
||||
// Purpose: Provides a simple interface for managing runtime error messages.
|
||||
//
|
||||
// Date: 08/25/2017
|
||||
//
|
||||
// Author: Michael E. Tryby
|
||||
// US EPA - ORD/NRMRL
|
||||
//-----------------------------------------------------------------------------
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "errormanager.h"
|
||||
|
||||
error_handle_t* new_errormanager(void (*p_error_message)(int, char*, int))
|
||||
//
|
||||
// Purpose: Constructs a new error handle.
|
||||
//
|
||||
{
|
||||
error_handle_t* error_handle;
|
||||
error_handle = (error_handle_t*)calloc(1, sizeof(error_handle_t));
|
||||
|
||||
error_handle->p_msg_lookup = p_error_message;
|
||||
|
||||
return error_handle;
|
||||
}
|
||||
|
||||
void dst_errormanager(error_handle_t* error_handle)
|
||||
//
|
||||
// Purpose: Destroys the error handle.
|
||||
//
|
||||
{
|
||||
free(error_handle);
|
||||
}
|
||||
|
||||
int set_error(error_handle_t* error_handle, int errorcode)
|
||||
//
|
||||
// Purpose: Sets an error code in the handle.
|
||||
//
|
||||
{
|
||||
// If the error code is 0 no action is taken and 0 is returned.
|
||||
// This is a feature not a bug.
|
||||
if (errorcode)
|
||||
error_handle->error_status = errorcode;
|
||||
|
||||
return errorcode;
|
||||
}
|
||||
|
||||
char* check_error(error_handle_t* error_handle)
|
||||
//
|
||||
// Purpose: Returns the error message or NULL.
|
||||
//
|
||||
// Note: Caller must free memory allocated by check_error
|
||||
//
|
||||
{
|
||||
char* temp = NULL;
|
||||
|
||||
if (error_handle->error_status != 0) {
|
||||
temp = (char*) calloc(ERR_MAXMSG, sizeof(char));
|
||||
|
||||
if (temp)
|
||||
error_handle->p_msg_lookup(error_handle->error_status, temp, ERR_MAXMSG);
|
||||
}
|
||||
return temp;
|
||||
}
|
||||
|
||||
void clear_error(error_handle_t* error_handle)
|
||||
//
|
||||
// Purpose: Clears the error from the handle.
|
||||
//
|
||||
{
|
||||
error_handle->error_status = 0;
|
||||
}
|
||||
27
tools/epanet-output/src/errormanager.h
Normal file
27
tools/epanet-output/src/errormanager.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* errormanager.h
|
||||
*
|
||||
* Created on: Aug 25, 2017
|
||||
*
|
||||
* Author: Michael E. Tryby
|
||||
* US EPA - ORD/NRMRL
|
||||
*/
|
||||
|
||||
#ifndef ERRORMANAGER_H_
|
||||
#define ERRORMANAGER_H_
|
||||
|
||||
#define ERR_MAXMSG 256
|
||||
|
||||
typedef struct error_s {
|
||||
int error_status;
|
||||
void (*p_msg_lookup)(int, char*, int);
|
||||
} error_handle_t;
|
||||
|
||||
error_handle_t* new_errormanager(void (*p_error_message)(int, char*, int));
|
||||
void dst_errormanager(error_handle_t* error_handle);
|
||||
|
||||
int set_error(error_handle_t* error_handle, int errorcode);
|
||||
char* check_error(error_handle_t* error_handle);
|
||||
void clear_error(error_handle_t* error_handle);
|
||||
|
||||
#endif /* ERRORMANAGER_H_ */
|
||||
29
tools/epanet-output/src/messages.h
Normal file
29
tools/epanet-output/src/messages.h
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* messages.h - EPANET
|
||||
*
|
||||
* Created on: June 1, 2017
|
||||
*
|
||||
* Author: Michael E. Tryby
|
||||
* US EPA - ORD/NRMRL
|
||||
*/
|
||||
|
||||
#ifndef MESSAGES_H_
|
||||
#define MESSAGES_H_
|
||||
/*------------------- Error Messages --------------------*/
|
||||
#define MAXMSG 53
|
||||
|
||||
#define WARN10 "Warning: model run issued warnings"
|
||||
|
||||
#define ERR411 "Input Error 411: no memory allocated for results"
|
||||
#define ERR412 "Input Error 412: binary file hasn't been opened"
|
||||
#define ERR421 "Input Error 421: invalid parameter code"
|
||||
#define ERR422 "Input Error 422: reporting period index out of range"
|
||||
#define ERR423 "Input Error 423: element index out of range"
|
||||
|
||||
#define ERR434 "File Error 434: unable to open binary file"
|
||||
#define ERR435 "File Error 435: invalid binary file type"
|
||||
#define ERR436 "File Error 436: no results in binary file"
|
||||
|
||||
#define ERRERR "Error: An unknown error has occurred"
|
||||
|
||||
#endif /* MESSAGES_H_ */
|
||||
14
tools/epanet-output/test/data/__init__.py
Normal file
14
tools/epanet-output/test/data/__init__.py
Normal file
@@ -0,0 +1,14 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# __init__.py
|
||||
#
|
||||
# Created: 11/13/2017
|
||||
# Author: Michael E. Tryby
|
||||
# US EPA - ORD/NRMRL
|
||||
#
|
||||
|
||||
import os
|
||||
|
||||
DATA_PATH = os.path.abspath(os.path.dirname(__file__))
|
||||
|
||||
OUTPUT_FILE_EXAMPLE1 = os.path.join(DATA_PATH, 'net1.out')
|
||||
BIN
tools/epanet-output/test/data/net1.out
Normal file
BIN
tools/epanet-output/test/data/net1.out
Normal file
Binary file not shown.
102
tools/epanet-output/test/epanet_output_test.py
Normal file
102
tools/epanet-output/test/epanet_output_test.py
Normal file
@@ -0,0 +1,102 @@
|
||||
#
|
||||
# epanet_output_test.py
|
||||
#
|
||||
# Created: 11/8/2017
|
||||
# Author: Michael E. Tryby
|
||||
# US EPA - ORD/NRMRL
|
||||
#
|
||||
# Unit testing for EPANET Output API using pytest.
|
||||
#
|
||||
|
||||
import pytest
|
||||
import numpy as np
|
||||
|
||||
import epanet_output as oapi
|
||||
|
||||
from data import OUTPUT_FILE_EXAMPLE1
|
||||
|
||||
@pytest.fixture()
|
||||
def enr_handle(request):
|
||||
_handle = oapi.enr_init()
|
||||
oapi.enr_open(_handle, OUTPUT_FILE_EXAMPLE1)
|
||||
|
||||
def enr_close():
|
||||
oapi.enr_close()
|
||||
|
||||
request.addfinalizer(enr_close)
|
||||
return _handle
|
||||
|
||||
|
||||
def test_get_times(enr_handle):
|
||||
num_periods = oapi.enr_get_times(enr_handle, oapi.Time.NUM_PERIODS)
|
||||
assert num_periods == 25
|
||||
|
||||
|
||||
# def test_get_size(file_path):
|
||||
# handle = oapi.enr_init()
|
||||
# oapi.enr_open(handle, file_path)
|
||||
#
|
||||
# size = oapi.enr_get_net_size(handle)
|
||||
#
|
||||
# print(size)
|
||||
#
|
||||
# handle = oapi.enr_close()
|
||||
#
|
||||
# def test_get_names(file_path):
|
||||
# handle = oapi.enr_init()
|
||||
# oapi.enr_open(handle, file_path)
|
||||
#
|
||||
# name = oapi.enr_get_element_name(handle, oapi.ElementType.NODE, 10)
|
||||
#
|
||||
# print(name)
|
||||
#
|
||||
# handle = oapi.enr_close()
|
||||
#
|
||||
# def test_get_energy(file_path):
|
||||
# handle = oapi.enr_init()
|
||||
# oapi.enr_open(handle, file_path)
|
||||
#
|
||||
# result = oapi.enr_get_energy_usage(handle, 1)
|
||||
#
|
||||
# print(result)
|
||||
#
|
||||
# handle = oapi.enr_close()
|
||||
#
|
||||
# def test_get_react(file_path):
|
||||
# handle = oapi.enr_init()
|
||||
# oapi.enr_open(handle, file_path)
|
||||
#
|
||||
# result = oapi.enr_get_net_reacts(handle)
|
||||
#
|
||||
# print(result)
|
||||
#
|
||||
# handle = oapi.enr_close()
|
||||
#
|
||||
def test_get_node_attribute(enr_handle):
|
||||
ref_array = np.array([ 1., 0.44407997, 0.43766347, 0.42827705, 0.41342604,
|
||||
0.42804748, 0.44152543, 0.40502965, 0.38635802, 1., 0.96745253])
|
||||
|
||||
array = oapi.enr_get_node_attribute(enr_handle, 1, oapi.NodeAttribute.QUALITY)
|
||||
assert len(array) == 11
|
||||
assert np.allclose(array, ref_array)
|
||||
|
||||
def test_get_link_attribute(enr_handle):
|
||||
ref_array = np.array([ 1848.58117676, 1220.42736816, 130.11161804,
|
||||
187.68930054, 119.88839722, 40.46448898, -748.58111572, 478.15377808,
|
||||
191.73458862, 30.11160851, 140.4644928, 59.53551483, 1848.58117676])
|
||||
|
||||
array = oapi.enr_get_link_attribute(enr_handle, 1, oapi.LinkAttribute.FLOW)
|
||||
assert len(array) == 13
|
||||
assert np.allclose(array, ref_array)
|
||||
|
||||
# if __name__ == "__main__":
|
||||
#
|
||||
# file_path = "M:\\net mydocuments\\EPA Projects\\EPAnet Examples\\net1.out"
|
||||
# test_get_times(file_path)
|
||||
# test_get_size(file_path)
|
||||
# test_get_names(file_path)
|
||||
# test_get_energy(file_path)
|
||||
# test_get_react(file_path)
|
||||
# test_get_node_attribute(file_path)
|
||||
# test_get_link_attribute(file_path)
|
||||
#
|
||||
251
tools/epanet-output/test/test_epanet_output.cpp
Normal file
251
tools/epanet-output/test/test_epanet_output.cpp
Normal file
@@ -0,0 +1,251 @@
|
||||
/*
|
||||
* test_epanet_output.cpp
|
||||
*
|
||||
* Created: 8/4/2017
|
||||
* Author: Michael E. Tryby
|
||||
* US EPA - ORD/NRMRL
|
||||
*
|
||||
* Unit testing for EPANET Output API using google test.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "../src/epanet_output.h"
|
||||
|
||||
#define PROJECT_HOME "C:/Users/mtryby/Workspace/GitRepo/michaeltryby/epanet/"
|
||||
#define DATA_PATH "tools/epanet-output/test/data/net1.out"
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
TEST(ENR_init, InitTest) {
|
||||
ENR_Handle p_handle;
|
||||
|
||||
int error = ENR_init(&p_handle);
|
||||
ASSERT_EQ(0, error);
|
||||
ASSERT_TRUE(p_handle != NULL);
|
||||
}
|
||||
|
||||
TEST(ENR_open, OpenTest) {
|
||||
std::string path = std::string(PROJECT_HOME) + std::string(DATA_PATH);
|
||||
ENR_Handle p_handle;
|
||||
ENR_init(&p_handle);
|
||||
|
||||
int error = ENR_open(p_handle, path.c_str());
|
||||
ASSERT_EQ(0, error);
|
||||
ENR_close(&p_handle);
|
||||
}
|
||||
|
||||
TEST(ENR_close, CloseTest) {
|
||||
ENR_Handle p_handle;
|
||||
int error = ENR_init(&p_handle);
|
||||
|
||||
error = ENR_close(&p_handle);
|
||||
ASSERT_EQ(-1, error);
|
||||
ASSERT_TRUE(p_handle != NULL);
|
||||
}
|
||||
|
||||
|
||||
|
||||
class OutputapiTest : public testing::Test {
|
||||
protected:
|
||||
// SetUp for OutputapiTest fixture
|
||||
virtual void SetUp() {
|
||||
std::string path = std::string(PROJECT_HOME) + std::string(DATA_PATH);
|
||||
|
||||
error = ENR_init(&p_handle);
|
||||
ENR_clearError(p_handle);
|
||||
error = ENR_open(p_handle, path.c_str());
|
||||
}
|
||||
|
||||
// TearDown for OutputapiTest fixture
|
||||
virtual void TearDown() {
|
||||
ENR_free((void**)&array);
|
||||
error = ENR_close(&p_handle);
|
||||
}
|
||||
|
||||
int error = 0;
|
||||
ENR_Handle p_handle = NULL;
|
||||
|
||||
float* array = NULL;
|
||||
int array_dim = 0;
|
||||
};
|
||||
|
||||
TEST_F(OutputapiTest, getNetSizeTest) {
|
||||
int* i_array = NULL;
|
||||
// nodes, tanks, links, pumps, valves
|
||||
int ref_array[5] = {11,2,13,1,0};
|
||||
|
||||
error = ENR_getNetSize(p_handle, &i_array, &array_dim);
|
||||
ASSERT_EQ(0, error);
|
||||
|
||||
for (int i = 0; i < array_dim; i++)
|
||||
EXPECT_EQ(ref_array[i], i_array[i]);
|
||||
|
||||
ENR_free((void**)&i_array);
|
||||
}
|
||||
|
||||
TEST_F(OutputapiTest, getElementName) {
|
||||
char* name = new char[MAXID];
|
||||
int length, index = 1;
|
||||
|
||||
error = ENR_getElementName(p_handle, ENR_node, index, &name, &length);
|
||||
ASSERT_EQ(0, error);
|
||||
|
||||
EXPECT_STREQ("10", name);
|
||||
|
||||
delete(name);
|
||||
}
|
||||
|
||||
TEST_F(OutputapiTest, getNodeAttributeTest) {
|
||||
float ref_array[11] = { 1.0,
|
||||
0.44407997,
|
||||
0.43766347,
|
||||
0.42827705,
|
||||
0.41342604,
|
||||
0.42804748,
|
||||
0.44152543,
|
||||
0.40502965,
|
||||
0.38635802,
|
||||
1.0,
|
||||
0.96745253 };
|
||||
|
||||
error = ENR_getNodeAttribute(p_handle, 1, ENR_quality, &array, &array_dim);
|
||||
ASSERT_EQ(0, error);
|
||||
|
||||
for (int i = 0; i < array_dim; i++)
|
||||
EXPECT_FLOAT_EQ(ref_array[i], array[i]);
|
||||
}
|
||||
|
||||
TEST_F(OutputapiTest, getLinkAttributeTest) {
|
||||
float ref_array[13] = { 1848.5812,
|
||||
1220.4274,
|
||||
130.11162,
|
||||
187.6893,
|
||||
119.8884,
|
||||
40.464489,
|
||||
-748.58112,
|
||||
478.15378,
|
||||
191.73459,
|
||||
30.111609,
|
||||
140.46449,
|
||||
59.535515,
|
||||
1848.5812};
|
||||
|
||||
error = ENR_getLinkAttribute(p_handle, 1, ENR_flow, &array ,&array_dim);
|
||||
ASSERT_EQ(0, error);
|
||||
|
||||
for (int i = 0; i < array_dim; i++)
|
||||
EXPECT_FLOAT_EQ(ref_array[i], array[i]);
|
||||
}
|
||||
|
||||
TEST_F(OutputapiTest, getNodeResultTest) {
|
||||
float ref_array[4] = {0.041142918,
|
||||
150.0,
|
||||
987.98358,
|
||||
120.45029};
|
||||
|
||||
error = ENR_getNodeResult(p_handle, 1, 2, &array, &array_dim);
|
||||
ASSERT_EQ(0, error);
|
||||
|
||||
for (int i = 0; i < array_dim; i++)
|
||||
EXPECT_FLOAT_EQ(ref_array[i], array[i]);
|
||||
}
|
||||
|
||||
TEST_F(OutputapiTest, getLinkResultTest) {
|
||||
float ref_array[8] = {0.58586824,
|
||||
1892.2433,
|
||||
0.0,
|
||||
-200.71875,
|
||||
1.0,
|
||||
3.0,
|
||||
1.0,
|
||||
0.0};
|
||||
|
||||
error = ENR_getLinkResult(p_handle, 24, 13, &array, &array_dim);
|
||||
ASSERT_EQ(0, error);
|
||||
|
||||
for (int i = 0; i < array_dim; i++)
|
||||
EXPECT_FLOAT_EQ(ref_array[i], array[i]);
|
||||
}
|
||||
|
||||
TEST_F(OutputapiTest, getNodeSeriesTest){
|
||||
float ref_array[10] = {119.25731,
|
||||
120.45029,
|
||||
121.19854,
|
||||
122.00622,
|
||||
122.37414,
|
||||
122.8122,
|
||||
122.82034,
|
||||
122.90379,
|
||||
123.40434,
|
||||
123.81807};
|
||||
|
||||
error = ENR_getNodeSeries(p_handle, 2, ENR_pressure, 0, 10, &array, &array_dim);
|
||||
ASSERT_EQ(0, error);
|
||||
|
||||
for (int i = 0; i < array_dim; i++)
|
||||
EXPECT_FLOAT_EQ(ref_array[i], array[i]);
|
||||
}
|
||||
|
||||
TEST_F(OutputapiTest, getLinkSeriesTest) {
|
||||
float ref_array[10] = {1234.2072,
|
||||
1220.4274,
|
||||
1164.4,
|
||||
1154.8175,
|
||||
1100.0635,
|
||||
1094.759,
|
||||
1041.7854,
|
||||
1040.7617,
|
||||
1087.556,
|
||||
1082.5011};
|
||||
|
||||
error = ENR_getLinkSeries(p_handle, 2, ENR_flow, 0, 10, &array, &array_dim);
|
||||
ASSERT_EQ(0, error);
|
||||
|
||||
for (int i = 0; i < array_dim; i++)
|
||||
EXPECT_FLOAT_EQ(ref_array[i], array[i]);
|
||||
}
|
||||
|
||||
TEST_F(OutputapiTest, getNetReactsTest) {
|
||||
float ref_array[4] = {18806.59,
|
||||
85424.438,
|
||||
115174.05,
|
||||
238972.66};
|
||||
|
||||
error = ENR_getNetReacts(p_handle, &array, &array_dim);
|
||||
ASSERT_EQ(0, error);
|
||||
|
||||
for (int i = 0; i < array_dim; i++)
|
||||
EXPECT_FLOAT_EQ(ref_array[i], array[i]);
|
||||
}
|
||||
|
||||
TEST_F(OutputapiTest, getEnergyUsageTest) {
|
||||
float ref_array[6] = {57.712959,
|
||||
75.0,
|
||||
880.41583,
|
||||
96.254318,
|
||||
96.707115,
|
||||
0.0};
|
||||
|
||||
int linkIdx;
|
||||
|
||||
error = ENR_getEnergyUsage(p_handle, 1, &linkIdx, &array, &array_dim);
|
||||
ASSERT_EQ(0, error);
|
||||
|
||||
for (int i = 0; i < array_dim; i++)
|
||||
EXPECT_FLOAT_EQ(ref_array[i], array[i]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
GTEST_API_ int main(int argc, char **argv) {
|
||||
|
||||
printf("Running main() from gtest_main.cc\n");
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
46
tools/gen-config.sh
Executable file
46
tools/gen-config.sh
Executable file
@@ -0,0 +1,46 @@
|
||||
#! /bin/bash
|
||||
|
||||
#
|
||||
# gen-config.sh - Generates nrtest app configuration file for test executable
|
||||
#
|
||||
# Date Created: 10/16/2017
|
||||
#
|
||||
# Author: Michael E. Tryby
|
||||
# US EPA - ORD/NRMRL
|
||||
#
|
||||
# Arguments:
|
||||
# 1 - absolute path to test executable
|
||||
#
|
||||
# NOT IMPLEMENTED YET
|
||||
# 2 - test executable version number
|
||||
# 3 - build description
|
||||
#
|
||||
|
||||
unameOut="$(uname -s)"
|
||||
case "${unameOut}" in
|
||||
Linux*) ;&
|
||||
Darwin*) abs_build_path=$1
|
||||
test_cmd="runepanet"
|
||||
;;
|
||||
|
||||
MINGW*) ;&
|
||||
MSYS*) # Remove leading '/c' from file path for nrtest
|
||||
abs_build_path="$( echo "$1" | sed -e 's#/c##' )"
|
||||
test_cmd="runepanet.exe"
|
||||
;;
|
||||
|
||||
*) # Machine unknown
|
||||
esac
|
||||
|
||||
version=""
|
||||
build_description=""
|
||||
|
||||
cat<<EOF
|
||||
{
|
||||
"name" : "epanet",
|
||||
"version" : "${version}",
|
||||
"description" : "${build_description}",
|
||||
"setup_script" : "",
|
||||
"exe" : "${abs_build_path}/${test_cmd}"
|
||||
}
|
||||
EOF
|
||||
136
tools/nrtest-epanet/main.py
Normal file
136
tools/nrtest-epanet/main.py
Normal file
@@ -0,0 +1,136 @@
|
||||
|
||||
import numpy as np
|
||||
import time
|
||||
import cStringIO
|
||||
import itertools as it
|
||||
|
||||
import epanet_reader as er
|
||||
|
||||
def result_compare(path_test, path_ref, comp_args):
|
||||
|
||||
isclose = True
|
||||
close = 0
|
||||
notclose = 0
|
||||
equal = 0
|
||||
total = 0
|
||||
output = cStringIO.StringIO()
|
||||
eps = np.finfo(float).eps
|
||||
|
||||
start = time.time()
|
||||
|
||||
test_reader = er.reader(path_test)
|
||||
ref_reader = er.reader(path_ref)
|
||||
|
||||
for test, ref in it.izip(test_reader, ref_reader):
|
||||
total += 1
|
||||
if total%100000 == 0:
|
||||
print(total)
|
||||
|
||||
if test.size != ref.size:
|
||||
raise ValueError('Inconsistent lengths')
|
||||
|
||||
# Skip results if they are zero or equal
|
||||
if np.array_equal(test, ref):
|
||||
equal += 1
|
||||
continue
|
||||
else:
|
||||
try:
|
||||
np.testing.assert_allclose(test, ref, 1.0e-06, 2*eps)
|
||||
close += 1
|
||||
|
||||
except AssertionError as ae:
|
||||
notclose += 1
|
||||
output.write(str(ae))
|
||||
output.write('\n\n')
|
||||
continue
|
||||
|
||||
stop = time.time()
|
||||
|
||||
print(output.getvalue())
|
||||
output.close()
|
||||
|
||||
print('equal: %d close: %d notclose: %d total: %d in %f (sec)\n' %
|
||||
(equal, close, notclose, total, (stop - start)))
|
||||
|
||||
if notclose > 0:
|
||||
print('%d differences found\n' % notclose)
|
||||
isclose = False
|
||||
|
||||
return isclose
|
||||
|
||||
|
||||
|
||||
from nrtest.testsuite import TestSuite
|
||||
from nrtest.compare import compare_testsuite, validate_testsuite
|
||||
|
||||
def nrtest_compare(path_test, path_ref, (comp_args)):
|
||||
|
||||
ts_new = TestSuite.read_benchmark(path_test)
|
||||
ts_old = TestSuite.read_benchmark(path_ref)
|
||||
|
||||
if not validate_testsuite(ts_new) or not validate_testsuite(ts_old):
|
||||
exit(1)
|
||||
|
||||
try:
|
||||
# logging.info('Found %i tests' % len(ts_new.tests))
|
||||
compatible = compare_testsuite(ts_new, ts_old, comp_args[0], comp_args[1])
|
||||
except KeyboardInterrupt:
|
||||
# logging.warning('Process interrupted by user')
|
||||
compatible = False
|
||||
# else:
|
||||
# logging.info('Finished')
|
||||
|
||||
# Non-zero exit code indicates failure
|
||||
exit(not compatible)
|
||||
|
||||
def nrtest_execute(app_path, test_path, output_path):
|
||||
import logging
|
||||
import glob
|
||||
from os import listdir
|
||||
from os.path import exists, isfile, isdir, join
|
||||
from nrtest.execute import execute_testsuite, validate_testsuite
|
||||
|
||||
# for path in test_path + [app_path]:
|
||||
# if not exists(path):
|
||||
# logging.error('Could not find path: "%s"' % path)
|
||||
|
||||
test_dirs = glob.glob(test_path)
|
||||
test_files = [p for p in test_path if isfile(p)]
|
||||
test_files += [p for d in test_dirs for p in glob.glob(d + '*.json')]
|
||||
# if p.endswith('.json')]
|
||||
|
||||
test_files = list(set(test_files)) # remove duplicates
|
||||
|
||||
ts = TestSuite.read_config(app_path, test_files, output_path)
|
||||
|
||||
if not validate_testsuite(ts):
|
||||
exit(1)
|
||||
|
||||
try:
|
||||
logging.info('Found %i tests' % len(test_files))
|
||||
success = execute_testsuite(ts)
|
||||
ts.write_manifest()
|
||||
except KeyboardInterrupt:
|
||||
logging.warning('Process interrupted by user')
|
||||
success = False
|
||||
else:
|
||||
logging.info('Finished')
|
||||
|
||||
# Non-zero exit code indicates failure
|
||||
exit(not success)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# app_path = "apps\\swmm-5.1.11.json"
|
||||
# test_path = "tests\\examples\\example1.json"
|
||||
# output_path = "benchmarks\\test\\"
|
||||
# nrtest_execute(app_path, test_path, output_path)
|
||||
|
||||
# test_path = "C:\\Users\\mtryby\\Workspace\\GitRepo\\Local\\epanet-testsuite\\benchmarks\\v2011a"
|
||||
# ref_path = "C:\\Users\\mtryby\\Workspace\\GitRepo\\Local\\epanet-testsuite\\benchmarks\\v2012"
|
||||
# print(nrtest_compare(test_path, ref_path, (0.001, 0.0)))
|
||||
|
||||
|
||||
path_test = "C:\\Users\\mtryby\\Workspace\\GitRepo\\Local\\epanet-testsuite\\benchmarks\\v2011a\\Example_3\\example3.out"
|
||||
path_ref = "C:\\Users\\mtryby\\Workspace\\GitRepo\\Local\\epanet-testsuite\\benchmarks\\v2012\\Example_3\\example3.out"
|
||||
result_compare(path_test, path_ref, (0.001, 0.0))
|
||||
121
tools/nrtest-epanet/nrtest_epanet/__init__.py
Normal file
121
tools/nrtest-epanet/nrtest_epanet/__init__.py
Normal file
@@ -0,0 +1,121 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# __init__.py - nrtest_epanet module
|
||||
#
|
||||
# Author: Michael E. Tryby
|
||||
# US EPA - ORD/NRMRL
|
||||
#
|
||||
|
||||
'''
|
||||
Numerical regression testing (nrtest) plugin for comparing EPANET binary results
|
||||
files and EPANET text based report files.
|
||||
'''
|
||||
|
||||
# system imports
|
||||
import itertools as it
|
||||
|
||||
# third party imports
|
||||
import header_detail_footer as hdf
|
||||
import numpy as np
|
||||
|
||||
# project import
|
||||
import nrtest_epanet.output_reader as ordr
|
||||
|
||||
|
||||
__author__ = "Michael Tryby"
|
||||
__copyright__ = "None"
|
||||
__credits__ = "Colleen Barr, Maurizio Cingi, Mark Gray, David Hall, Bryant McDonnell"
|
||||
__license__ = "CC0 1.0 Universal"
|
||||
|
||||
__version__ = "0.4.0"
|
||||
__date__ = "September 6, 2017"
|
||||
|
||||
__maintainer__ = "Michael Tryby"
|
||||
__email__ = "tryby.michael@epa.gov"
|
||||
__status = "Development"
|
||||
|
||||
|
||||
def epanet_allclose_compare(path_test, path_ref, rtol, atol):
|
||||
'''
|
||||
Compares results in two EPANET binary files. Using the comparison criteria
|
||||
described in the numpy assert_allclose documentation.
|
||||
|
||||
(test_value - ref_value) <= atol + rtol * abs(ref_value)
|
||||
|
||||
Returns true if all of the results in the two binary files meet the
|
||||
comparison criteria; otherwise, an AssertionError is thrown.
|
||||
|
||||
Numpy allclose is quite expensive to evaluate. Test and reference results
|
||||
are checked to see if they are equal before being compared using the
|
||||
allclose criteria. This reduces comparison times significantly.
|
||||
|
||||
Arguments:
|
||||
path_test - path to result file being tested
|
||||
path_ref - path to reference result file
|
||||
rtol - relative tolerance
|
||||
atol - absolute tolerance
|
||||
|
||||
Returns:
|
||||
True
|
||||
|
||||
Raises:
|
||||
ValueError()
|
||||
AssertionError()
|
||||
...
|
||||
'''
|
||||
for (test, ref) in it.izip(ordr.output_generator(path_test),
|
||||
ordr.output_generator(path_ref)):
|
||||
|
||||
if len(test) != len(ref):
|
||||
raise ValueError('Inconsistent lengths')
|
||||
|
||||
# Skip over arrays that are equal
|
||||
if np.array_equal(test, ref):
|
||||
continue
|
||||
else:
|
||||
np.testing.assert_allclose(test, ref, rtol, atol)
|
||||
|
||||
return True
|
||||
|
||||
# def epanet_better_compare(path_test, path_ref, rtol, atol):
|
||||
# '''
|
||||
# If you don't like assert_allclose you can add another function here.
|
||||
# '''
|
||||
# pass
|
||||
|
||||
def epanet_report_compare(path_test, path_ref, rtol, atol):
|
||||
'''
|
||||
Compares results in two report files ignoring contents of header and footer.
|
||||
|
||||
Note: Header is 11 lines with report summary turned off. This test will fail
|
||||
if the report summary is turned on because a time stamp is being written
|
||||
immediately after it.
|
||||
|
||||
Arguments:
|
||||
path_test - path to result file being tested
|
||||
path_ref - path to reference result file
|
||||
rtol - ignored
|
||||
atol - ignored
|
||||
|
||||
Returns:
|
||||
True or False
|
||||
|
||||
Raises:
|
||||
HeaderError()
|
||||
FooterError()
|
||||
RunTimeError()
|
||||
...
|
||||
'''
|
||||
HEADER = 11
|
||||
FOOTER = 3
|
||||
|
||||
with open(path_test ,'r') as ftest, open(path_ref, 'r') as fref:
|
||||
|
||||
for (test_line, ref_line) in it.izip(hdf.parse(ftest, HEADER, FOOTER)[1],
|
||||
hdf.parse(fref, HEADER, FOOTER)[1]):
|
||||
|
||||
if test_line != ref_line:
|
||||
return False
|
||||
|
||||
return True
|
||||
69
tools/nrtest-epanet/nrtest_epanet/output_reader.py
Normal file
69
tools/nrtest-epanet/nrtest_epanet/output_reader.py
Normal file
@@ -0,0 +1,69 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# output_reader.py
|
||||
#
|
||||
# Date Created: Aug 31, 2016
|
||||
#
|
||||
# Author: Michael E. Tryby
|
||||
# US EPA - ORD/NRMRL
|
||||
#
|
||||
'''
|
||||
The module output_reader provides the class used to implement the output
|
||||
generator.
|
||||
'''
|
||||
|
||||
# project import
|
||||
import epanet_output as oapi
|
||||
|
||||
|
||||
def output_generator(path_ref):
|
||||
'''
|
||||
The output_generator is designed to iterate over an EPANET binary file and
|
||||
yield element attributes. It is useful for comparing contents of binary
|
||||
files for numerical regression testing.
|
||||
|
||||
The generator yields a Python list containing element attributes.
|
||||
|
||||
Arguments:
|
||||
path_ref - path to result file
|
||||
|
||||
Raises:
|
||||
Exception()
|
||||
...
|
||||
'''
|
||||
with OutputReader(path_ref) as br:
|
||||
|
||||
for period_index in range(0, br.report_periods()):
|
||||
for element_type in oapi.ElementType:
|
||||
for attribute in br.elementAttributes[element_type]:
|
||||
|
||||
yield br.element_attribute(element_type, period_index, attribute)
|
||||
|
||||
|
||||
class OutputReader():
|
||||
'''
|
||||
Provides a minimal API used to implement output_generator.
|
||||
'''
|
||||
def __init__(self, filename):
|
||||
self.filepath = filename
|
||||
self.handle = None
|
||||
self.elementAttributes = {oapi.ElementType.NODE: oapi.NodeAttribute,
|
||||
oapi.ElementType.LINK: oapi.LinkAttribute}
|
||||
|
||||
self.getElementAttribute = {oapi.ElementType.NODE: oapi.enr_get_node_attribute,
|
||||
oapi.ElementType.LINK: oapi.enr_get_link_attribute}
|
||||
|
||||
def __enter__(self):
|
||||
self.handle = oapi.enr_init()
|
||||
oapi.enr_open(self.handle, self.filepath.encode())
|
||||
return self
|
||||
|
||||
def __exit__(self, type, value, traceback):
|
||||
self.handle = oapi.enr_close()
|
||||
|
||||
def report_periods(self):
|
||||
return oapi.enr_get_times(self.handle, oapi.Time.NUM_PERIODS)
|
||||
|
||||
def element_attribute(self, element_type, time_index, attribute):
|
||||
return self.getElementAttribute[element_type](self.handle, time_index, attribute)
|
||||
44
tools/nrtest-epanet/setup.py
Normal file
44
tools/nrtest-epanet/setup.py
Normal file
@@ -0,0 +1,44 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# setup.py
|
||||
#
|
||||
# Created on Aug 30, 2016
|
||||
# Author: Michael E. Tryby
|
||||
# US EPA - ORD/NRMRL
|
||||
#
|
||||
''' Setup up script for nrtest_epanet package. '''
|
||||
|
||||
try:
|
||||
from setuptools import setup
|
||||
except ImportError:
|
||||
from distutils.core import setup
|
||||
|
||||
entry_points = {
|
||||
'nrtest.compare': [
|
||||
'epanet allclose = nrtest_epanet:epanet_allclose_compare',
|
||||
'epanet report = nrtest_epanet:epanet_report_compare',
|
||||
# Add entry point for new comparison functions here
|
||||
]
|
||||
}
|
||||
|
||||
setup(
|
||||
name='nrtest-epanet',
|
||||
version='0.3.0',
|
||||
description="EPANET extension for nrtest",
|
||||
|
||||
author="Michael E. Tryby",
|
||||
author_email='tryby.michael@epa.gov',
|
||||
url='https://github.com/USEPA',
|
||||
|
||||
packages=['nrtest_epanet',],
|
||||
entry_points=entry_points,
|
||||
|
||||
install_requires=[
|
||||
'header_detail_footer>=2.3',
|
||||
'nrtest>=0.2.0',
|
||||
'numpy>=1.7.0',
|
||||
'epanet_output>=0.4.0'
|
||||
],
|
||||
keywords='nrtest_epanet'
|
||||
)
|
||||
@@ -1 +0,0 @@
|
||||
'''
|
||||
File diff suppressed because one or more lines are too long
@@ -1,502 +0,0 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// outputapi.c -- API for reading results from EPANet binary output file
|
||||
//
|
||||
// Version: 0.10
|
||||
// Date: 08/05/14
|
||||
// Date: 05/21/14
|
||||
//
|
||||
// Author: Michael E. Tryby
|
||||
// US EPA - NRMRL
|
||||
//
|
||||
// Purpose: Output API provides an interface for retrieving results from
|
||||
// an EPANet binary output file.
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include "outputapi.h"
|
||||
|
||||
#define INT4 int
|
||||
#define REAL4 float
|
||||
#define RECORDSIZE 4 // number of bytes per file record
|
||||
|
||||
#define MEMCHECK(x) (((x) == NULL) ? 411 : 0 )
|
||||
|
||||
#define MINNREC 14 // minimum allowable number of records
|
||||
#define NNODERESULTS 4 // number of result fields for nodes
|
||||
#define NLINKRESULTS 8 // number of result fields for links
|
||||
|
||||
|
||||
struct ENResultsAPI {
|
||||
char name[MAXFNAME + 1]; // file path/name
|
||||
bool isOpened; // current state (CLOSED = 0, OPEN = 1)
|
||||
FILE *file; // FILE structure pointer
|
||||
|
||||
INT4 nodeCount, tankCount, linkCount, pumpCount, valveCount;
|
||||
INT4 reportStart, reportStep, simDuration, nPeriods;
|
||||
|
||||
INT4 flowFlag, pressFlag;
|
||||
|
||||
INT4 outputStartPos; // starting file position of output data
|
||||
INT4 bytesPerPeriod; // bytes saved per simulation time period
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Local functions
|
||||
//-----------------------------------------------------------------------------
|
||||
float getNodeValue(ENResultsAPI*, int, int, ENR_NodeAttribute);
|
||||
float getLinkValue(ENResultsAPI*, int, int, ENR_LinkAttribute);
|
||||
|
||||
|
||||
ENResultsAPI* DLLEXPORT ENR_alloc(void)
|
||||
{
|
||||
return malloc(sizeof(struct ENResultsAPI));
|
||||
}
|
||||
|
||||
int DLLEXPORT ENR_open(ENResultsAPI* enrapi, const char* path)
|
||||
//
|
||||
// Purpose: Open the output binary file and read epilogue
|
||||
//
|
||||
{
|
||||
int magic1, magic2, errCode, version;
|
||||
|
||||
strncpy(enrapi->name, path, MAXFNAME);
|
||||
enrapi->isOpened = false;
|
||||
|
||||
// Attempt to open binary output file for reading only
|
||||
if ((enrapi->file = fopen(path, "rb")) == NULL)
|
||||
return 434;
|
||||
else
|
||||
enrapi->isOpened = true;
|
||||
|
||||
// Fast forward to end and check for minimum number of records
|
||||
fseek(enrapi->file, 0L, SEEK_END);
|
||||
if (ftell(enrapi->file) < MINNREC*RECORDSIZE) {
|
||||
fclose(enrapi->file);
|
||||
// Error run terminated no results in binary file
|
||||
return 435;
|
||||
}
|
||||
|
||||
// Fast forward to end and read file epilogue
|
||||
fseek(enrapi->file, -3*RECORDSIZE, SEEK_END);
|
||||
fread(&(enrapi->nPeriods), RECORDSIZE, 1, enrapi->file);
|
||||
fread(&errCode, RECORDSIZE, 1, enrapi->file);
|
||||
fread(&magic2, RECORDSIZE, 1, enrapi->file);
|
||||
|
||||
// Rewind and read magic number from beginning of file
|
||||
fseek(enrapi->file, 0L, SEEK_SET);
|
||||
fread(&magic1, RECORDSIZE, 1, enrapi->file);
|
||||
|
||||
// Perform error checks
|
||||
if (magic1 != magic2 || errCode != 0 || enrapi->nPeriods == 0) {
|
||||
fclose(enrapi->file);
|
||||
// Error run terminated no results in binary file
|
||||
return 435;
|
||||
}
|
||||
|
||||
// Otherwise read network size
|
||||
fread(&version, RECORDSIZE, 1, enrapi->file);
|
||||
fread(&(enrapi->nodeCount), RECORDSIZE, 1, enrapi->file);
|
||||
fread(&(enrapi->tankCount), RECORDSIZE, 1, enrapi->file);
|
||||
fread(&(enrapi->linkCount), RECORDSIZE, 1, enrapi->file);
|
||||
fread(&(enrapi->pumpCount), RECORDSIZE, 1, enrapi->file);
|
||||
|
||||
// Jump ahead and read flow and pressure units
|
||||
fseek(enrapi->file, 3*RECORDSIZE, SEEK_CUR);
|
||||
fread(&(enrapi->flowFlag), RECORDSIZE, 1, enrapi->file);
|
||||
fread(&(enrapi->pressFlag), RECORDSIZE, 1, enrapi->file);
|
||||
|
||||
// Jump ahead and read time information
|
||||
fseek(enrapi->file, RECORDSIZE, SEEK_CUR);
|
||||
fread(&(enrapi->reportStart), RECORDSIZE, 1, enrapi->file);
|
||||
fread(&(enrapi->reportStep), RECORDSIZE, 1, enrapi->file);
|
||||
fread(&(enrapi->simDuration), RECORDSIZE, 1, enrapi->file);
|
||||
|
||||
// Compute positions and offsets for retrieving data
|
||||
enrapi->outputStartPos = 884;
|
||||
enrapi->outputStartPos += 32*enrapi->nodeCount + 32*enrapi->linkCount;
|
||||
enrapi->outputStartPos += 12*enrapi->linkCount+ 8*enrapi->tankCount
|
||||
+ 4*enrapi->nodeCount + 8*enrapi->linkCount;
|
||||
enrapi->outputStartPos += 28*enrapi->pumpCount + 4;
|
||||
|
||||
enrapi->bytesPerPeriod = 16*enrapi->nodeCount + 32*enrapi->linkCount;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int DLLEXPORT ENR_getNetSize(ENResultsAPI* enrapi, ENR_ElementCount code, int* count)
|
||||
//
|
||||
// Purpose: Returns network size
|
||||
//
|
||||
{
|
||||
*count = -1;
|
||||
if (enrapi->isOpened) {
|
||||
switch (code)
|
||||
{
|
||||
case ENR_nodeCount: *count = enrapi->nodeCount; break;
|
||||
case ENR_tankCount: *count = enrapi->tankCount; break;
|
||||
case ENR_linkCount: *count = enrapi->linkCount; break;
|
||||
case ENR_pumpCount: *count = enrapi->pumpCount; break;
|
||||
case ENR_valveCount: *count = enrapi->valveCount; break;
|
||||
default: return 421;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return 412;
|
||||
}
|
||||
|
||||
int DLLEXPORT ENR_getUnits(ENResultsAPI* enrapi, ENR_Unit code, int* unitFlag)
|
||||
//
|
||||
// Purpose: Returns pressure and flow units
|
||||
//
|
||||
{
|
||||
*unitFlag = -1;
|
||||
if (enrapi->isOpened) {
|
||||
switch (code)
|
||||
{
|
||||
case ENR_flowUnits: *unitFlag = enrapi->flowFlag; break;
|
||||
case ENR_pressUnits: *unitFlag = enrapi->pressFlag; break;
|
||||
default: return 421;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return 412;
|
||||
}
|
||||
|
||||
int DLLEXPORT ENR_getTimes(ENResultsAPI* enrapi, ENR_Time code, int* time)
|
||||
//
|
||||
// Purpose: Returns report and simulation time related parameters.
|
||||
//
|
||||
{
|
||||
*time = -1;
|
||||
if (enrapi->isOpened) {
|
||||
switch (code)
|
||||
{
|
||||
case ENR_reportStart: *time = enrapi->reportStart; break;
|
||||
case ENR_reportStep: *time = enrapi->reportStep; break;
|
||||
case ENR_simDuration: *time = enrapi->simDuration; break;
|
||||
case ENR_numPeriods: *time = enrapi->nPeriods; break;
|
||||
default: return 421;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return 412;
|
||||
}
|
||||
|
||||
|
||||
float* ENR_newOutValueSeries(ENResultsAPI* enrapi, int seriesStart,
|
||||
int seriesLength, int* length, int* errcode)
|
||||
//
|
||||
// Purpose: Allocates memory for outValue Series.
|
||||
//
|
||||
// Warning: Caller must free memory allocated by this function using ENR_free().
|
||||
//
|
||||
{
|
||||
int size;
|
||||
float* array;
|
||||
|
||||
if (enrapi->isOpened) {
|
||||
|
||||
size = seriesLength - seriesStart;
|
||||
if (size > enrapi->nPeriods)
|
||||
size = enrapi->nPeriods;
|
||||
|
||||
// Allocate memory for outValues
|
||||
array = (float*) calloc(size + 1, sizeof(float));
|
||||
*errcode = (MEMCHECK(array));
|
||||
|
||||
*length = size;
|
||||
return array;
|
||||
}
|
||||
*errcode = 412;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
float* ENR_newOutValueArray(ENResultsAPI* enrapi, ENR_ApiFunction func,
|
||||
ENR_ElementType type, int* length, int* errcode)
|
||||
//
|
||||
// Purpose: Allocates memory for outValue Array.
|
||||
//
|
||||
// Warning: Caller must free memory allocated by this function using ENR_free().
|
||||
//
|
||||
{
|
||||
int size;
|
||||
float* array;
|
||||
|
||||
if (enrapi->isOpened) {
|
||||
switch (func)
|
||||
{
|
||||
case ENR_getAttribute:
|
||||
if (type == ENR_node)
|
||||
size = enrapi->nodeCount;
|
||||
else
|
||||
size = enrapi->linkCount;
|
||||
break;
|
||||
case ENR_getResult:
|
||||
if (type == ENR_node)
|
||||
size = NNODERESULTS;
|
||||
else
|
||||
size = NLINKRESULTS;
|
||||
break;
|
||||
default: *errcode = 421;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Allocate memory for outValues
|
||||
array = (float*) calloc(size, sizeof(float));
|
||||
*errcode = (MEMCHECK(array));
|
||||
|
||||
*length = size;
|
||||
return array;
|
||||
}
|
||||
*errcode = 412;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
int DLLEXPORT ENR_getNodeSeries(ENResultsAPI* enrapi, int nodeIndex, ENR_NodeAttribute attr,
|
||||
int seriesStart, int seriesLength, float* outValueSeries, int* length)
|
||||
//
|
||||
// What if timeIndex 0? length 0?
|
||||
//
|
||||
// Purpose: Get time series results for particular attribute. Specify series
|
||||
// start and length using seriesStart and seriesLength respectively.
|
||||
//
|
||||
{
|
||||
int k;
|
||||
|
||||
if (enrapi->isOpened) {
|
||||
|
||||
// Check memory for outValues
|
||||
if (outValueSeries == NULL) return 411;
|
||||
|
||||
// loop over and build time series
|
||||
for (k = 0; k <= seriesLength; k++)
|
||||
outValueSeries[k] = getNodeValue(enrapi, seriesStart + 1 + k,
|
||||
nodeIndex, attr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
// Error no results to report on binary file not opened
|
||||
return 412;
|
||||
}
|
||||
|
||||
int DLLEXPORT ENR_getLinkSeries(ENResultsAPI* enrapi, int linkIndex, ENR_LinkAttribute attr,
|
||||
int seriesStart, int seriesLength, float* outValueSeries)
|
||||
//
|
||||
// What if timeIndex 0? length 0?
|
||||
//
|
||||
// Purpose: Get time series results for particular attribute. Specify series
|
||||
// start and length using seriesStart and seriesLength respectively.
|
||||
//
|
||||
{
|
||||
int k;
|
||||
|
||||
if (enrapi->isOpened) {
|
||||
// Check memory for outValues
|
||||
if (outValueSeries == NULL) return 411;
|
||||
|
||||
// loop over and build time series
|
||||
for (k = 0; k <= seriesLength; k++)
|
||||
outValueSeries[k] = getLinkValue(enrapi, seriesStart +1 + k,
|
||||
linkIndex, attr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
// Error no results to report on binary file not opened
|
||||
return 412;
|
||||
}
|
||||
|
||||
|
||||
int DLLEXPORT ENR_getNodeAttribute(ENResultsAPI* enrapi, int timeIndex,
|
||||
ENR_NodeAttribute attr, float* outValueArray)
|
||||
//
|
||||
// Purpose: For all nodes at given time, get a particular attribute
|
||||
//
|
||||
{
|
||||
INT4 offset;
|
||||
|
||||
if (enrapi->isOpened) {
|
||||
// Check memory for outValues
|
||||
if (outValueArray == NULL) return 411;
|
||||
|
||||
// calculate byte offset to start time for series
|
||||
offset = enrapi->outputStartPos + (timeIndex)*enrapi->bytesPerPeriod;
|
||||
// add offset for node and attribute
|
||||
offset += (attr*enrapi->nodeCount)*RECORDSIZE;
|
||||
|
||||
fseek(enrapi->file, offset, SEEK_SET);
|
||||
fread(outValueArray, RECORDSIZE, enrapi->nodeCount, enrapi->file);
|
||||
|
||||
return 0;
|
||||
}
|
||||
// Error no results to report on binary file not opened
|
||||
return 412;
|
||||
}
|
||||
|
||||
int DLLEXPORT ENR_getLinkAttribute(ENResultsAPI* enrapi, int timeIndex,
|
||||
ENR_LinkAttribute attr, float* outValueArray)
|
||||
//
|
||||
// Purpose: For all links at given time, get a particular attribute
|
||||
//
|
||||
{
|
||||
INT4 offset;
|
||||
|
||||
if (enrapi->isOpened) {
|
||||
// Check memory for outValues
|
||||
if (outValueArray == NULL) return 411;
|
||||
|
||||
// calculate byte offset to start time for series
|
||||
offset = enrapi->outputStartPos + (timeIndex)*enrapi->bytesPerPeriod
|
||||
+ (NNODERESULTS*enrapi->nodeCount)*RECORDSIZE;
|
||||
// add offset for link and attribute
|
||||
offset += (attr*enrapi->linkCount)*RECORDSIZE;
|
||||
|
||||
fseek(enrapi->file, offset, SEEK_SET);
|
||||
fread(outValueArray, RECORDSIZE, enrapi->linkCount, enrapi->file);
|
||||
|
||||
return 0;
|
||||
}
|
||||
// Error no results to report on binary file not opened
|
||||
return 412;
|
||||
}
|
||||
|
||||
int DLLEXPORT ENR_getNodeResult(ENResultsAPI* enrapi, int timeIndex, int nodeIndex,
|
||||
float* outValueArray)
|
||||
//
|
||||
// Purpose: For a node at given time, get all attributes
|
||||
//
|
||||
{
|
||||
int j;
|
||||
|
||||
if (enrapi->isOpened) {
|
||||
// Check memory for outValues
|
||||
if (outValueArray == NULL) return 411;
|
||||
|
||||
for (j = 0; j < NNODERESULTS; j++)
|
||||
outValueArray[j] = getNodeValue(enrapi, timeIndex + 1, nodeIndex, j);
|
||||
|
||||
return 0;
|
||||
}
|
||||
// Error no results to report on binary file not opened
|
||||
return 412;
|
||||
}
|
||||
|
||||
int DLLEXPORT ENR_getLinkResult(ENResultsAPI* enrapi, int timeIndex, int linkIndex,
|
||||
float* outValueArray)
|
||||
//
|
||||
// Purpose: For a link at given time, get all attributes
|
||||
//
|
||||
{
|
||||
int j;
|
||||
|
||||
if (enrapi->isOpened) {
|
||||
// Check memory for outValues
|
||||
if (outValueArray == NULL) return 411;
|
||||
|
||||
for (j = 0; j < NLINKRESULTS; j++)
|
||||
outValueArray[j] = getLinkValue(enrapi, timeIndex + 1, linkIndex, j);
|
||||
|
||||
return 0;
|
||||
}
|
||||
// Error no results to report on binary file not opened
|
||||
return 412;
|
||||
}
|
||||
|
||||
int DLLEXPORT ENR_free(float* array)
|
||||
//
|
||||
// Purpose: frees memory allocated using ENR_newOutValueSeries() or
|
||||
// ENR_newOutValueArray()
|
||||
//
|
||||
{
|
||||
if (array != NULL)
|
||||
free(array);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int DLLEXPORT ENR_close(ENResultsAPI* enrapi)
|
||||
//
|
||||
// Purpose: Clean up after and close Output API
|
||||
//
|
||||
{
|
||||
if (enrapi->isOpened) {
|
||||
fclose(enrapi->file);
|
||||
free(enrapi);
|
||||
}
|
||||
// Error binary file not opened
|
||||
else return 412;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int DLLEXPORT ENR_errMessage(int errcode, char* errmsg, int n)
|
||||
//
|
||||
// Purpose: takes error code returns error message
|
||||
//
|
||||
// Input Error 411: no memory allocated for results
|
||||
// Input Error 412: no results binary file hasn't been opened
|
||||
// Input Error 421: invalid parameter code
|
||||
// File Error 434: unable to open binary output file
|
||||
// File Error 435: run terminated no results in binary file
|
||||
{
|
||||
switch (errcode)
|
||||
{
|
||||
case 411: strncpy(errmsg, ERR411, n); break;
|
||||
case 412: strncpy(errmsg, ERR412, n); break;
|
||||
case 421: strncpy(errmsg, ERR421, n); break;
|
||||
case 434: strncpy(errmsg, ERR434, n); break;
|
||||
case 435: strncpy(errmsg, ERR435, n); break;
|
||||
default: return 421;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
float getNodeValue(ENResultsAPI* enrapi, int timeIndex, int nodeIndex,
|
||||
ENR_NodeAttribute attr)
|
||||
//
|
||||
// Purpose: Retrieves an attribute value at a specified node and time
|
||||
//
|
||||
{
|
||||
REAL4 y;
|
||||
INT4 offset;
|
||||
|
||||
// calculate byte offset to start time for series
|
||||
offset = enrapi->outputStartPos + (timeIndex - 1)*enrapi->bytesPerPeriod;
|
||||
// add bytepos for node and attribute
|
||||
offset += (nodeIndex + attr*enrapi->nodeCount)*RECORDSIZE;
|
||||
|
||||
fseek(enrapi->file, offset, SEEK_SET);
|
||||
fread(&y, RECORDSIZE, 1, enrapi->file);
|
||||
|
||||
return y;
|
||||
}
|
||||
|
||||
float getLinkValue(ENResultsAPI* enrapi, int timeIndex, int linkIndex,
|
||||
ENR_LinkAttribute attr)
|
||||
//
|
||||
// Purpose: Retrieves an attribute value at a specified link and time
|
||||
//
|
||||
{
|
||||
REAL4 y;
|
||||
INT4 offset;
|
||||
|
||||
// Calculate byte offset to start time for series
|
||||
offset = enrapi->outputStartPos + (timeIndex - 1)*enrapi->bytesPerPeriod
|
||||
+ (NNODERESULTS*enrapi->nodeCount)*RECORDSIZE;
|
||||
// add bytepos for link and attribute
|
||||
offset += (linkIndex + attr*enrapi->linkCount)*RECORDSIZE;
|
||||
|
||||
fseek(enrapi->file, offset, SEEK_SET);
|
||||
fread(&y, RECORDSIZE, 1, enrapi->file);
|
||||
|
||||
return y;
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
/*
|
||||
* outputapi.h
|
||||
*
|
||||
* Created on: Jun 4, 2014
|
||||
* Author: mtryby
|
||||
*/
|
||||
|
||||
#ifndef OUTPUTAPI_H_
|
||||
#define OUTPUTAPI_H_
|
||||
|
||||
#define MAXFNAME 259
|
||||
|
||||
/*------------------- Error Messages --------------------*/
|
||||
#define ERR411 "Input Error 411: no memory allocated for results."
|
||||
#define ERR412 "Input Error 412: no results; binary file hasn't been opened."
|
||||
#define ERR421 "Input Error 421: invalid parameter code."
|
||||
#define ERR434 "File Error 434: unable to open binary output file."
|
||||
#define ERR435 "File Error 435: run terminated; no results in binary file."
|
||||
|
||||
|
||||
/* Epanet Results binary file API */
|
||||
typedef struct ENResultsAPI ENResultsAPI; // opaque struct object
|
||||
|
||||
typedef enum {
|
||||
ENR_node = 1,
|
||||
ENR_link = 2
|
||||
} ENR_ElementType;
|
||||
|
||||
typedef enum {
|
||||
ENR_getSeries = 1,
|
||||
ENR_getAttribute = 2,
|
||||
ENR_getResult = 3
|
||||
} ENR_ApiFunction;
|
||||
|
||||
typedef enum {
|
||||
ENR_nodeCount = 1,
|
||||
ENR_tankCount = 2,
|
||||
ENR_linkCount = 3,
|
||||
ENR_pumpCount = 4,
|
||||
ENR_valveCount = 5
|
||||
} ENR_ElementCount;
|
||||
|
||||
typedef enum {
|
||||
ENR_flowUnits = 1,
|
||||
ENR_pressUnits = 2
|
||||
} ENR_Unit;
|
||||
|
||||
typedef enum {
|
||||
ENR_reportStart = 1,
|
||||
ENR_reportStep = 2,
|
||||
ENR_simDuration = 3,
|
||||
ENR_numPeriods = 4
|
||||
}ENR_Time;
|
||||
|
||||
typedef enum {
|
||||
ENR_demand = 0,
|
||||
ENR_head = 1,
|
||||
ENR_pressure = 2,
|
||||
ENR_quality = 3
|
||||
} ENR_NodeAttribute;
|
||||
|
||||
typedef enum {
|
||||
ENR_flow = 0,
|
||||
ENR_velocity = 1,
|
||||
ENR_headloss = 2,
|
||||
ENR_avgQuality = 3,
|
||||
ENR_status = 4,
|
||||
ENR_setting = 5,
|
||||
ENR_rxRate = 6,
|
||||
ENT_frctnFctr = 7
|
||||
} ENR_LinkAttribute;
|
||||
|
||||
|
||||
#ifdef WINDOWS
|
||||
#ifdef __cplusplus
|
||||
#define DLLEXPORT extern "C" __declspec(dllexport) __stdcall
|
||||
#else
|
||||
#define DLLEXPORT __declspec(dllexport) __stdcall
|
||||
#endif
|
||||
#else
|
||||
#ifdef __cplusplus
|
||||
#define DLLEXPORT extern "C"
|
||||
#else
|
||||
#define DLLEXPORT
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
ENResultsAPI* DLLEXPORT ENR_alloc(void);
|
||||
|
||||
int DLLEXPORT ENR_open(ENResultsAPI* enrapi, const char* path);
|
||||
|
||||
int DLLEXPORT ENR_getNetSize(ENResultsAPI* enrapi, ENR_ElementCount code, int* count);
|
||||
int DLLEXPORT ENR_getUnits(ENResultsAPI* enrapi, ENR_Unit code, int* unitFlag);
|
||||
|
||||
float* ENR_newOutValueSeries(ENResultsAPI* enrapi, int seriesStart,
|
||||
int seriesLength, int* length, int* errcode);
|
||||
float* ENR_newOutValueArray(ENResultsAPI* enrapi, ENR_ApiFunction func,
|
||||
ENR_ElementType type, int* length, int* errcode);
|
||||
|
||||
int DLLEXPORT ENR_getNodeSeries(ENResultsAPI* enrapi, int nodeIndex, ENR_NodeAttribute attr,
|
||||
int timeIndex, int length, float* outValueSeries, int* len);
|
||||
int DLLEXPORT ENR_getLinkSeries(ENResultsAPI* enrapi, int linkIndex, ENR_LinkAttribute attr,
|
||||
int timeIndex, int length, float* outValueSeries);
|
||||
|
||||
int DLLEXPORT ENR_getNodeAttribute(ENResultsAPI* enrapi, int timeIndex,
|
||||
ENR_NodeAttribute attr, float* outValueArray);
|
||||
int DLLEXPORT ENT_getLinkAttribute(ENResultsAPI* enrapi, int timeIndex,
|
||||
ENR_LinkAttribute attr, float* outValueArray);
|
||||
|
||||
int DLLEXPORT ENR_getNodeResult(ENResultsAPI* enrapi, int timeIndex, int nodeIndex,
|
||||
float* outValueArray);
|
||||
int DLLEXPORT ENR_getLinkResult(ENResultsAPI* enrapi, int timeIndex, int linkIndex,
|
||||
float* outValueArray);
|
||||
|
||||
int DLLEXPORT ENR_free(float *array);
|
||||
int DLLEXPORT ENR_close(ENResultsAPI* enrapi);
|
||||
int DLLEXPORT ENR_errMessage(int errcode, char* errmsg, int n);
|
||||
|
||||
|
||||
#endif /* OUTPUTAPI_H_ */
|
||||
|
||||
16
tools/requirements.txt
Normal file
16
tools/requirements.txt
Normal file
@@ -0,0 +1,16 @@
|
||||
#
|
||||
# requirements.txt
|
||||
#
|
||||
# Date Created: 10/10/2017
|
||||
# Author: Michael E. Tryby
|
||||
# US EPA ORD/NRMRL
|
||||
#
|
||||
# Useful for configuring a python environment to run epanet-nrtestsuite.
|
||||
#
|
||||
# command:
|
||||
# $ pip install --src build/packages -r tools/requirements.txt
|
||||
#
|
||||
|
||||
-e git+https://github.com/OpenWaterAnalytics/nrtest.git@master#egg=nrtest
|
||||
-e ./tools/epanet-output
|
||||
-e ./tools/nrtest-epanet
|
||||
58
tools/run-nrtest.sh
Executable file
58
tools/run-nrtest.sh
Executable file
@@ -0,0 +1,58 @@
|
||||
#! /bin/bash
|
||||
|
||||
#
|
||||
# run-nrtest.sh - Runs numerical regression test
|
||||
#
|
||||
# Date Created: 10/16/2017
|
||||
#
|
||||
# Author: Michael E. Tryby
|
||||
# US EPA - ORD/NRMRL
|
||||
#
|
||||
# Arguments:
|
||||
# 1 - test suite path
|
||||
# 2 - version/build identifier
|
||||
#
|
||||
|
||||
run-nrtest()
|
||||
{
|
||||
|
||||
return_value=0
|
||||
|
||||
test_suite_path=$1
|
||||
|
||||
nrtest_execute_cmd="nrtest execute"
|
||||
test_app_path="apps/epanet-$2.json"
|
||||
tests="tests/examples"
|
||||
test_output_path="benchmark/epanet-$2"
|
||||
|
||||
nrtest_compare_cmd="nrtest compare"
|
||||
ref_output_path="benchmark/epanet-2012"
|
||||
rtol_value=0.1
|
||||
atol_value=0.0
|
||||
|
||||
|
||||
# change current directory to test_suite
|
||||
cd ${test_suite_path}
|
||||
|
||||
# clean test benchmark results
|
||||
rm -rf ${test_output_path}
|
||||
|
||||
echo INFO: Creating test benchmark
|
||||
nrtest_command="${nrtest_execute_cmd} ${test_app_path} ${tests} -o ${test_output_path}"
|
||||
echo INFO: "$nrtest_command"
|
||||
if ! [ $( $nrtest_command ) ]; then
|
||||
echo
|
||||
echo INFO: Comparing test and ref benchmarks
|
||||
nrtest_command="${nrtest_compare_cmd} ${test_output_path} ${ref_output_path} --rtol ${rtol_value} --atol ${atol_value}"
|
||||
echo INFO: "$nrtest_command"
|
||||
return_value=$( $nrtest_command )
|
||||
else
|
||||
echo ERROR: Test benchmark creation failed
|
||||
exit 1
|
||||
fi
|
||||
|
||||
return $return_value
|
||||
|
||||
}
|
||||
|
||||
run-nrtest $1 $2
|
||||
Reference in New Issue
Block a user