Merge pull request #465 from michaeltryby/dev-list

Dev list
This commit is contained in:
Michael Tryby
2019-04-24 18:57:46 -04:00
committed by GitHub
20 changed files with 1182 additions and 328 deletions

4
CMakeLists.txt Executable file → Normal file
View File

@@ -83,8 +83,8 @@ ENDIF (MSVC)
# configure file groups # configure file groups
file(GLOB EPANET_SOURCES RELATIVE ${PROJECT_SOURCE_DIR} src/*.c) file(GLOB EPANET_SOURCES RELATIVE ${PROJECT_SOURCE_DIR} src/*.c src/util/*.c)
file(GLOB EPANET_LIB_ALL RELATIVE ${PROJECT_SOURCE_DIR} src/*) file(GLOB EPANET_LIB_ALL RELATIVE ${PROJECT_SOURCE_DIR} src/* src/util/*)
# exclude epanet python API from the default build # exclude epanet python API from the default build
list(REMOVE_ITEM EPANET_LIB_ALL "src/epanet_py.c") list(REMOVE_ITEM EPANET_LIB_ALL "src/epanet_py.c")
source_group("Library" FILES ${EPANET_LIB_ALL}) source_group("Library" FILES ${EPANET_LIB_ALL})

View File

@@ -851,7 +851,14 @@ typedef struct Project *EN_Project;
These properties have units that depend on the units used for flow rate (see @ref Units). These properties have units that depend on the units used for flow rate (see @ref Units).
*/ */
int DLLEXPORT EN_setjuncdata(EN_Project ph, int index, double elev, double dmnd, int DLLEXPORT EN_setjuncdata(EN_Project ph, int index, double elev, double dmnd,
char *dmndpat); char *dmndpat);
int DLLEXPORT EN_adddemand(EN_Project p, int node_index, double demand,
char *demand_pattern, const char *category_name, int *demand_index);
int DLLEXPORT EN_removedemand(EN_Project p, int node_index, int demand_index);
/** /**
@brief Sets a group of properties for a tank node. @brief Sets a group of properties for a tank node.

133
src/demand.c Normal file
View File

@@ -0,0 +1,133 @@
/*
******************************************************************************
Project: OWA EPANET
Version: 2.2
Module: demand.c
Description: data for demand pattern list
Authors: see AUTHORS
Copyright: see AUTHORS
License: see LICENSE
Last Updated: 04/12/2019
******************************************************************************
*/
#ifdef _DEBUG
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
#else
#include <stdlib.h>
#endif
#include <string.h>
#include "demand.h"
typedef struct demand_data_s
{
double base_demand;
int pattern_index;
char *category_name;
} demand_data_t;
list_t *create_demand_list(double base_demand, int pattern_index, const char *category_name)
{
list_t *demand_list;
demand_data_t *demand_data;
demand_list = create_list(get_demand_data_size(), delete_demand_data);
if (!demand_list) return NULL;
demand_data = create_demand_data(base_demand, pattern_index, category_name);
if (!demand_data) return NULL;
append_list(demand_list, &demand_data);
return demand_list;
}
demand_data_t *create_demand_data(double base_demand, int pattern_index, const char *category_name)
{
demand_data_t *demand_data = (demand_data_t *)malloc(sizeof(demand_data_t));
demand_data->base_demand = base_demand;
demand_data->pattern_index = pattern_index;
if (category_name)
demand_data->category_name = strdup(category_name);
else
demand_data->category_name = NULL;
return demand_data;
}
void delete_demand_data(void *data)
{
demand_data_t *demand_data = *(demand_data_t **)data;
if (demand_data->category_name)
free(demand_data->category_name);
free(demand_data);
}
size_t get_demand_data_size(void)
{
return sizeof(demand_data_t *);
}
demand_data_t *get_demand_data(list_node_t *lnode)
{
return *(demand_data_t **)get_data(lnode);
}
bool convert_units(list_node_t *lnode, double unit_conversion)
{
double base_demand = get_base_demand(lnode);
set_base_demand(lnode, base_demand/unit_conversion);
return true;
}
double get_base_demand(list_node_t *lnode)
{
return get_demand_data(lnode)->base_demand;
}
void set_base_demand(list_node_t *lnode, double base_demand)
{
get_demand_data(lnode)->base_demand = base_demand;
}
int get_pattern_index(list_node_t *lnode)
{
return get_demand_data(lnode)->pattern_index;
}
void set_pattern_index(list_node_t *lnode, int pattern_index)
{
get_demand_data(lnode)->pattern_index = pattern_index;
}
char *get_category_name(list_node_t *lnode)
// Be advised: caller must free memory returned
{
char *temp = get_demand_data(lnode)->category_name;
if (temp)
return strdup(temp);
else
return NULL;
}
void set_category_name(list_node_t *lnode, const char *category_name)
{
free(get_demand_data(lnode)->category_name);
get_demand_data(lnode)->category_name = strdup(category_name);
}

63
src/demand.h Normal file
View File

@@ -0,0 +1,63 @@
/*
******************************************************************************
Project: OWA EPANET
Version: 2.2
Module: demand.h
Description: data for demand pattern list
Authors: see AUTHORS
Copyright: see AUTHORS
License: see LICENSE
Last Updated: 04/12/2019
******************************************************************************
*/
#ifndef DEMAND_H
#define DEMAND_H
#include "util/list.h"
#ifdef __cplusplus
extern "C" {
#endif
// Forward declarations
typedef struct demand_data_s demand_data_t;
// demand list gets declared in types.h struct Snode
list_t *create_demand_list(double base_demand, int pattern_index, const char *category_name);
demand_data_t *create_demand_data(double base_demand, int pat_index, const char *cat_name);
void delete_demand_data(void *data);
size_t get_demand_data_size(void);
bool convert_units(list_node_t *lnode, double unit_conversion);
double get_base_demand(list_node_t *lnode);
void set_base_demand(list_node_t *lnode, double base_demand);
int get_pattern_index(list_node_t *lnode);
void set_pattern_index(list_node_t *lnode, int pattern_index);
char *get_category_name(list_node_t *lnode);
void set_category_name(list_node_t *lnode, const char *category_name);
// Make this private?
demand_data_t *get_demand_data(list_node_t *lnode);
#ifdef __cplusplus
}
#endif
#endif /* DEMAND_H */

View File

@@ -30,6 +30,8 @@
#include "text.h" #include "text.h"
#include "enumstxt.h" #include "enumstxt.h"
#include "demand.h"
#ifdef _WIN32 #ifdef _WIN32
#define snprintf _snprintf #define snprintf _snprintf
#endif #endif
@@ -1719,8 +1721,8 @@ int DLLEXPORT EN_addnode(EN_Project p, char *id, int nodeType, int *index)
Hydraul *hyd = &p->hydraul; Hydraul *hyd = &p->hydraul;
Quality *qual = &p->quality; Quality *qual = &p->quality;
int i, nIdx, size; int i, nIdx;
struct Sdemand *demand; int size;
Stank *tank; Stank *tank;
Snode *node; Snode *node;
Scontrol *control; Scontrol *control;
@@ -1751,12 +1753,7 @@ int DLLEXPORT EN_addnode(EN_Project p, char *id, int nodeType, int *index)
nIdx = net->Njuncs; nIdx = net->Njuncs;
node = &net->Node[nIdx]; node = &net->Node[nIdx];
demand = (struct Sdemand *)malloc(sizeof(struct Sdemand)); node->D = NULL;
demand->Base = 0.0;
demand->Pat = 0;
demand->Name = NULL;
demand->next = NULL;
node->D = demand;
// shift rest of Node array // shift rest of Node array
for (i = net->Nnodes; i >= net->Njuncs; i--) for (i = net->Nnodes; i >= net->Njuncs; i--)
@@ -1857,7 +1854,7 @@ int DLLEXPORT EN_deletenode(EN_Project p, int index, int actionCode)
int i, nodeType, tankindex; int i, nodeType, tankindex;
Snode *node; Snode *node;
Pdemand demand, nextdemand; list_t *demand;
// Cannot modify network structure while solvers are active // Cannot modify network structure while solvers are active
if (!p->Openflag) return 102; if (!p->Openflag) return 102;
@@ -1891,13 +1888,9 @@ int DLLEXPORT EN_deletenode(EN_Project p, int index, int actionCode)
// Free memory allocated to node's demands, WQ source & comment // Free memory allocated to node's demands, WQ source & comment
demand = node->D; demand = node->D;
while (demand != NULL) if (demand)
{ delete_list(demand);
nextdemand = demand->next;
free(demand->Name);
free(demand);
demand = nextdemand;
}
free(node->S); free(node->S);
free(node->Comment); free(node->Comment);
@@ -2064,7 +2057,6 @@ int DLLEXPORT EN_getnodevalue(EN_Project p, int index, int property, double *val
Quality *qual = &p->quality; Quality *qual = &p->quality;
double v = 0.0; double v = 0.0;
Pdemand demand;
Psource source; Psource source;
Snode *Node = net->Node; Snode *Node = net->Node;
@@ -2091,26 +2083,24 @@ int DLLEXPORT EN_getnodevalue(EN_Project p, int index, int property, double *val
case EN_BASEDEMAND: case EN_BASEDEMAND:
v = 0.0; v = 0.0;
// NOTE: primary demand category is last on demand list // NOTE: primary demand category is first on demand list
if (index <= nJuncs) if (index <= nJuncs)
{ {
for (demand = Node[index].D; demand != NULL; demand = demand->next) list_t *demand = Node[index].D;
{ if (demand)
v = (demand->Base); v = get_base_demand(head_list(demand, false));
}
} }
v *= Ucf[FLOW]; v *= Ucf[FLOW];
break; break;
case EN_PATTERN: case EN_PATTERN:
v = 0.0; v = 0.0;
// NOTE: primary demand category is last on demand list // NOTE: primary demand category is first on demand list
if (index <= nJuncs) if (index <= nJuncs)
{ {
for (demand = Node[index].D; demand != NULL; demand = demand->next) list_t *demand = Node[index].D;
{ if (demand)
v = (double)(demand->Pat); v = get_pattern_index(head_list(demand, false));
}
} }
else v = (double)(Tank[index - nJuncs].Pat); else v = (double)(Tank[index - nJuncs].Pat);
break; break;
@@ -2265,7 +2255,6 @@ int DLLEXPORT EN_setnodevalue(EN_Project p, int index, int property, double valu
double *Ucf = p->Ucf; double *Ucf = p->Ucf;
int i, j, n; int i, j, n;
Pdemand demand;
Psource source; Psource source;
double hTmp; double hTmp;
@@ -2288,26 +2277,24 @@ int DLLEXPORT EN_setnodevalue(EN_Project p, int index, int property, double valu
break; break;
case EN_BASEDEMAND: case EN_BASEDEMAND:
// NOTE: primary demand category is last on demand list // NOTE: primary demand category is first on demand list
if (index <= nJuncs) if (index <= nJuncs)
{ {
for (demand = Node[index].D; demand != NULL; demand = demand->next) list_t *demand = Node[index].D;
{ if (demand)
if (demand->next == NULL) demand->Base = value / Ucf[FLOW]; set_base_demand(head_list(demand, false), value / Ucf[FLOW]);
}
} }
break; break;
case EN_PATTERN: case EN_PATTERN:
// NOTE: primary demand category is last on demand list // NOTE: primary demand category is first on demand list
j = ROUND(value); j = ROUND(value);
if (j < 0 || j > nPats) return 205; if (j < 0 || j > nPats) return 205;
if (index <= nJuncs) if (index <= nJuncs)
{ {
for (demand = Node[index].D; demand != NULL; demand = demand->next) list_t *demand = Node[index].D;
{ if (demand)
if (demand->next == NULL) demand->Pat = j; set_pattern_index(head_list(demand, false), j);
}
} }
else Tank[index - nJuncs].Pat = j; else Tank[index - nJuncs].Pat = j;
break; break;
@@ -2504,7 +2491,6 @@ int DLLEXPORT EN_setjuncdata(EN_Project p, int index, double elev,
int i, patIndex = 0; int i, patIndex = 0;
Snode *Node = net->Node; Snode *Node = net->Node;
Pdemand demand;
// Check that junction exists // Check that junction exists
if (!p->Openflag) return 102; if (!p->Openflag) return 102;
@@ -2526,14 +2512,56 @@ int DLLEXPORT EN_setjuncdata(EN_Project p, int index, double elev,
// Assign values to junction's parameters // Assign values to junction's parameters
Node[index].El = elev / p->Ucf[ELEV]; Node[index].El = elev / p->Ucf[ELEV];
for (demand = Node[index].D; demand != NULL; demand = demand->next)
{ list_t *demand_list = Node[index].D;
if (demand->next == NULL) if (!demand_list) {
{ demand_list = create_list(get_demand_data_size(), delete_demand_data);
demand->Base = dmnd / p->Ucf[FLOW]; if (!demand_list) return 101;
demand->Pat = patIndex;
}
} }
demand_data_t *demand_data = create_demand_data(dmnd/p->Ucf[FLOW], patIndex, NULL);
if (!demand_data) return 101;
append_list(demand_list, &demand_data);
return 0;
}
int DLLEXPORT EN_adddemand(EN_Project p, int node_index, double demand,
char *demand_pattern, const char *category_name, int *demand_key)
{
Network *net = &p->network;
int pattern_index, error = 0;
*demand_key = -1;
if (error = EN_getpatternindex(p, demand_pattern, &pattern_index) != 0) return error;
Snode *Node = net->Node;
list_t *demand_list = Node[node_index].D;
if (!demand_list) {
demand_list = create_demand_list(demand/p->Ucf[FLOW], pattern_index, category_name);
if (!demand_list) return 101;
Node[node_index].D = demand_list;
}
else {
demand_data_t *demand_data = create_demand_data(demand/p->Ucf[FLOW], pattern_index, category_name);
if (!demand_data) return 101;
*demand_key = append_list(demand_list, &demand_data);
}
return 0;
}
int DLLEXPORT EN_removedemand(EN_Project p, int node_index, int demand_key)
{
Network *net = &p->network;
Snode *Node = net->Node;
list_t *dlist = Node[node_index].D;
remove_node(dlist, demand_key);
return 0; return 0;
} }
@@ -2714,16 +2742,20 @@ int DLLEXPORT EN_getnumdemands(EN_Project p, int nodeIndex, int *numDemands)
**---------------------------------------------------------------- **----------------------------------------------------------------
*/ */
{ {
Pdemand d; //Pdemand d;
int n = 0; //int n = 0;
// Check for valid arguments // Check for valid arguments
if (!p->Openflag) return 102; if (!p->Openflag) return 102;
if (nodeIndex <= 0 || nodeIndex > p->network.Nnodes) return 203; if (nodeIndex <= 0 || nodeIndex > p->network.Nnodes) return 203;
// Count the number of demand categories // Count the number of demand categories
for (d = p->network.Node[nodeIndex].D; d != NULL; d = d->next) n++; list_t *demand_list = p->network.Node[nodeIndex].D;
*numDemands = n; if (demand_list)
*numDemands = size_list(demand_list);
else
*numDemands = 0;
return 0; return 0;
} }
@@ -2738,9 +2770,6 @@ int DLLEXPORT EN_getbasedemand(EN_Project p, int nodeIndex, int demandIndex,
**---------------------------------------------------------------- **----------------------------------------------------------------
*/ */
{ {
Pdemand d;
int n = 1;
// Check for valid arguments // Check for valid arguments
if (!p->Openflag) return 102; if (!p->Openflag) return 102;
if (nodeIndex <= 0 || nodeIndex > p->network.Nnodes) return 203; if (nodeIndex <= 0 || nodeIndex > p->network.Nnodes) return 203;
@@ -2748,10 +2777,13 @@ int DLLEXPORT EN_getbasedemand(EN_Project p, int nodeIndex, int demandIndex,
// Retrieve demand for specified category // Retrieve demand for specified category
if (nodeIndex <= p->network.Njuncs) if (nodeIndex <= p->network.Njuncs)
{ {
for (d = p->network.Node[nodeIndex].D; n < demandIndex && d->next != NULL; // Locate demand category record and assign demandName to it
d = d->next) n++; list_t *dlist = p->network.Node[nodeIndex].D;
if (n != demandIndex) return 253; list_node_t *lnode = get_nth_list(dlist, demandIndex);
*baseDemand = (double)(d->Base * p->Ucf[FLOW]); if (!lnode)
return 253;
else
*baseDemand = get_base_demand(lnode) * p->Ucf[FLOW];
} }
else *baseDemand = (double)(0.0); else *baseDemand = (double)(0.0);
return 0; return 0;
@@ -2769,9 +2801,6 @@ int DLLEXPORT EN_setbasedemand(EN_Project p, int nodeIndex, int demandIndex,
**---------------------------------------------------------------- **----------------------------------------------------------------
*/ */
{ {
Pdemand d;
int n = 1;
// Check for valid arguments // Check for valid arguments
if (!p->Openflag) return 102; if (!p->Openflag) return 102;
if (nodeIndex <= 0 || nodeIndex > p->network.Nnodes) return 203; if (nodeIndex <= 0 || nodeIndex > p->network.Nnodes) return 203;
@@ -2779,10 +2808,22 @@ int DLLEXPORT EN_setbasedemand(EN_Project p, int nodeIndex, int demandIndex,
// Set baseline demand for specified category // Set baseline demand for specified category
if (nodeIndex <= p->network.Njuncs) if (nodeIndex <= p->network.Njuncs)
{ {
for (d = p->network.Node[nodeIndex].D; n < demandIndex && d->next != NULL; list_t *dlist = p->network.Node[nodeIndex].D;
d = d->next) n++; // If demand list is null create one and set demand
if (n != demandIndex) return 253; if (!dlist) {
d->Base = baseDemand / p->Ucf[FLOW]; dlist = create_demand_list(baseDemand / p->Ucf[FLOW], 0, NULL);
if (!dlist) return 101;
p->network.Node[nodeIndex].D = dlist;
}
// else find the demand entry and set demand
else {
list_node_t *lnode = get_nth_list(dlist, demandIndex);
if (!lnode)
return 253;
else
set_base_demand(lnode, baseDemand / p->Ucf[FLOW]);
}
} }
return 0; return 0;
} }
@@ -2798,8 +2839,7 @@ int DLLEXPORT EN_getdemandname(EN_Project p, int nodeIndex, int demandIndex,
**---------------------------------------------------------------- **----------------------------------------------------------------
*/ */
{ {
Pdemand d; char *temp = NULL;
int n = 1;
strcpy(demandName, ""); strcpy(demandName, "");
@@ -2808,12 +2848,21 @@ int DLLEXPORT EN_getdemandname(EN_Project p, int nodeIndex, int demandIndex,
if (nodeIndex <= 0 || nodeIndex > p->network.Njuncs) return 203; if (nodeIndex <= 0 || nodeIndex > p->network.Njuncs) return 203;
// Locate demand category record and retrieve its name // Locate demand category record and retrieve its name
for (d = p->network.Node[nodeIndex].D; list_t *dlist = p->network.Node[nodeIndex].D;
n < demandIndex && d->next != NULL; d = d->next) n++; if (dlist) {
if (n != demandIndex) return 253; list_node_t *lnode = get_nth_list(dlist, demandIndex);
if (!lnode)
return 253;
else
temp = get_category_name(lnode);
if (temp) {
strcpy(demandName, temp);
free(temp);
}
}
else return 253;
if (d->Name) strcpy(demandName, d->Name);
else demandName[0] = '\0';
return 0; return 0;
} }
@@ -2829,8 +2878,6 @@ int DLLEXPORT EN_setdemandname(EN_Project p, int nodeIndex, int demandIndex,
**---------------------------------------------------------------- **----------------------------------------------------------------
*/ */
{ {
Pdemand d;
int n = 1;
// Check for valid arguments // Check for valid arguments
if (!p->Openflag) return 102; if (!p->Openflag) return 102;
@@ -2840,10 +2887,20 @@ int DLLEXPORT EN_setdemandname(EN_Project p, int nodeIndex, int demandIndex,
if (strlen(demandName) > MAXID) return 252; if (strlen(demandName) > MAXID) return 252;
// Locate demand category record and assign demandName to it // Locate demand category record and assign demandName to it
for (d = p->network.Node[nodeIndex].D; list_t *dlist = p->network.Node[nodeIndex].D;
n < demandIndex && d->next != NULL; d = d->next) n++; if (!dlist) {
if (n != demandIndex) return 253; dlist = create_demand_list(0, 0, demandName);
d->Name = xstrcpy(&d->Name, demandName, MAXID); if (!dlist) return 101;
p->network.Node[nodeIndex].D = dlist;
}
else {
list_node_t *lnode = get_nth_list(dlist, demandIndex);
if (!lnode)
return 253;
else
set_category_name(lnode, demandName);
}
return 0; return 0;
} }
@@ -2859,16 +2916,18 @@ int DLLEXPORT EN_getdemandpattern(EN_Project p, int nodeIndex, int demandIndex,
**---------------------------------------------------------------- **----------------------------------------------------------------
*/ */
{ {
Pdemand d;
int n = 1;
// Check for valid arguments // Check for valid arguments
if (!p->Openflag) return 102; if (!p->Openflag) return 102;
if (nodeIndex <= 0 || nodeIndex > p->network.Nnodes) return 203; if (nodeIndex <= 0 || nodeIndex > p->network.Nnodes) return 203;
for (d = p->network.Node[nodeIndex].D;
n < demandIndex && d->next != NULL; d = d->next) n++; // Locate demand category record and assign demandName to it
if (n != demandIndex) return 253; list_t *dlist = p->network.Node[nodeIndex].D;
*patIndex = d->Pat; list_node_t *lnode = get_nth_list(dlist, demandIndex);
if (!lnode)
return 253;
else
*patIndex = get_pattern_index(lnode);
return 0; return 0;
} }
@@ -2886,21 +2945,28 @@ int DLLEXPORT EN_setdemandpattern(EN_Project p, int nodeIndex, int demandIndex,
{ {
Network *net = &p->network; Network *net = &p->network;
Pdemand d;
int n = 1;
// Check for valid arguments // Check for valid arguments
if (!p->Openflag) return 102; if (!p->Openflag) return 102;
if (nodeIndex <= 0 || nodeIndex > net->Nnodes) return 203; if (nodeIndex <= 0 || nodeIndex > net->Nnodes) return 203;
if (patIndex <= 0 || patIndex > net->Npats) return 205; if (patIndex <= 0 || patIndex > net->Npats) return 205;
// Locate demand category record and assign time pattern to it // Locate demand category record and assign time pattern to it
if (nodeIndex <= net->Njuncs) if (nodeIndex <= net->Njuncs) {
{
for (d = net->Node[nodeIndex].D; list_t *dlist = p->network.Node[nodeIndex].D;
n < demandIndex && d->next != NULL; d = d->next) n++; if (!dlist) {
if (n != demandIndex) return 253; dlist = create_demand_list(0, patIndex, NULL);
d->Pat = patIndex; if (!dlist) return 101;
p->network.Node[nodeIndex].D = dlist;
}
else {
list_node_t *lnode = get_nth_list(dlist, demandIndex);
if (!lnode)
return 253;
else
set_pattern_index(lnode, patIndex);
}
} }
return 0; return 0;
} }

View File

@@ -27,6 +27,9 @@
#include "funcs.h" #include "funcs.h"
#include "text.h" #include "text.h"
#include "demand.h"
const double QZERO = 1.e-6; // Equivalent to zero flow in cfs const double QZERO = 1.e-6; // Equivalent to zero flow in cfs
// Imported functions // Imported functions
@@ -553,7 +556,7 @@ void demands(Project *pr)
int i ,j, n; int i ,j, n;
long k, p; long k, p;
double djunc, sum; double djunc, sum;
Pdemand demand; // Pdemand demand;
// Determine total elapsed number of pattern periods // Determine total elapsed number of pattern periods
p = (time->Htime + time->Pstart) / time->Pstep; p = (time->Htime + time->Pstart) / time->Pstep;
@@ -563,15 +566,19 @@ void demands(Project *pr)
for (i = 1; i <= net->Njuncs; i++) for (i = 1; i <= net->Njuncs; i++)
{ {
sum = 0.0; sum = 0.0;
for (demand = net->Node[i].D; demand != NULL; demand = demand->next) list_t *dlist = net->Node[i].D;
{
// pattern period (k) = (elapsed periods) modulus (periods per pattern) if (dlist) {
j = demand->Pat; for (list_node_t *lnode = first_list(dlist); done_list(lnode); lnode = next_list(lnode))
k = p % (long) net->Pattern[j].Length; {
djunc = (demand->Base) * net->Pattern[j].F[k] * hyd->Dmult; // pattern period (k) = (elapsed periods) modulus (periods per pattern)
if (djunc > 0.0) hyd->Dsystem += djunc; j = get_pattern_index(lnode);
sum += djunc; k = p % (long)net->Pattern[j].Length;
} djunc = (get_base_demand(lnode)) * net->Pattern[j].F[k] * hyd->Dmult;
if (djunc > 0.0) hyd->Dsystem += djunc;
sum += djunc;
}
}
hyd->NodeDemand[i] = sum; hyd->NodeDemand[i] = sum;
// Initialize pressure dependent demand // Initialize pressure dependent demand

View File

@@ -28,6 +28,8 @@ Last Updated: 04/03/2019
#include "hash.h" #include "hash.h"
#include "text.h" #include "text.h"
#include "demand.h"
// Defined in enumstxt.h in EPANET.C // Defined in enumstxt.h in EPANET.C
extern char *LinkTxt[]; extern char *LinkTxt[];
extern char *FormTxt[]; extern char *FormTxt[];
@@ -119,6 +121,48 @@ void saveauxdata(Project *pr, FILE *f)
InFile = NULL; InFile = NULL;
} }
void write_demands(Project *pr, FILE *f) {
int i, j;
Snode *node = NULL;
list_node_t *lnode = NULL;
char *temp = NULL;
char s[MAXLINE + 1],
s1[MAXLINE + 1];
double ucf = pr->Ucf[DEMAND];
Network *net = &pr->network;
fprintf(f, "\n\n");
fprintf(f, s_DEMANDS);
for (i = 1; i <= net->Njuncs; i++) {
node = &net->Node[i];
if (node->D) {
for (lnode = first_list(node->D); done_list(lnode); lnode = next_list(lnode)) {
if (lnode) {
sprintf(s, " %-31s %14.6f", node->ID, ucf * get_base_demand(lnode));
if
((j = get_pattern_index(lnode)) > 0) sprintf(s1, " %-31s", net->Pattern[j].ID);
else
strcpy(s1, " ");
fprintf(f, "\n%s %-31s", s, s1);
if (temp = get_category_name(lnode)) {
fprintf(f, " ;%s", temp);
free(temp);
}
}
}
}
}
}
int saveinpfile(Project *pr, const char *fname) int saveinpfile(Project *pr, const char *fname)
/* /*
------------------------------------------------- -------------------------------------------------
@@ -135,9 +179,9 @@ int saveinpfile(Project *pr, const char *fname)
Times *time = &pr->times; Times *time = &pr->times;
int i, j, n; int i, j, n;
double d, kc, ke, km, ucf; double d, kc, ke, km;
char s[MAXLINE + 1], s1[MAXLINE + 1], s2[MAXLINE + 1]; char s[MAXLINE + 1], s1[MAXLINE + 1], s2[MAXLINE + 1];
Pdemand demand; //Pdemand demand;
Psource source; Psource source;
FILE *f; FILE *f;
Slink *link; Slink *link;
@@ -325,22 +369,10 @@ int saveinpfile(Project *pr, const char *fname)
if (link->Comment) fprintf(f, " ;%s", link->Comment); if (link->Comment) fprintf(f, " ;%s", link->Comment);
} }
// Write [DEMANDS] section // Write [DEMANDS] section
fprintf(f, "\n\n"); write_demands(pr, f);
fprintf(f, s_DEMANDS);
ucf = pr->Ucf[DEMAND];
for (i = 1; i <= net->Njuncs; i++)
{
node = &net->Node[i];
for (demand = node->D; demand != NULL; demand = demand->next)
{
sprintf(s, " %-31s %14.6f", node->ID, ucf * demand->Base);
if ((j = demand->Pat) > 0) sprintf(s1, " %-31s", net->Pattern[j].ID);
else strcpy(s1, " ");
fprintf(f, "\n%s %-31s", s, s1);
if (demand->Name) fprintf(f, " ;%s", demand->Name);
}
}
// Write [EMITTERS] section // Write [EMITTERS] section
fprintf(f, "\n\n"); fprintf(f, "\n\n");

View File

@@ -23,6 +23,8 @@ Last Updated: 04/03/2019
#include <math.h> #include <math.h>
#include "demand.h"
#include "types.h" #include "types.h"
#include "funcs.h" #include "funcs.h"
#include "hash.h" #include "hash.h"
@@ -220,9 +222,9 @@ void adjustdata(Project *pr)
int i; int i;
double ucf; // Unit conversion factor double ucf; // Unit conversion factor
Pdemand demand; // Pointer to demand record //Pdemand demand; // Pointer to demand record
Slink *link; Slink *link;
Snode *node; //Snode *node;
Stank *tank; Stank *tank;
// Use 1 hr pattern & report time step if none specified // Use 1 hr pattern & report time step if none specified
@@ -330,16 +332,16 @@ void adjustdata(Project *pr)
if (tank->Kb == MISSING) tank->Kb = qual->Kbulk; if (tank->Kb == MISSING) tank->Kb = qual->Kbulk;
} }
// Use default pattern if none assigned to a demand // Use default pattern if none assigned to a demand
parser->DefPat = findpattern(net, parser->DefPatID); parser->DefPat = findpattern(net, parser->DefPatID);
if (parser->DefPat > 0) for (i = 1; i <= net->Nnodes; i++) if (parser->DefPat > 0) {
{ for (i = 1; i <= net->Nnodes; i++) {
node = &net->Node[i]; for (list_node_t *lnode = first_list((&net->Node[i])->D); done_list(lnode); lnode = next_list(lnode)) {
for (demand = node->D; demand != NULL; demand = demand->next) if (get_pattern_index(lnode) == 0)
{ set_pattern_index(lnode, parser->DefPat);
if (demand->Pat == 0) demand->Pat = parser->DefPat; }
} }
} }
// Remove QUALITY as a reporting variable if no WQ analysis // Remove QUALITY as a reporting variable if no WQ analysis
if (qual->Qualflag == NONE) rpt->Field[QUALITY].Enabled = FALSE; if (qual->Qualflag == NONE) rpt->Field[QUALITY].Enabled = FALSE;
@@ -542,7 +544,7 @@ void convertunits(Project *pr)
int i, j, k; int i, j, k;
double ucf; // Unit conversion factor double ucf; // Unit conversion factor
Pdemand demand; // Pointer to demand record //Pdemand demand; // Pointer to demand record
Snode *node; Snode *node;
Stank *tank; Stank *tank;
Slink *link; Slink *link;
@@ -562,11 +564,17 @@ void convertunits(Project *pr)
for (i = 1; i <= net->Njuncs; i++) for (i = 1; i <= net->Njuncs; i++)
{ {
node = &net->Node[i]; node = &net->Node[i];
for (demand = node->D; demand != NULL; demand = demand->next) list_t *dlist = node->D;
{ if (dlist) {
demand->Base /= pr->Ucf[DEMAND]; for (list_node_t *lnode = first_list(dlist); done_list(lnode); lnode = next_list(lnode))
} convert_units(lnode, pr->Ucf[DEMAND]);
}
// for (demand = node->D; demand != NULL; demand = demand->next)
// {
// demand->Base /= pr->Ucf[DEMAND];
// }
} }
hyd->Pmin /= pr->Ucf[PRESSURE]; hyd->Pmin /= pr->Ucf[PRESSURE];
hyd->Preq /= pr->Ucf[PRESSURE]; hyd->Preq /= pr->Ucf[PRESSURE];

View File

@@ -23,6 +23,7 @@ Last Updated: 04/03/2019
#include <math.h> #include <math.h>
#include "demand.h"
#include "types.h" #include "types.h"
#include "funcs.h" #include "funcs.h"
#include "hash.h" #include "hash.h"
@@ -80,12 +81,15 @@ int juncdata(Project *pr)
Parser *parser = &pr->parser; Parser *parser = &pr->parser;
Hydraul *hyd = &pr->hydraul; Hydraul *hyd = &pr->hydraul;
int p = 0; // time pattern index int p = 0; // time pattern index
int n; // number of tokens int n; // number of tokens
int njuncs; // number of network junction nodes int njuncs; // number of network junction nodes
double el, // elevation double el, // elevation
y = 0.0; // base demand y = 0.0; // base demand
Pdemand demand; // demand record
list_t *demand_list = NULL; // demand list
Snode *node; Snode *node;
// Add new junction to data base // Add new junction to data base
@@ -118,14 +122,13 @@ int juncdata(Project *pr)
node->Type = JUNCTION; node->Type = JUNCTION;
node->Comment = xstrcpy(&node->Comment, parser->Comment, MAXMSG); node->Comment = xstrcpy(&node->Comment, parser->Comment, MAXMSG);
// create a demand record, even if no demand is specified here. // create demand data only if a demand has been specified
demand = (struct Sdemand *) malloc(sizeof(struct Sdemand)); if (y != 0.0) {
if (demand == NULL) return 101; demand_list = create_demand_list(y, p, NULL);
demand->Base = y; if (!demand_list) return 101;
demand->Pat = p; }
demand->Name = NULL; node->D = demand_list;
demand->next = NULL;
node->D = demand;
hyd->NodeDemand[njuncs] = y; hyd->NodeDemand[njuncs] = y;
return 0; return 0;
} }
@@ -709,8 +712,12 @@ int demanddata(Project *pr)
int j, n, p = 0; int j, n, p = 0;
double y; double y;
Pdemand demand;
Pdemand cur_demand; list_t *demand_list = NULL;
demand_data_t *demand_data = NULL;
//Pdemand demand;
//Pdemand cur_demand;
// Extract data from tokens // Extract data from tokens
n = parser->Ntokens; n = parser->Ntokens;
@@ -734,38 +741,27 @@ int demanddata(Project *pr)
if (p == 0) return setError(parser, 2, 205); if (p == 0) return setError(parser, 2, 205);
} }
// Replace any demand entered in [JUNCTIONS] section
demand = net->Node[j].D;
if (hyd->NodeDemand[j] != MISSING)
{
// First category encountered will overwrite "dummy" demand category
// with what is specified in this section
demand->Base = y;
demand->Pat = p;
if (parser->Comment[0])
{
demand->Name = xstrcpy(&demand->Name, parser->Comment, MAXID);
}
hyd->NodeDemand[j] = MISSING; // marker - next iteration will append a new category.
}
// Otherwise add new demand to junction // if no demands were specified in [JUNCTIONS] create the list here
else demand_list = net->Node[j].D;
{ if (demand_list == NULL) {
cur_demand = net->Node[j].D; demand_list = create_list(get_demand_data_size(), delete_demand_data);
while (cur_demand->next != NULL) cur_demand = cur_demand->next; if (demand_list == NULL) return 101;
demand = (struct Sdemand *)malloc(sizeof(struct Sdemand)); net->Node[j].D = demand_list;
if (demand == NULL) return 101; }
demand->Base = y;
demand->Pat = p; // else replace the demand data entered in [JUNCTIONS] section
demand->Name = NULL; else if (size_list(demand_list) == 1) {
if (parser->Comment[0]) list_node_t *lnode = head_list(demand_list, true);
{ delete_node(demand_list, lnode);
demand->Name = xstrcpy(&demand->Name, parser->Comment, MAXID); }
}
demand->next = NULL; // append the data to the list
cur_demand->next = demand; demand_data = create_demand_data(y, p, parser->Comment);
} if (demand_data == NULL) return 101;
append_list(demand_list, &demand_data);
return 0; return 0;
} }

View File

@@ -29,6 +29,8 @@
#include "types.h" #include "types.h"
#include "funcs.h" #include "funcs.h"
#include "demand.h"
int openfiles(Project *pr, const char *f1, const char *f2, const char *f3) int openfiles(Project *pr, const char *f1, const char *f2, const char *f3)
/*---------------------------------------------------------------- /*----------------------------------------------------------------
** Input: f1 = pointer to name of input file ** Input: f1 = pointer to name of input file
@@ -385,7 +387,7 @@ void freedata(Project *pr)
*/ */
{ {
int j; int j;
Pdemand demand, nextdemand; //Pdemand demand, nextdemand;
// Free memory for computed results // Free memory for computed results
free(pr->hydraul.NodeDemand); free(pr->hydraul.NodeDemand);
@@ -404,14 +406,10 @@ void freedata(Project *pr)
for (j = 1; j <= pr->parser.MaxNodes; j++) for (j = 1; j <= pr->parser.MaxNodes; j++)
{ {
// Free memory used for demand category list // Free memory used for demand category list
demand = pr->network.Node[j].D; list_t *demand = pr->network.Node[j].D;
while (demand != NULL) if(demand)
{ delete_list(demand);
nextdemand = demand->next;
free(demand->Name);
free(demand);
demand = nextdemand;
}
// Free memory used for WQ source data // Free memory used for WQ source data
free(pr->network.Node[j].S); free(pr->network.Node[j].S);
free(pr->network.Node[j].Comment); free(pr->network.Node[j].Comment);
@@ -788,10 +786,20 @@ void adjustpattern(int *pat, int index)
**---------------------------------------------------------------- **----------------------------------------------------------------
*/ */
{ {
if (*pat == index) *pat = 0; if (*pat == index) *pat = 0;
else if (*pat > index) (*pat)--; else if (*pat > index) (*pat)--;
} }
void adjust_demand_pattern(list_node_t *list_node, int deletion_index)
{
int pat_idx = get_pattern_index(list_node);
if (pat_idx == deletion_index) set_pattern_index(list_node, 0);
else if (pat_idx > deletion_index) set_pattern_index(list_node, --pat_idx);
}
void adjustpatterns(Network *network, int index) void adjustpatterns(Network *network, int index)
/*---------------------------------------------------------------- /*----------------------------------------------------------------
** Input: index = index of time pattern being deleted ** Input: index = index of time pattern being deleted
@@ -801,16 +809,17 @@ void adjustpatterns(Network *network, int index)
*/ */
{ {
int j; int j;
Pdemand demand; //Pdemand demand;
Psource source; Psource source;
// Adjust patterns used by junctions // Adjust patterns used by junctions
for (j = 1; j <= network->Nnodes; j++) for (j = 1; j <= network->Nnodes; j++)
{ {
// Adjust demand patterns // Adjust demand patterns
for (demand = network->Node[j].D; demand != NULL; demand = demand->next) list_t *dlist = network->Node[j].D;
{ if (dlist) {
adjustpattern(&demand->Pat, index); for (list_node_t *lnode = first_list(dlist); done_list(lnode); lnode = next_list(lnode))
adjust_demand_pattern(lnode, index);
} }
// Adjust WQ source patterns // Adjust WQ source patterns
source = network->Node[j].S; source = network->Node[j].S;

View File

@@ -14,9 +14,12 @@
#ifndef TYPES_H #ifndef TYPES_H
#define TYPES_H #define TYPES_H
#include "hash.h"
#include <stdio.h> #include <stdio.h>
#include "hash.h"
#include "util/list.h"
/* /*
------------------------------------------- -------------------------------------------
Definition of 4-byte integers & reals Definition of 4-byte integers & reals
@@ -336,14 +339,14 @@ typedef struct // Curve Object
double *Y; // y-values double *Y; // y-values
} Scurve; } Scurve;
struct Sdemand // Demand List Item //struct Sdemand // Demand List Item
{ //{
double Base; // baseline demand // double Base; // baseline demand
int Pat; // pattern index // int Pat; // pattern index
char *Name; // demand category name // char *Name; // demand category name
struct Sdemand *next; // next demand list item // struct Sdemand *next; // next demand list item
}; //};
typedef struct Sdemand *Pdemand; // Pointer to demand list //typedef struct Sdemand *Pdemand; // Pointer to demand list
typedef struct // Energy Usage Object typedef struct // Energy Usage Object
{ {
@@ -370,7 +373,8 @@ typedef struct // Node Object
double X; // x-coordinate double X; // x-coordinate
double Y; // y-coordinate double Y; // y-coordinate
double El; // elevation double El; // elevation
Pdemand D; // demand pointer // Pdemand D; // demand pointer
list_t *D; // pointer to demand list
Psource S; // source pointer Psource S; // source pointer
double C0; // initial quality double C0; // initial quality
double Ke; // emitter coeff. double Ke; // emitter coeff.

View File

@@ -22,6 +22,7 @@
#endif #endif
#include <string.h> #include <string.h>
#include <time.h>
#include <assert.h> #include <assert.h>
#include "list.h" #include "list.h"
@@ -29,6 +30,7 @@
typedef struct list_node_s { typedef struct list_node_s {
void *data; void *data;
int key;
struct list_node_s *next; struct list_node_s *next;
} list_node_t; } list_node_t;
@@ -42,6 +44,9 @@ typedef struct list_s {
} list_t; } list_t;
// local declarations
int gen_key();
list_t *create_list(size_t elementSize, freeFunction freeFn) list_t *create_list(size_t elementSize, freeFunction freeFn)
{ {
list_t *list; list_t *list;
@@ -67,12 +72,14 @@ void delete_list(list_t *list)
free(list); free(list);
} }
void prepend_list(list_t *list, void *element) int prepend_list(list_t *list, void *element)
{ {
list_node_t *node = malloc(sizeof(list_node_t)); list_node_t *node = malloc(sizeof(list_node_t));
node->data = malloc(list->elementSize); node->data = malloc(list->elementSize);
memcpy(node->data, element, list->elementSize); memcpy(node->data, element, list->elementSize);
node->key = gen_key();
node->next = list->head; node->next = list->head;
list->head = node; list->head = node;
@@ -82,14 +89,18 @@ void prepend_list(list_t *list, void *element)
} }
list->logicalLength++; list->logicalLength++;
return node->key;
} }
void append_list(list_t *list, void *element) int append_list(list_t *list, void *element)
{ {
list_node_t *node = malloc(sizeof(list_node_t)); list_node_t *node = malloc(sizeof(list_node_t));
node->data = malloc(list->elementSize); node->data = malloc(list->elementSize);
node->next = NULL; node->next = NULL;
node->key = gen_key();
memcpy(node->data, element, list->elementSize); memcpy(node->data, element, list->elementSize);
if(list->logicalLength == 0) { if(list->logicalLength == 0) {
@@ -100,34 +111,37 @@ void append_list(list_t *list, void *element)
} }
list->logicalLength++; list->logicalLength++;
return node->key;
} }
void for_each_list(list_t *list, listIterator iterator) void for_each_list(list_t *list, listIterator iterator)
{ {
assert(iterator != NULL); assert(iterator != NULL);
list_node_t *node = list->head; list_node_t *node = list->head;
bool result = true; bool result = true;
while(node != NULL && result) {
result = iterator(node); while(node != NULL && result) {
result = iterator(node);
node = node->next; node = node->next;
} }
} }
list_node_t *head_list(list_t *list, bool removeFromList) list_node_t *head_list(list_t *list, bool removeFromList)
//
// Warning: When node is removed caller is responsible for freeing it. // Warning: When node is removed caller is responsible for freeing it.
//
{ {
assert(list->head != NULL); if (list) {
list_node_t *node = list->head;
list_node_t *node = list->head; if (removeFromList) {
if(removeFromList) { // Disconnecting head node
// Disconnecting head node list->head = node->next;
list->head = node->next; list->logicalLength--;
list->logicalLength--; }
return node;
} }
return node; return NULL;
} }
list_node_t *tail_list(list_t *list) list_node_t *tail_list(list_t *list)
@@ -136,16 +150,83 @@ list_node_t *tail_list(list_t *list)
return list->tail; return list->tail;
} }
list_node_t *get_nth_list(list_t *list, int index)
{
int n;
list_node_t *lnode;
for (n = 1, lnode = first_list(list); n < index && done_list(lnode); n++, lnode = next_list(lnode));
if (n != index)
return NULL;
else
return lnode;
}
list_node_t *search_list(list_t *list, int key)
// Naive list search. Will not perform for large lists.
{
list_node_t *lnode = first_list(list);
while (done_list(lnode)) {
if (get_key(lnode) == key)
return lnode;
lnode = next_list(lnode);
}
return NULL;
}
void remove_node(list_t *list, int key)
{
list_node_t *temp;
list_node_t *target = search_list(list, key);
if (target == list->head)
delete_node(list, head_list(list, true));
else if (target == list->tail) {
// find next to last node
temp = list->head;
while (temp != NULL) {
if (temp->next == target)
break;
temp = temp->next;
}
// detatch tail
temp->next = NULL;
delete_node(list, list->tail);
}
else {
temp = target->next;
list->freeFn(target->data);
free(target->data);
target->data = temp->data;
target->next = temp->next;
free(temp);
}
}
int size_list(list_t *list) int size_list(list_t *list)
{ {
return list->logicalLength; return list->logicalLength;
} }
int get_key(list_node_t *lnode)
{
return lnode->key;
}
void *get_data(list_node_t *lnode) void *get_data(list_node_t *lnode)
{ {
return lnode->data; return lnode->data;
} }
list_node_t *get_next(list_node_t *lnode)
{
return lnode->next;
}
void delete_node(list_t *list, list_node_t *lnode) void delete_node(list_t *list, list_node_t *lnode)
{ {
if (list->freeFn) if (list->freeFn)
@@ -155,22 +236,11 @@ void delete_node(list_t *list, list_node_t *lnode)
free(lnode); free(lnode);
} }
//
// Iterator first/done/next operations provide containment for list abstraction
// http://www.cs.yale.edu/homes/aspnes/pinewiki/C(2f)Iterators.html
// Accessed on April 11, 2019
//
list_node_t *first_list(list_t *list)
{
return list->head;
}
bool done_list(list_node_t *lnode) // local functions
{
return lnode != NULL;
}
list_node_t *next_list(list_node_t *lnode) int gen_key()
// Naive key generator. No guarentee of uniqueness
{ {
return lnode->next; return rand();
} }

View File

@@ -19,6 +19,7 @@
#include <stdbool.h> #include <stdbool.h>
#if defined(__cplusplus) #if defined(__cplusplus)
extern "C" { extern "C" {
#endif #endif
@@ -28,8 +29,8 @@ extern "C" {
typedef struct list_node_s list_node_t; typedef struct list_node_s list_node_t;
typedef struct list_s list_t; typedef struct list_s list_t;
typedef void(*freeFunction)(void *); typedef void (*freeFunction) (void *);
typedef bool(*listIterator)(list_node_t *); typedef bool (*listIterator) (list_node_t *);
/** /**
@@ -45,14 +46,14 @@ with each nodes data pointer.
void delete_list(list_t *list); void delete_list(list_t *list);
/** /**
@brief Adds a node to the head of the list. @brief Adds a node to the head of the list and returns its key.
*/ */
void prepend_list(list_t *list, void *element); int prepend_list(list_t *list, void *element);
/** /**
@brief Adds a node to the tail of the list. @brief Adds a node to the tail of the list and returns its key.
*/ */
void append_list(list_t *list, void *element); int append_list(list_t *list, void *element);
/** /**
@brief Returns the number of items in the list. @brief Returns the number of items in the list.
@@ -65,6 +66,16 @@ int size_list(list_t *list);
*/ */
void *get_data(list_node_t *lnode); void *get_data(list_node_t *lnode);
/**
@brief Returns list node's key value.
*/
int get_key(list_node_t *lnode);
/**
@brief Returns next list node.
*/
list_node_t *get_next(list_node_t *lnode);
/** /**
@brief Frees memory associated with a list node. @brief Frees memory associated with a list node.
*/ */
@@ -87,21 +98,41 @@ list_node_t *head_list(list_t *list, bool removeFromList);
*/ */
list_node_t *tail_list(list_t *list); list_node_t *tail_list(list_t *list);
/**
@brief Returns nth node of the list or NULL.
*/
list_node_t *get_nth_list(list_t *list, int index);
/**
@brief Returns the list node with the given key or NULL.
*/
list_node_t *search_list(list_t *list, int key);
/**
@brief Removes the list node with the given key from the list.
*/
void remove_node(list_t *list, int key);
//
// Iterator first/done/next operations
// http://www.cs.yale.edu/homes/aspnes/pinewiki/C(2f)Iterators.html
// Accessed on April 11, 2019
//
/** /**
@brief Returns list head node. @brief Returns list head node.
*/ */
list_node_t *first_list(list_t *list); static inline list_node_t *first_list(list_t *list) { return head_list(list, false); }
/** /**
@brief Returns true if end of list false otherwise. @brief Returns true if end of list false otherwise.
*/ */
bool done_list(list_node_t *lnode); static inline bool done_list(list_node_t *lnode) { return lnode != NULL; }
/** /**
@brief Returns next node in the list. @brief Returns next node in the list.
*/ */
list_node_t *next_list(list_node_t *lnode); static inline list_node_t *next_list(list_node_t *lnode) { return get_next(lnode); }
#if defined(__cplusplus) #if defined(__cplusplus)

View File

@@ -23,6 +23,15 @@ add_test(NAME test_net_builder
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/data) WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/data)
add_executable(test_demand_data test_demand_data.cpp
../src/demand.c
../src/util/list.c)
target_include_directories(test_demand_data PUBLIC ../src/ ../src/util/)
target_link_libraries(test_demand_data ${Boost_LIBRARIES} epanet2)
add_test(NAME test_demand_data
COMMAND ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test_demand_data
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/data)
set(toolkit_test_srcs set(toolkit_test_srcs
test_toolkit.cpp test_toolkit.cpp

View File

@@ -18,6 +18,7 @@
BOOST_AUTO_TEST_SUITE (test_demand) BOOST_AUTO_TEST_SUITE (test_demand)
BOOST_AUTO_TEST_CASE(test_categories_save) BOOST_AUTO_TEST_CASE(test_categories_save)
{ {
int error = 0; int error = 0;
@@ -38,7 +39,7 @@ BOOST_AUTO_TEST_CASE(test_categories_save)
error = EN_getdemandname(ph, Nindex, ndem, demname); error = EN_getdemandname(ph, Nindex, ndem, demname);
BOOST_REQUIRE(error == 0); BOOST_REQUIRE(error == 0);
error = EN_setdemandname(ph, Nindex, ndem, (char *)"Demand category name"); error = EN_setdemandname(ph, Nindex, ndem, (char *)"CUB_SCOUT_MOTOR_POOL");
BOOST_REQUIRE(error == 0); BOOST_REQUIRE(error == 0);
error = EN_saveinpfile(ph, "net1_dem_cat.inp"); error = EN_saveinpfile(ph, "net1_dem_cat.inp");
BOOST_REQUIRE(error == 0); BOOST_REQUIRE(error == 0);
@@ -49,6 +50,7 @@ BOOST_AUTO_TEST_CASE(test_categories_save)
BOOST_REQUIRE(error == 0); BOOST_REQUIRE(error == 0);
} }
BOOST_AUTO_TEST_CASE(test_categories_reopen, * boost::unit_test::depends_on("test_demand/test_categories_save")) BOOST_AUTO_TEST_CASE(test_categories_reopen, * boost::unit_test::depends_on("test_demand/test_categories_save"))
{ {
int error = 0; int error = 0;
@@ -70,9 +72,9 @@ BOOST_AUTO_TEST_CASE(test_categories_reopen, * boost::unit_test::depends_on("tes
char demname[31]; char demname[31];
error = EN_getdemandname(ph, Nindex, ndem, demname); error = EN_getdemandname(ph, Nindex, ndem, demname);
BOOST_REQUIRE(error == 0); BOOST_CHECK(error == 0);
BOOST_CHECK(check_string(demname, "Demand category name")); BOOST_CHECK(check_string(demname, "CUB_SCOUT_MOTOR_POOL"));
error = EN_close(ph); error = EN_close(ph);
BOOST_REQUIRE(error == 0); BOOST_REQUIRE(error == 0);
@@ -80,4 +82,34 @@ BOOST_AUTO_TEST_CASE(test_categories_reopen, * boost::unit_test::depends_on("tes
BOOST_REQUIRE(error == 0); BOOST_REQUIRE(error == 0);
} }
BOOST_FIXTURE_TEST_CASE(test_adddemand, FixtureSingleNode)
{
int key, demand_key;
error = EN_adddemand(ph, node_qhut, 100.0, "PrimaryPattern", "PrimaryDemand", &demand_key);
BOOST_CHECK(error != 0);
error = EN_addpattern(ph, (char *)"PrimaryPattern");
BOOST_REQUIRE(error == 0);
error = EN_adddemand(ph, node_qhut, 100.0, "PrimaryPattern", "PrimaryDemand", &demand_key);
BOOST_CHECK(error == 0);
error = EN_addpattern(ph, (char *)"SecondaryPattern");
BOOST_REQUIRE(error == 0);
error = EN_adddemand(ph, node_qhut, 10.0, "SecondaryPattern", "SecondaryDemand", &key);
BOOST_CHECK(error == 0);
error = EN_addpattern(ph, (char *)"TertiaryPattern");
BOOST_REQUIRE(error == 0);
error = EN_adddemand(ph, node_qhut, 1.0, "TertiaryPattern", "TertiaryDemand", &demand_key);
BOOST_CHECK(error == 0);
error = EN_removedemand(ph, node_qhut, key);
BOOST_CHECK(error == 0);
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()

295
tests/test_demand_data.cpp Normal file
View File

@@ -0,0 +1,295 @@
/*
******************************************************************************
Project: OWA EPANET
Version: 2.2
Module: /test_demand_data.cpp
Description: tests demand data list node struct
Authors: see AUTHORS
Copyright: see AUTHORS
License: see LICENSE
Last Updated: 04/18/2019
******************************************************************************
*/
#define BOOST_TEST_MODULE demand_data
#include <boost/test/unit_test.hpp>
#include "demand.h"
#include "epanet2_2.h"
#define DATA_PATH_NET1 "./net1.inp"
#define DATA_PATH_TMP "./tmp.inp"
#define DATA_PATH_RPT "./test.rpt"
#define DATA_PATH_OUT "./test.out"
boost::test_tools::predicate_result check_string(std::string test, std::string ref)
{
if (ref.compare(test) == 0)
return true;
else
return false;
}
BOOST_AUTO_TEST_SUITE(test_demand_data)
BOOST_AUTO_TEST_CASE(test_create_destroy_demand_list)
{
list_t *dlist;
dlist = create_demand_list(100.0, 1, "CUB_SCOUT_DAY_CAMP");
BOOST_CHECK(dlist != NULL);
delete_list(dlist);
}
BOOST_AUTO_TEST_CASE (test_create_destroy)
{
void *data = NULL;
data = create_demand_data(100.0, 1, NULL);
BOOST_CHECK(data != NULL);
delete_demand_data(&data);
data = NULL;
data = create_demand_data(100.0, 1, "CUB_SCOUT_BASE_CAMP");
BOOST_CHECK(data != NULL);
delete_demand_data(&data);
}
BOOST_AUTO_TEST_CASE(test_get_size)
{
size_t size = get_demand_data_size();
BOOST_CHECK(size == sizeof(demand_data_t *));
}
struct Fixture {
Fixture() {
_data = NULL;
dlist = NULL;
dlist = create_list(get_demand_data_size(), delete_demand_data);
_data = create_demand_data(100.0, 1, "CUB_SCOUT_BASE_CAMP");
append_list(dlist, &_data);
}
~Fixture() {
delete_list(dlist);
}
demand_data_t *_data;
list_t *dlist;
};
BOOST_FIXTURE_TEST_CASE(test_demand_list, Fixture)
{
list_node_t *lnode = head_list(dlist, false);
BOOST_CHECK(lnode != NULL);
}
BOOST_FIXTURE_TEST_CASE(test_demand_getset, Fixture)
{
list_node_t *lnode = head_list(dlist, false);
double demand;
demand = get_base_demand(lnode);
BOOST_CHECK(demand == 100.0);
set_base_demand(lnode, 200.0);
demand = get_base_demand(lnode);
BOOST_CHECK(demand == 200.0);
}
BOOST_FIXTURE_TEST_CASE(test_pattern_getset, Fixture)
{
list_node_t *lnode = head_list(dlist, false);
int index;
index = get_pattern_index(lnode);
BOOST_CHECK(index == 1);
set_pattern_index(lnode, 2);
index = get_pattern_index(lnode);
BOOST_CHECK(index == 2);
}
BOOST_FIXTURE_TEST_CASE(test_category_getset, Fixture)
{
list_node_t *lnode = head_list(dlist, false);
char *name = NULL;
name = get_category_name(lnode);
BOOST_CHECK(check_string(name, (char *)"CUB_SCOUT_BASE_CAMP"));
free(name);
name = NULL;
set_category_name(lnode, (char *)"CUB_SCOUT_COMMAND");
name = get_category_name(lnode);
BOOST_CHECK(check_string(name, "CUB_SCOUT_COMMAND"));
free(name);
}
BOOST_FIXTURE_TEST_CASE(test_convert_demand, Fixture)
{
list_node_t *lnode = head_list(dlist, false);
BOOST_CHECK(lnode != NULL);
// 100.0 GPM == 6.31 LPS
convert_units(lnode, 15.850);
double demand = get_base_demand(lnode);
BOOST_TEST(demand == 6.31, boost::test_tools::tolerance(0.01));
}
BOOST_AUTO_TEST_CASE(test_initclose)
{
int error;
EN_Project ph = NULL;
EN_createproject(&ph);
error = EN_init(ph, DATA_PATH_RPT, DATA_PATH_OUT, EN_GPM, EN_HW);
BOOST_REQUIRE(error == 0);
error = EN_close(ph);
BOOST_REQUIRE(error == 0);
EN_deleteproject(&ph);
}
#define DATA_PATH_NET1 "./net1.inp"
#define DATA_PATH_TMP "./tmp.inp"
#define DATA_PATH_RPT "./test.rpt"
#define DATA_PATH_OUT "./test.out"
struct FixtureSingleNode {
FixtureSingleNode() {
error = 0;
ph = NULL;
EN_createproject(&ph);
EN_init(ph, DATA_PATH_RPT, DATA_PATH_OUT, EN_GPM, EN_HW);
EN_addnode(ph, (char *)"CUB_SCOUT_QUONSET_HUT", EN_JUNCTION, &node_qhut);
//EN_getnodeindex(ph, (char *)"CUB_SCOUT_QUONSET_HUT", &node_qhut);
}
~FixtureSingleNode() {
EN_close(ph);
EN_deleteproject(&ph);
}
int error, index, node_qhut;
EN_Project ph;
};
BOOST_FIXTURE_TEST_CASE(test_single_node, FixtureSingleNode)
{
int demand_idx, pattern_idx, n;
double demand;
error = EN_getnumdemands(ph, node_qhut, &n);
BOOST_REQUIRE(error == 0);
BOOST_CHECK(n == 0);
demand_idx = 1;
error = EN_getbasedemand(ph, node_qhut, demand_idx, &demand);
BOOST_REQUIRE(error == 253);
error = EN_getdemandpattern(ph, node_qhut, demand_idx, &pattern_idx);
BOOST_REQUIRE(error == 253);
char demname[31];
error = EN_getdemandname(ph, node_qhut, demand_idx, demname);
BOOST_REQUIRE(error == 253);
BOOST_CHECK(check_string(demname, "\0"));
error = EN_setbasedemand(ph, node_qhut, demand_idx, 100.0);
BOOST_REQUIRE(error == 0);
// only one demand category
pattern_idx = 1;
error = EN_setdemandpattern(ph, node_qhut, demand_idx, pattern_idx);
BOOST_REQUIRE(error == 205);
// create pattern
error = EN_addpattern(ph, (char *)"Pat2");
BOOST_REQUIRE(error == 0);
error = EN_getpatternindex(ph, (char *)"Pat2", &pattern_idx);
BOOST_REQUIRE(error == 0);
error = EN_setdemandpattern(ph, node_qhut, demand_idx, pattern_idx);
BOOST_REQUIRE(error == 0);
error = EN_setdemandname(ph, node_qhut, demand_idx, (char *)"CUB_SCOUT_MESS_HALL");
BOOST_REQUIRE(error == 0);
}
BOOST_FIXTURE_TEST_CASE(test_pattern_edits, FixtureSingleNode)
{
int n, node_cpoint, pat2_idx, pat3_idx;
EN_addnode(ph, (char *)"CUB_SCOUT_CHECKPOINT", EN_JUNCTION, &node_cpoint);
//EN_getnodeindex(ph, (char *)"CUB_SCOUT_CHECKPOINT", &node_cpoint);
// Add 2 new patterns
error = EN_addpattern(ph, (char *)"DefPat");
BOOST_REQUIRE(error == 0);
error = EN_addpattern(ph, (char *)"Pat2");
BOOST_REQUIRE(error == 0);
error = EN_getpatternindex(ph, (char *)"Pat2", &pat2_idx);
BOOST_REQUIRE(error == 0);
error = EN_addpattern(ph, (char *)"Pat3");
BOOST_REQUIRE(error == 0);
error = EN_getpatternindex(ph, (char *)"Pat3", &pat3_idx);
BOOST_REQUIRE(error == 0);
double f2[] = { 2.1, 2.2 };
double f3[] = { 3.1, 3.2, 3.3, 3.4 };
error = EN_setpattern(ph, pat2_idx, f2, 2);
BOOST_REQUIRE(error == 0);
error = EN_setpattern(ph, pat3_idx, f3, 4);
BOOST_REQUIRE(error == 0);
// Assign Pat3 to 3rd junction
error = EN_setdemandpattern(ph, node_cpoint, 1, pat3_idx);
BOOST_REQUIRE(error == 0);
// Delete Pat2
error = EN_deletepattern(ph, pat2_idx);
BOOST_REQUIRE(error == 0);
//Check that there are now 2 patterns
error = EN_getcount(ph, EN_PATCOUNT, &n);
BOOST_REQUIRE(error == 0);
BOOST_CHECK(n == 2);
// Check that Pat3 with 4 factors is still assigned to 3rd junction
error = EN_getdemandpattern(ph, node_cpoint, 1, &pat3_idx);
BOOST_REQUIRE(error == 0);
error = EN_getpatternlen(ph, pat3_idx, &n);
BOOST_REQUIRE(error == 0);
BOOST_CHECK(n == 4);
}
BOOST_AUTO_TEST_SUITE_END()

View File

@@ -18,7 +18,51 @@
BOOST_AUTO_TEST_SUITE (pattern) BOOST_AUTO_TEST_SUITE (pattern)
BOOST_AUTO_TEST_CASE(add_set_pattern) BOOST_FIXTURE_TEST_CASE(test_set_get_default_pattern, FixtureOpenClose)
{
// Assign the default pattern index
int defPatIdx = 1;
int patIdx;
// Rename the default pattern
error = EN_setpatternid(ph, defPatIdx, (char *)"Pat1");
BOOST_REQUIRE(error == 0);
error = EN_getpatternindex(ph, (char *)"Pat1", &patIdx);
BOOST_REQUIRE(error == 0);
BOOST_CHECK(defPatIdx == patIdx);
}
BOOST_FIXTURE_TEST_CASE(test_add_delete, FixtureOpenClose)
{
double f2[] = { 2.1, 2.2 };
double f3[] = { 3.1, 3.2, 3.3, 3.4 };
// Add 2 new patterns
error = EN_addpattern(ph, (char *)"Pat2");
BOOST_REQUIRE(error == 0);
error = EN_addpattern(ph, (char *)"Pat3");
BOOST_REQUIRE(error == 0);
error = EN_setpattern(ph, 2, f2, 2);
BOOST_REQUIRE(error == 0);
error = EN_setpattern(ph, 3, f3, 4);
BOOST_REQUIRE(error == 0);
// Delete Pat2
error = EN_deletepattern(ph, 2);
BOOST_REQUIRE(error == 0);
// Check that there are now 2 patterns
int n;
error = EN_getcount(ph, EN_PATCOUNT, &n);
BOOST_REQUIRE(error == 0);
BOOST_CHECK(n == 2);
}
BOOST_AUTO_TEST_CASE(test_add_set)
{ {
std::string path_inp(DATA_PATH_NET1); std::string path_inp(DATA_PATH_NET1);
std::string path_rpt(DATA_PATH_RPT); std::string path_rpt(DATA_PATH_RPT);
@@ -34,7 +78,7 @@ BOOST_AUTO_TEST_CASE(add_set_pattern)
BOOST_REQUIRE(error == 0); BOOST_REQUIRE(error == 0);
// Assign the default pattern index // Assign the default pattern index
int defPatIdx = 1; int n, defPatIdx = 1;
int patIdx; int patIdx;
// Rename the default pattern // Rename the default pattern
@@ -56,8 +100,7 @@ BOOST_AUTO_TEST_CASE(add_set_pattern)
// Delete Pat2 // Delete Pat2
EN_deletepattern(ph, 2); EN_deletepattern(ph, 2);
// Check that there are now 2 patterns //Check that there are now 2 patterns
int n;
EN_getcount(ph, EN_PATCOUNT, &n); EN_getcount(ph, EN_PATCOUNT, &n);
BOOST_REQUIRE(n == 2); BOOST_REQUIRE(n == 2);

View File

@@ -81,10 +81,10 @@ BOOST_AUTO_TEST_CASE(test_save)
error = EN_saveinpfile(ph_save, "test_reopen.inp"); error = EN_saveinpfile(ph_save, "test_reopen.inp");
BOOST_REQUIRE(error == 0); BOOST_REQUIRE(error == 0);
BOOST_CHECK(boost::filesystem::exists("test_reopen.inp") == true); BOOST_CHECK(boost::filesystem::exists("test_reopen.inp") == true);
error = EN_close(ph_save); error = EN_close(ph_save);
BOOST_REQUIRE(error == 0); BOOST_REQUIRE(error == 0);
EN_deleteproject(&ph_save); EN_deleteproject(&ph_save);
} }

View File

@@ -102,6 +102,26 @@ struct FixtureAfterStep{
EN_Project ph; EN_Project ph;
}; };
struct FixtureSingleNode {
FixtureSingleNode() {
error = 0;
ph = NULL;
EN_createproject(&ph);
EN_init(ph, DATA_PATH_RPT, DATA_PATH_OUT, EN_GPM, EN_HW);
EN_addnode(ph, (char *)"CUB_SCOUT_QUONSET_HUT", EN_JUNCTION, &node_qhut);
}
~FixtureSingleNode() {
EN_close(ph);
EN_deleteproject(&ph);
}
int error, index, node_qhut;
EN_Project ph;
};
boost::test_tools::predicate_result check_cdd_double(std::vector<double>& test, boost::test_tools::predicate_result check_cdd_double(std::vector<double>& test,
std::vector<double>& ref, long cdd_tol); std::vector<double>& ref, long cdd_tol);
boost::test_tools::predicate_result check_string(std::string test, std::string ref); boost::test_tools::predicate_result check_string(std::string test, std::string ref);

View File

@@ -13,7 +13,9 @@
****************************************************************************** ******************************************************************************
*/ */
#include <stdlib.h>
#include <string.h> #include <string.h>
#include <time.h>
#define BOOST_TEST_MODULE list #define BOOST_TEST_MODULE list
#include <boost/test/unit_test.hpp> #include <boost/test/unit_test.hpp>
@@ -35,7 +37,7 @@ int *get_int_data(list_node_t *lnode) {
bool iterate_int(list_node_t *lnode) bool iterate_int(list_node_t *lnode)
{ {
printf("Found value: %d\n", *get_int_data(lnode)); printf("At Key: %d Found value: %d\n", get_key(lnode), *get_int_data(lnode));
return true; return true;
} }
@@ -57,14 +59,22 @@ BOOST_AUTO_TEST_CASE(test_int_list){
int i, numbers = 10; int i, numbers = 10;
list_t *list = NULL; list_t *list = NULL;
int key[10 + 1];
srand((unsigned int)time(0));
list = create_list(sizeof(int), NULL); list = create_list(sizeof(int), NULL);
for(i = 1; i <= numbers; i++) { for(i = 1; i <= numbers; i++) {
append_list(list, &i); key[i] = append_list(list, &i);
} }
BOOST_CHECK(size_list(list) == 10); BOOST_CHECK(size_list(list) == 10);
for_each_list(list, iterate_int); listIterator iterator = (listIterator)iterate_int;
for_each_list(list, iterator);
list_node_t *lnode = search_list(list, key[5]);
BOOST_CHECK(get_key(lnode) == key[5]);
delete_list(list); delete_list(list);
} }
@@ -111,7 +121,8 @@ BOOST_FIXTURE_TEST_CASE(test_string_list, FixtureStrings) {
BOOST_CHECK(size_list(list) == 5); BOOST_CHECK(size_list(list) == 5);
for_each_list(list, iterate_string); listIterator iterator = (listIterator)iterate_string;
for_each_list(list, iterator);
} }
@@ -177,38 +188,56 @@ bool iterate_test_data(list_node_t *lnode)
} }
char *get_name(list_node_t *lnode)
{
return get_test_data(lnode)->name;
}
BOOST_AUTO_TEST_CASE(test_struct_list){ BOOST_AUTO_TEST_CASE(test_struct_list){
int key, head_key, tail_key;
list_t *list = NULL; list_t *list = NULL;
list = create_list(sizeof(test_data_t *), delete_test_data); list = create_list(sizeof(test_data_t *), delete_test_data);
test_data_t *data = create_test_data(1, "David"); test_data_t *data = create_test_data(1, "David");
append_list(list, &data); head_key = append_list(list, &data);
data = create_test_data(2, "Kevin"); data = create_test_data(2, "Kevin");
append_list(list, &data); key = append_list(list, &data);
data = create_test_data(3, "Michael"); data = create_test_data(3, "Michael");
append_list(list, &data); append_list(list, &data);
data = create_test_data(4, "Craig");
append_list(list, &data);
BOOST_CHECK(size_list(list) == 3); data = create_test_data(5, "Jimi");
tail_key = append_list(list, &data);
for_each_list(list, iterate_test_data); BOOST_CHECK(size_list(list) == 5);
listIterator iterator = (listIterator)iterate_test_data;
for_each_list(list, iterator);
list_node_t *lnode; // locate a list node by a key
// Iterate over list while maintaining containment of list abstraction printf("Found %s!\n", get_name(search_list(list, key)));
for (lnode = first_list(list); done_list(lnode); lnode = next_list(lnode)) {
test_data_t *test_data = get_test_data(lnode);
if (test_data->num == 2) printf("Removing Kevin\n");
printf("Found %s!\n", test_data->name); remove_node(list, key);
} for_each_list(list, iterator);
lnode = head_list(list, true); printf("Removing David\n");
remove_node(list, head_key);
for_each_list(list, iterator);
printf("Removing Jimi\n");
remove_node(list, tail_key);
for_each_list(list, iterator);
list_node_t *lnode = head_list(list, true);
delete_node(list, lnode); delete_node(list, lnode);
delete_list(list); delete_list(list);