Refactors the API's demand editing functions

This commit is contained in:
Lew Rossman
2019-05-09 10:26:40 -04:00
parent 52bda22833
commit 365ab00dcb
22 changed files with 449 additions and 1124 deletions

View File

@@ -307,6 +307,9 @@ Public Const EN_R_IS_ACTIVE = 3
'Nodal Demand Functions
Declare Function ENgetdemandmodel Lib "epanet2.dll" (type_ As Long, pmin As Single, preq As Single, pexp As Single) As Long
Declare Function ENsetdemandmodel Lib "epanet2.dll" (ByVal type_ As Long, ByVal pmin As Single, ByVal preq As Single, ByVal pexp As Single) As Long
Declare Function ENadddemand Lib "epanet2.dll" (ByVal nodeIndex As Long, ByVal baseDemand As Single, ByVal patternName As String, ByVal demandName As String) As Long
Declare Function ENdeletedemand Lib "epanet2.dll" (ByVal nodeIndex As Long, ByVal demandIndex As Long) As Long
Declare Function ENgetdemandindex Lib "epanet2.dll" (ByVal nodeIndex As Long, ByVal demandName As String, demandIndex As Long) As Long
Declare Function ENgetnumdemands Lib "epanet2.dll" (ByVal nodeIndex As Long, numDemands As Long) As Long
Declare Function ENgetbasedemand Lib "epanet2.dll" (ByVal nodeIndex As Long, ByVal demandIndex As Long, value As Single) As Long
Declare Function ENsetbasedemand Lib "epanet2.dll" (ByVal nodeIndex As Long, ByVal demandIndex As Long, ByVal BaseDemand As Single) As Long

View File

@@ -5,6 +5,7 @@ EXPORTS
ENaddcurve = _ENaddcurve@4
ENaddlink = _ENaddlink@20
ENaddnode = _ENaddnode@12
ENadddemand = _ENadddemand@16
ENaddpattern = _ENaddpattern@4
ENaddrule = _ENaddrule@4
ENclearreport = _ENclearreport@0
@@ -14,6 +15,7 @@ EXPORTS
ENcopyreport = _ENcopyreport@4
ENdeletecontrol = _ENdeletecontrol@4
ENdeletecurve = _ENdeletecurve@4
ENdeletedemand = _ENdeletedemand@8
ENdeletelink = _ENdeletelink@8
ENdeletenode = _ENdeletenode@8
ENdeletepattern = _ENdeletepattern@4
@@ -31,6 +33,7 @@ EXPORTS
ENgetcurvelen = _ENgetcurvelen@8
ENgetcurvetype = _ENgetcurvetype@8
ENgetcurvevalue = _ENgetcurvevalue@16
ENgetdemandindex = _ENgetdemandindex@12
ENgetdemandmodel = _ENgetdemandmodel@16
ENgetdemandname = _ENgetdemandname@12
ENgetdemandpattern = _ENgetdemandpattern@12

View File

@@ -230,8 +230,15 @@ extern "C" {
int DLLEXPORT ENsetdemandmodel(int model, EN_API_FLOAT_TYPE pmin,
EN_API_FLOAT_TYPE preq, EN_API_FLOAT_TYPE pexp);
int DLLEXPORT ENadddemand(int nodeIndex, EN_API_FLOAT_TYPE baseDemand,
char *demandPattern, char *demandName);
int DLLEXPORT ENdeletedemand(int nodeIndex, int demandIndex);
int DLLEXPORT ENgetnumdemands(int nodeIndex, int *numDemands);
int DLLEXPORT ENgetdemandindex(int nodeIndex, char *demandName, int *demandIndex);
int DLLEXPORT ENgetbasedemand(int nodeIndex, int demandIndex,
EN_API_FLOAT_TYPE *baseDemand);

View File

@@ -312,6 +312,9 @@ Public Const EN_R_IS_ACTIVE = 3
'Nodal Demand Functions
Declare Function ENgetdemandmodel Lib "epanet2.dll" (type_ As Int32, pmin As Single, preq As Single, pexp As Single) As Int32
Declare Function ENsetdemandmodel Lib "epanet2.dll" (ByVal type_ As Int32, ByVal pmin As Single, ByVal preq As Single, ByVal pexp As Single) As Int32
Declare Function ENadddemand Lib "epanet2.dll" (ByVal nodeIndex As Int32, ByVal baseDemand As Single, ByVal patternName As String, ByVal demandName As String) As Int32
Declare Function ENdeletedemand Lib "epanet2.dll" (ByVal nodeIndex As Int32, ByVal demandIndex As Int32) As Int32
Declare Function ENgetdemandindex Lib "epanet2.dll" (ByVal nodeIndex As Int32, ByVal demandName As String, demandIndex As Int32) As Int32
Declare Function ENgetnumdemands Lib "epanet2.dll" (ByVal nodeIndex As Int32, numDemands As Int32) As Int32
Declare Function ENgetbasedemand Lib "epanet2.dll" (ByVal nodeIndex As Int32, ByVal demandIndex As Int32, value As Single) As Int32
Declare Function ENsetbasedemand Lib "epanet2.dll" (ByVal nodeIndex As Int32, ByVal demandIndex As Int32, ByVal BaseDemand As Single) As Int32

View File

@@ -853,13 +853,6 @@ typedef struct Project *EN_Project;
int DLLEXPORT EN_setjuncdata(EN_Project ph, int index, double elev, double dmnd,
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.
@param ph an EPANET project handle.
@@ -940,6 +933,41 @@ typedef struct Project *EN_Project;
int DLLEXPORT EN_setdemandmodel(EN_Project ph, int type, double pmin,
double preq, double pexp);
/**
@brief appends a new demand to a junction node demands list.
@param ph an EPANET project handle.
@param nodeIndex the index of a node (starting from 1).
@param baseDemand the demand's base value.
@param demandPattern the name of a time pattern used by the demand
@param demandName the name of the demand's category
@return an error code.
A NULL or blank string can be used for `demandPattern` and for `demandName` to indicate
that no time pattern or category name is associated with the demand.
*/
int DLLEXPORT EN_adddemand(EN_Project ph, int nodeIndex, double baseDemand,
char *demandPattern, char *demandName);
/**
@brief deletes a demand from a junction node.
@param ph an EPANET project handle.
@param nodeIndex the index of a node (starting from 1).
@param demandIndex the position of the demand in the node's demands list (starting from 1).
@return an error code.
*/
int DLLEXPORT EN_deletedemand(EN_Project ph, int nodeIndex, int demandIndex);
/**
@brief Retrieves the index of a node's named demand category
@param nodeIndex the index of a node (starting from 1)
@param demandName the name of a demand category for the node
@param[out] demandIndex the index of the demand being sought
@return an error code
*/
int DLLEXPORT EN_getdemandindex(EN_Project p, int nodeIndex, char *demandName,
int *demandIndex);
/**
@brief Retrieves the number of demand categories for a junction node.
@param ph an EPANET project handle.

View File

@@ -1,133 +0,0 @@
/*
******************************************************************************
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);
}

View File

@@ -1,63 +0,0 @@
/*
******************************************************************************
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

@@ -20,7 +20,6 @@
#endif
#include <stdio.h>
#include <string.h>
#include <float.h>
#include <math.h>
@@ -30,8 +29,6 @@
#include "text.h"
#include "enumstxt.h"
#include "demand.h"
#ifdef _WIN32
#define snprintf _snprintf
#endif
@@ -1721,8 +1718,7 @@ int DLLEXPORT EN_addnode(EN_Project p, char *id, int nodeType, int *index)
Hydraul *hyd = &p->hydraul;
Quality *qual = &p->quality;
int i, nIdx;
int size;
int i, nIdx, size;
Stank *tank;
Snode *node;
Scontrol *control;
@@ -1752,8 +1748,8 @@ int DLLEXPORT EN_addnode(EN_Project p, char *id, int nodeType, int *index)
net->Njuncs++;
nIdx = net->Njuncs;
node = &net->Node[nIdx];
node->D = NULL;
node->D = NULL;
adddemand(node, 0.0, 0, NULL);
// shift rest of Node array
for (i = net->Nnodes; i >= net->Njuncs; i--)
@@ -1766,21 +1762,18 @@ int DLLEXPORT EN_addnode(EN_Project p, char *id, int nodeType, int *index)
{
net->Tank[i].Node += 1;
}
// shift indices of Links, if necessary
for (i = 1; i <= net->Nlinks; i++)
{
if (net->Link[i].N1 > net->Njuncs - 1) net->Link[i].N1 += 1;
if (net->Link[i].N2 > net->Njuncs - 1) net->Link[i].N2 += 1;
}
// shift indices of tanks/reservoir nodes in controls
for (i = 1; i <= net->Ncontrols; ++i)
{
control = &net->Control[i];
if (control->Node > net->Njuncs - 1) control->Node += 1;
}
// adjust indices of tanks/reservoirs in Rule premises (see RULES.C)
adjusttankrules(p);
}
@@ -1854,7 +1847,6 @@ int DLLEXPORT EN_deletenode(EN_Project p, int index, int actionCode)
int i, nodeType, tankindex;
Snode *node;
list_t *demand;
// Cannot modify network structure while solvers are active
if (!p->Openflag) return 102;
@@ -1886,10 +1878,7 @@ int DLLEXPORT EN_deletenode(EN_Project p, int index, int actionCode)
hashtable_delete(net->NodeHashTable, node->ID);
// Free memory allocated to node's demands, WQ source & comment
demand = node->D;
if (demand)
delete_list(demand);
freedemands(node);
free(node->S);
free(node->Comment);
@@ -2081,25 +2070,18 @@ int DLLEXPORT EN_getnodevalue(EN_Project p, int index, int property, double *val
break;
case EN_BASEDEMAND:
v = 0.0;
// NOTE: primary demand category is first on demand list
if (index <= nJuncs)
{
list_t *demand = Node[index].D;
if (demand)
v = get_base_demand(head_list(demand, false));
if (Node[index].D) v = Node[index].D->Base * Ucf[FLOW];
}
v *= Ucf[FLOW];
break;
case EN_PATTERN:
v = 0.0;
// NOTE: primary demand category is first on demand list
if (index <= nJuncs)
{
list_t *demand = Node[index].D;
if (demand)
v = get_pattern_index(head_list(demand, false));
if (Node[index].D) v = (double)(Node[index].D->Pat);
}
else v = (double)(Tank[index - nJuncs].Pat);
break;
@@ -2280,9 +2262,7 @@ int DLLEXPORT EN_setnodevalue(EN_Project p, int index, int property, double valu
// NOTE: primary demand category is first on demand list
if (index <= nJuncs)
{
list_t *demand = Node[index].D;
if (demand)
set_base_demand(head_list(demand, false), value / Ucf[FLOW]);
if (Node[index].D) Node[index].D->Base = value / Ucf[FLOW];
}
break;
@@ -2292,9 +2272,7 @@ int DLLEXPORT EN_setnodevalue(EN_Project p, int index, int property, double valu
if (j < 0 || j > nPats) return 205;
if (index <= nJuncs)
{
list_t *demand = Node[index].D;
if (demand)
set_pattern_index(head_list(demand, false), j);
if (Node[index].D) Node[index].D->Pat = j;
}
else Tank[index - nJuncs].Pat = j;
break;
@@ -2542,81 +2520,33 @@ int DLLEXPORT EN_setjuncdata(EN_Project p, int index, double elev,
**----------------------------------------------------------------
*/
{
Network *net = &p->network;
int i, patIndex = 0;
Snode *Node = net->Node;
int patIndex = 0;
Snode *node;
// Check that junction exists
if (!p->Openflag) return 102;
if (index <= 0 || index > net->Njuncs) return 203;
if (index <= 0 || index > p->network.Njuncs) return 203;
// Check that demand pattern exists
if (strlen(dmndpat) > 0)
if (dmndpat && strlen(dmndpat) > 0)
{
for (i = 1; i <= net->Npats; i++)
{
if (strcmp(dmndpat, net->Pattern[i].ID) == 0)
{
patIndex = i;
break;
}
}
if (patIndex == 0) return 205;
if (EN_getpatternindex(p, dmndpat, &patIndex) > 0) return 205;
}
// Assign values to junction's parameters
Node[index].El = elev / p->Ucf[ELEV];
list_t *demand_list = Node[index].D;
if (!demand_list) {
demand_list = create_list(get_demand_data_size(), delete_demand_data);
if (!demand_list) return 101;
// Assign demand parameters to junction's primary demand category
node = &(p->network.Node[index]);
dmnd /= p->Ucf[FLOW];
// Category exists - update its properties
if (node->D)
{
(node->D)->Base = dmnd;
(node->D)->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);
// No demand categories exist -- create a new one
else if (!adddemand(node, dmnd, patIndex, NULL)) return 101;
// Assign new elevation value to junction
node->El = elev / p->Ucf[ELEV];
return 0;
}
@@ -2793,6 +2723,138 @@ int DLLEXPORT EN_setdemandmodel(EN_Project p, int model, double pmin,
return 0;
}
int DLLEXPORT EN_adddemand(EN_Project p, int nodeIndex, double baseDemand,
char *demandPattern, char *demandName)
/*----------------------------------------------------------------
** Input: nodeIndex = node index
** baseDemand = baseline demand value
** demandPattern = name of demand's time pattern (can be NULL or empty)
** demandName = name of demand's category (can be NULL or empty)
** Returns: error code
** Purpose: adds a new demand category to a junction node
**----------------------------------------------------------------
*/
{
int patIndex = 0;
Snode *node;
// Check for valid arguments
if (!p->Openflag) return 102;
if (nodeIndex <= 0 || nodeIndex > p->network.Nnodes) return 203;
if (demandPattern && strlen(demandPattern) > 0)
{
if (EN_getpatternindex(p, demandPattern, &patIndex) > 0) return 205;
}
// Do nothing if node is not a junction
if (nodeIndex > p->network.Njuncs) return 0;
// Add the new demand to the node's demands list
node = &(p->network.Node[nodeIndex]);
if (!adddemand(node, baseDemand / p->Ucf[FLOW], patIndex, demandName)) return 101;
return 0;
}
int DLLEXPORT EN_deletedemand(EN_Project p, int nodeIndex, int demandIndex)
/*----------------------------------------------------------------
** Input: nodeIndex = node index
** demandIndex = index of node's demand to be deleted
** Returns: error code
** Purpose: deletes an existing demand category from a junction node
**----------------------------------------------------------------
*/
{
Pdemand d, dprev;
Snode *node;
int n = 1;
// Check for valid arguments
if (!p->Openflag) return 102;
if (nodeIndex <= 0 || nodeIndex > p->network.Nnodes) return 203;
// Only junctions have demands
if (nodeIndex <= p->network.Njuncs)
{
// Find head of node's list of demands
node = &p->network.Node[nodeIndex];
d = node->D;
if (d == NULL) return 253;
dprev = d;
// Check if target demand is head of demand list
if (demandIndex == 1)
{
node->D = d->next;
free(d->Name);
free(d);
return 0;
}
// Otherwise locate target demand in demand list
while (d != NULL && n < demandIndex)
{
dprev = d;
d = d->next;
n++;
}
// Return error if target demand not found
if (d == NULL) return 253;
// Link the demands that precede and follow the target
dprev->next = d->next;
// Delete the target demand
free(d->Name);
free(d);
}
return 0;
}
int DLLEXPORT EN_getdemandindex(EN_Project p, int nodeIndex, char *demandName,
int *demandIndex)
/*----------------------------------------------------------------
** Input: nodeIndex = node index
** demandName = name of demand being sought
** Output: demandIndex = index of demand being sought
** Returns: error code
** Purpose: retrieves the position of a named demand category
** in a node's list of demands
**----------------------------------------------------------------
*/
{
Pdemand d;
int n = 0;
int nameEmpty = FALSE;
int found = FALSE;
// Check for valid arguments
*demandIndex = 0;
if (!p->Openflag) return 102;
if (nodeIndex <= 0 || nodeIndex > p->network.Nnodes) return 203;
if (demandName == NULL) return 253;
// Check if target name is empty
if (strlen(demandName) == 0) nameEmpty = TRUE;
// Locate target demand in node's demands list
for (d = p->network.Node[nodeIndex].D; d != NULL; d = d->next)
{
n++;
if (d->Name == NULL)
{
if (nameEmpty) found = TRUE;;
}
else if (strcmp(d->Name, demandName) == 0) found = TRUE;
if (found) break;
}
// Return target demand's index
if (!found) return 253;
*demandIndex = n;
return 0;
}
int DLLEXPORT EN_getnumdemands(EN_Project p, int nodeIndex, int *numDemands)
/*----------------------------------------------------------------
** Input: nodeIndex = node index
@@ -2802,20 +2864,16 @@ int DLLEXPORT EN_getnumdemands(EN_Project p, int nodeIndex, int *numDemands)
**----------------------------------------------------------------
*/
{
//Pdemand d;
//int n = 0;
Pdemand d;
int n = 0;
// Check for valid arguments
if (!p->Openflag) return 102;
if (nodeIndex <= 0 || nodeIndex > p->network.Nnodes) return 203;
// Count the number of demand categories
list_t *demand_list = p->network.Node[nodeIndex].D;
if (demand_list)
*numDemands = size_list(demand_list);
else
*numDemands = 0;
// Count the number of demand categories assigned to node
for (d = p->network.Node[nodeIndex].D; d != NULL; d = d->next) n++;
*numDemands = n;
return 0;
}
@@ -2830,22 +2888,19 @@ int DLLEXPORT EN_getbasedemand(EN_Project p, int nodeIndex, int demandIndex,
**----------------------------------------------------------------
*/
{
Pdemand d;
// Check for valid arguments
*baseDemand = 0.0;
if (!p->Openflag) return 102;
if (nodeIndex <= 0 || nodeIndex > p->network.Nnodes) return 203;
// Retrieve demand for specified category
if (nodeIndex <= p->network.Njuncs)
{
// Locate demand category record and assign demandName to it
list_t *dlist = p->network.Node[nodeIndex].D;
list_node_t *lnode = get_nth_list(dlist, demandIndex);
if (!lnode)
return 253;
else
*baseDemand = get_base_demand(lnode) * p->Ucf[FLOW];
}
else *baseDemand = (double)(0.0);
// Locate target demand in node's demands list
d = finddemand(p->network.Node[nodeIndex].D, demandIndex);
if (d == NULL) return 253;
// Retrieve target demand's base value
*baseDemand = d->Base * p->Ucf[FLOW];
return 0;
}
@@ -2861,30 +2916,18 @@ int DLLEXPORT EN_setbasedemand(EN_Project p, int nodeIndex, int demandIndex,
**----------------------------------------------------------------
*/
{
Pdemand d;
// Check for valid arguments
if (!p->Openflag) return 102;
if (nodeIndex <= 0 || nodeIndex > p->network.Nnodes) return 203;
// Set baseline demand for specified category
if (nodeIndex <= p->network.Njuncs)
{
list_t *dlist = p->network.Node[nodeIndex].D;
// If demand list is null create one and set demand
if (!dlist) {
dlist = create_demand_list(baseDemand / p->Ucf[FLOW], 0, NULL);
if (!dlist) return 101;
// Locate target demand in node's demands list
d = finddemand(p->network.Node[nodeIndex].D, demandIndex);
if (d == NULL) return 253;
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]);
}
}
// Assign new base value to target demand
d->Base = baseDemand / p->Ucf[FLOW];
return 0;
}
@@ -2899,7 +2942,7 @@ int DLLEXPORT EN_getdemandname(EN_Project p, int nodeIndex, int demandIndex,
**----------------------------------------------------------------
*/
{
char *temp = NULL;
Pdemand d;
strcpy(demandName, "");
@@ -2907,22 +2950,12 @@ int DLLEXPORT EN_getdemandname(EN_Project p, int nodeIndex, int demandIndex,
if (!p->Openflag) return 102;
if (nodeIndex <= 0 || nodeIndex > p->network.Njuncs) return 203;
// Locate demand category record and retrieve its name
list_t *dlist = p->network.Node[nodeIndex].D;
if (dlist) {
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;
// Locate target demand in node's demands list
d = finddemand(p->network.Node[nodeIndex].D, demandIndex);
if (d == NULL) return 253;
// Retrieve target demand's category name
if (d->Name) strcpy(demandName, d->Name);
return 0;
}
@@ -2938,29 +2971,18 @@ int DLLEXPORT EN_setdemandname(EN_Project p, int nodeIndex, int demandIndex,
**----------------------------------------------------------------
*/
{
Pdemand d;
// Check for valid arguments
if (!p->Openflag) return 102;
if (nodeIndex <= 0 || nodeIndex > p->network.Njuncs) return 203;
// Check that demandName is not too long
if (strlen(demandName) > MAXID) return 252;
// Locate target demand in node's demands list
d = finddemand(p->network.Node[nodeIndex].D, demandIndex);
if (d == NULL) return 253;
// Locate demand category record and assign demandName to it
list_t *dlist = p->network.Node[nodeIndex].D;
if (!dlist) {
dlist = create_demand_list(0, 0, demandName);
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);
}
// Assign category name to target demand
d->Name = xstrcpy(&d->Name, demandName, MAXID);
return 0;
}
@@ -2976,18 +2998,19 @@ int DLLEXPORT EN_getdemandpattern(EN_Project p, int nodeIndex, int demandIndex,
**----------------------------------------------------------------
*/
{
Pdemand d;
// Check for valid arguments
*patIndex = 0;
if (!p->Openflag) return 102;
if (nodeIndex <= 0 || nodeIndex > p->network.Nnodes) return 203;
// Locate demand category record and assign demandName to it
list_t *dlist = p->network.Node[nodeIndex].D;
list_node_t *lnode = get_nth_list(dlist, demandIndex);
if (!lnode)
return 253;
else
*patIndex = get_pattern_index(lnode);
// Locate target demand in node's demand list
d = finddemand(p->network.Node[nodeIndex].D, demandIndex);
if (d == NULL) return 253;
// Retrieve that demand's pattern index
*patIndex = d->Pat;
return 0;
}
@@ -3005,29 +3028,19 @@ int DLLEXPORT EN_setdemandpattern(EN_Project p, int nodeIndex, int demandIndex,
{
Network *net = &p->network;
Pdemand d;
// Check for valid arguments
if (!p->Openflag) return 102;
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
if (nodeIndex <= net->Njuncs) {
// Locate target demand in node's demand list
d = finddemand(p->network.Node[nodeIndex].D, demandIndex);
if (d == NULL) return 253;
list_t *dlist = p->network.Node[nodeIndex].D;
if (!dlist) {
dlist = create_demand_list(0, patIndex, NULL);
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);
}
}
// Assign new time pattern to target demand
d->Pat = patIndex;
return 0;
}

View File

@@ -385,6 +385,22 @@ int DLLEXPORT ENsetdemandmodel(int model, EN_API_FLOAT_TYPE pmin,
return EN_setdemandmodel(_defaultProject, model, pmin, preq, pexp);
}
int DLLEXPORT ENadddemand(int nodeIndex, EN_API_FLOAT_TYPE baseDemand,
char *demandPattern, char *demandName)
{
return EN_adddemand(_defaultProject, nodeIndex, baseDemand, demandPattern, demandName);
}
int DLLEXPORT ENdeletedemand(int nodeIndex, int demandIndex)
{
return EN_deletedemand(_defaultProject, nodeIndex, demandIndex);
}
int DLLEXPORT ENgetdemandindex(int nodeIndex, char *demandName, int *demandIndex)
{
return EN_getdemandindex(_defaultProject, nodeIndex, demandName, demandIndex);
}
int DLLEXPORT ENgetnumdemands(int nodeIndex, int *numDemands)
{
return EN_getnumdemands(_defaultProject, nodeIndex, numDemands);

View File

@@ -37,6 +37,10 @@ int findpump(Network *, int);
int findpattern(Network *, char *);
int findcurve(Network *, char *);
Pdemand finddemand(Pdemand, int);
int adddemand(Snode *, double, int, char *);
void freedemands(Snode *);
void adjustpatterns(Network *, int);
void adjustcurves(Network *, int);
int resizecurve(Scurve *, int);

View File

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

View File

@@ -28,7 +28,6 @@ Last Updated: 04/03/2019
#include "hash.h"
#include "text.h"
#include "demand.h"
// Defined in enumstxt.h in EPANET.C
extern char *LinkTxt[];
@@ -121,48 +120,6 @@ void saveauxdata(Project *pr, FILE *f)
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)
/*
-------------------------------------------------
@@ -179,9 +136,9 @@ int saveinpfile(Project *pr, const char *fname)
Times *time = &pr->times;
int i, j, n;
double d, kc, ke, km;
double d, kc, ke, km, ucf;
char s[MAXLINE + 1], s1[MAXLINE + 1], s2[MAXLINE + 1];
//Pdemand demand;
Pdemand demand;
Psource source;
FILE *f;
Slink *link;
@@ -371,7 +328,21 @@ int saveinpfile(Project *pr, const char *fname)
// Write [DEMANDS] section
write_demands(pr, f);
fprintf(f, "\n\n");
fprintf(f, s_DEMANDS);
ucf = pr->Ucf[DEMAND];
for (i = 1; i <= net->Njuncs; i++)
{
node = &net->Node[i];
for (demand = node->D; demand != NULL; demand = demand->next)
{
sprintf(s, " %-31s %14.6f", node->ID, ucf * demand->Base);
if ((j = demand->Pat) > 0) sprintf(s1, " %-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

View File

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

View File

@@ -20,10 +20,8 @@ Last Updated: 04/03/2019
#endif
#include <stdio.h>
#include <string.h>
#include <math.h>
#include "demand.h"
#include "types.h"
#include "funcs.h"
#include "hash.h"
@@ -86,10 +84,6 @@ int juncdata(Project *pr)
int njuncs; // number of network junction nodes
double el, // elevation
y = 0.0; // base demand
list_t *demand_list = NULL; // demand list
Snode *node;
// Add new junction to data base
@@ -122,13 +116,9 @@ int juncdata(Project *pr)
node->Type = JUNCTION;
node->Comment = xstrcpy(&node->Comment, parser->Comment, MAXMSG);
// create demand data only if a demand has been specified
if (y != 0.0) {
demand_list = create_demand_list(y, p, NULL);
if (!demand_list) return 101;
}
node->D = demand_list;
// Create a demand for the junction and use NodeDemand as an indicator
// to be used when processing demands from the [DEMANDS] section
if (!adddemand(node, y, p, NULL)) return 101;
hyd->NodeDemand[njuncs] = y;
return 0;
}
@@ -713,11 +703,7 @@ int demanddata(Project *pr)
int j, n, p = 0;
double y;
list_t *demand_list = NULL;
demand_data_t *demand_data = NULL;
//Pdemand demand;
//Pdemand cur_demand;
Pdemand demand;
// Extract data from tokens
n = parser->Ntokens;
@@ -741,27 +727,23 @@ int demanddata(Project *pr)
if (p == 0) return setError(parser, 2, 205);
}
// Replace any demand entered in [JUNCTIONS] section
demand = net->Node[j].D;
if (demand && hyd->NodeDemand[j] != MISSING)
{
// First category encountered will overwrite demand category
// created when junction was read from [JUNCTIONS] 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.
}
// if no demands were specified in [JUNCTIONS] create the list here
demand_list = net->Node[j].D;
if (demand_list == NULL) {
demand_list = create_list(get_demand_data_size(), delete_demand_data);
if (demand_list == NULL) return 101;
net->Node[j].D = demand_list;
}
// else replace the demand data entered in [JUNCTIONS] section
else if (size_list(demand_list) == 1) {
list_node_t *lnode = head_list(demand_list, true);
delete_node(demand_list, lnode);
}
// append the data to the list
demand_data = create_demand_data(y, p, parser->Comment);
if (demand_data == NULL) return 101;
append_list(demand_list, &demand_data);
// Otherwise add new demand to junction
else if (!adddemand(&net->Node[j], y, p, parser->Comment) > 0) return 101;
return 0;
}

View File

@@ -29,7 +29,6 @@
#include "types.h"
#include "funcs.h"
#include "demand.h"
int openfiles(Project *pr, const char *f1, const char *f2, const char *f3)
/*----------------------------------------------------------------
@@ -387,7 +386,6 @@ void freedata(Project *pr)
*/
{
int j;
//Pdemand demand, nextdemand;
// Free memory for computed results
free(pr->hydraul.NodeDemand);
@@ -405,12 +403,8 @@ void freedata(Project *pr)
{
for (j = 1; j <= pr->parser.MaxNodes; j++)
{
// Free memory used for demand category list
list_t *demand = pr->network.Node[j].D;
if(demand)
delete_list(demand);
// Free memory used for WQ source data
// Free memory used for demands and WQ source data
freedemands(&(pr->network.Node[j]));
free(pr->network.Node[j].S);
free(pr->network.Node[j].Comment);
}
@@ -471,6 +465,83 @@ void freedata(Project *pr)
}
}
Pdemand finddemand(Pdemand d, int index)
/*----------------------------------------------------------------
** Input: d = pointer to start of a list of demands
** index = the position of the demand to retrieve
** Output: none
** Returns: the demand at the requested position
** Purpose: finds the demand at a given position in a demand list
**----------------------------------------------------------------
*/
{
int n = 1;
if (index <= 0)return NULL;
while (d)
{
if (n == index) break;
n++;
d = d->next;
}
return d;
}
int adddemand(Snode *node, double dbase, int dpat, char *dname)
/*----------------------------------------------------------------
** Input: node = a network junction node
** dbase = base demand value
** dpat = demand pattern index
** dname = name of demand category
** Output: returns TRUE if successful, FALSE if not
** Purpose: adds a new demand category to a node.
**----------------------------------------------------------------
*/
{
Pdemand demand, lastdemand;
// Create a new demand struct
demand = (struct Sdemand *)malloc(sizeof(struct Sdemand));
if (demand == NULL) return FALSE;
// Assign it the designated properties
demand->Base = dbase;
demand->Pat = dpat;
demand->Name = NULL;
if (dname && strlen(dname) > 0) xstrcpy(&demand->Name, dname, MAXID);
demand->next = NULL;
// If node has no demands make this its first demand category
if (node->D == NULL) node->D = demand;
// Otherwise append this demand to the end of the node's demands list
else
{
lastdemand = node->D;
while (lastdemand->next) lastdemand = lastdemand->next;
lastdemand->next = demand;
}
return TRUE;
}
void freedemands(Snode *node)
/*----------------------------------------------------------------
** Input: node = a network junction node
** Output: node
** Purpose: frees the memory used for a node's list of demands.
**----------------------------------------------------------------
*/
{
Pdemand nextdemand;
Pdemand demand = node->D;
while (demand != NULL)
{
nextdemand = demand->next;
free(demand->Name);
free(demand);
demand = nextdemand;
}
node->D = NULL;
}
int buildadjlists(Network *net)
/*
@@ -790,16 +861,6 @@ void adjustpattern(int *pat, int index)
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)
/*----------------------------------------------------------------
** Input: index = index of time pattern being deleted
@@ -809,17 +870,16 @@ void adjustpatterns(Network *network, int index)
*/
{
int j;
//Pdemand demand;
Pdemand demand;
Psource source;
// Adjust patterns used by junctions
for (j = 1; j <= network->Nnodes; j++)
{
// Adjust demand patterns
list_t *dlist = network->Node[j].D;
if (dlist) {
for (list_node_t *lnode = first_list(dlist); done_list(lnode); lnode = next_list(lnode))
adjust_demand_pattern(lnode, index);
for (demand = network->Node[j].D; demand != NULL; demand = demand->next)
{
adjustpattern(&demand->Pat, index);
}
// Adjust WQ source patterns
source = network->Node[j].S;

View File

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

View File

@@ -23,16 +23,6 @@ add_test(NAME test_net_builder
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
test_toolkit.cpp
test_project.cpp
@@ -81,6 +71,3 @@ add_test(NAME test_filemanager
add_test(NAME test_output
COMMAND ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test_output
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/outfile/data)
add_test(NAME test_list
COMMAND ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test_list)

View File

@@ -84,31 +84,38 @@ BOOST_AUTO_TEST_CASE(test_categories_reopen, * boost::unit_test::depends_on("tes
BOOST_FIXTURE_TEST_CASE(test_adddemand, FixtureSingleNode)
{
int key, demand_key;
int Dindex, nD;
error = EN_adddemand(ph, node_qhut, 100.0, "PrimaryPattern", "PrimaryDemand", &demand_key);
error = EN_adddemand(ph, node_qhut, 100.0, "PrimaryPattern", "PrimaryDemand");
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);
error = EN_adddemand(ph, node_qhut, 100.0, "PrimaryPattern", "PrimaryDemand");
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);
error = EN_adddemand(ph, node_qhut, 10.0, "SecondaryPattern", "SecondaryDemand");
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);
error = EN_adddemand(ph, node_qhut, 1.0, "TertiaryPattern", "TertiaryDemand");
BOOST_CHECK(error == 0);
error = EN_removedemand(ph, node_qhut, key);
error = EN_getdemandindex(ph, node_qhut, "TertiaryDemand", &Dindex);
BOOST_CHECK(error == 0);
error = EN_deletedemand(ph, node_qhut, Dindex);
BOOST_CHECK(error == 0);
error = EN_getnumdemands(ph, node_qhut, &nD);
BOOST_REQUIRE(error == 0);
BOOST_CHECK(nD == 2);
}

View File

@@ -1,295 +0,0 @@
/*
******************************************************************************
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

@@ -25,8 +25,3 @@ add_executable(test_filemanager ./test_filemanager.cpp
target_include_directories(test_filemanager PUBLIC ../../src/)
target_link_libraries(test_filemanager ${Boost_LIBRARIES})
add_executable(test_list ./test_list.cpp
../../src/util/list.c)
target_include_directories(test_list PUBLIC ../../src/)
target_link_libraries(test_list ${Boost_LIBRARIES})

View File

@@ -1,248 +0,0 @@
/*
******************************************************************************
Project: OWA EPANET
Version: 2.2
Module: util/list.h
Description: Generic list
https://gist.github.com/pseudomuto/6334796#file-sample_app-c
Accessed: April 9, 2019
Authors: David Muto, Modified by Michael E. Tryby
Copyright: see AUTHORS
License: see LICENSE
Last Updated: 04/09/2019
******************************************************************************
*/
#include <stdlib.h>
#include <string.h>
#include <time.h>
#define BOOST_TEST_MODULE list
#include <boost/test/unit_test.hpp>
#include "util/list.h"
boost::test_tools::predicate_result check_string(std::string test, std::string ref)
{
if (ref.compare(test) == 0)
return true;
else
return false;
}
int *get_int_data(list_node_t *lnode) {
return (int *)get_data(lnode);
}
bool iterate_int(list_node_t *lnode)
{
printf("At Key: %d Found value: %d\n", get_key(lnode), *get_int_data(lnode));
return true;
}
BOOST_AUTO_TEST_SUITE(test_list)
BOOST_AUTO_TEST_CASE(test_create_delete) {
list_t *list;
list = create_list(sizeof(int), NULL);
delete_list(list);
}
BOOST_AUTO_TEST_CASE(test_int_list){
int i, numbers = 10;
list_t *list = NULL;
int key[10 + 1];
srand((unsigned int)time(0));
list = create_list(sizeof(int), NULL);
for(i = 1; i <= numbers; i++) {
key[i] = append_list(list, &i);
}
BOOST_CHECK(size_list(list) == 10);
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);
}
inline char *get_string_data(list_node_t *lnode)
{
return *(char **)get_data(lnode);
}
bool iterate_string(list_node_t *lnode)
{
printf("Found string value: %s\n", get_string_data(lnode));
return true;
}
void free_string(void *data)
{
free(*(char **)data);
}
struct FixtureStrings{
FixtureStrings() {
list = NULL;
int numNames = 5;
const char *names[] = { "David", "Kevin", "Michael", "Craig", "Jimi" };
list = create_list(sizeof(char *), free_string);
char *name;
for (int i = 0; i < numNames; i++) {
name = strdup(names[i]);
append_list(list, &name);
}
}
~FixtureStrings() {
delete_list(list);
}
list_t *list;
};
BOOST_FIXTURE_TEST_CASE(test_string_list, FixtureStrings) {
BOOST_CHECK(size_list(list) == 5);
listIterator iterator = (listIterator)iterate_string;
for_each_list(list, iterator);
}
BOOST_FIXTURE_TEST_CASE(test_head_list, FixtureStrings) {
BOOST_CHECK(check_string(get_string_data(head_list(list, false)), "David"));
BOOST_CHECK(size_list(list) == 5);
list_node_t *lnode = head_list(list, true);
BOOST_CHECK(check_string(get_string_data(lnode), "David"));
delete_node(list, lnode);
BOOST_CHECK(check_string(get_string_data(head_list(list, false)), "Kevin"));
BOOST_CHECK(size_list(list) == 4);
}
BOOST_FIXTURE_TEST_CASE(test_tail_list, FixtureStrings) {
BOOST_CHECK(check_string(get_string_data(tail_list(list)), "Jimi"));
BOOST_CHECK(size_list(list) == 5);
}
typedef struct test_data_s {
int num;
char *name;
} test_data_t;
test_data_t *create_test_data(int number, const char *name){
test_data_t *data = (test_data_t *)malloc(sizeof(test_data_t));
data->num = number;
if (name)
data->name = strdup(name);
else
data->name = NULL;
return data;
}
void delete_test_data(void *data) {
test_data_t *test_data = *(test_data_t **)data;
if (test_data->name)
free(test_data->name);
free(test_data);
}
inline test_data_t *get_test_data(list_node_t *lnode)
{
return *(test_data_t **)get_data(lnode);
}
bool iterate_test_data(list_node_t *lnode)
{
test_data_t *test_data = get_test_data(lnode);
printf("Found number: %i name: %s\n", test_data->num, test_data->name);
return true;
}
char *get_name(list_node_t *lnode)
{
return get_test_data(lnode)->name;
}
BOOST_AUTO_TEST_CASE(test_struct_list){
int key, head_key, tail_key;
list_t *list = NULL;
list = create_list(sizeof(test_data_t *), delete_test_data);
test_data_t *data = create_test_data(1, "David");
head_key = append_list(list, &data);
data = create_test_data(2, "Kevin");
key = append_list(list, &data);
data = create_test_data(3, "Michael");
append_list(list, &data);
data = create_test_data(4, "Craig");
append_list(list, &data);
data = create_test_data(5, "Jimi");
tail_key = append_list(list, &data);
BOOST_CHECK(size_list(list) == 5);
listIterator iterator = (listIterator)iterate_test_data;
for_each_list(list, iterator);
// locate a list node by a key
printf("Found %s!\n", get_name(search_list(list, key)));
printf("Removing Kevin\n");
remove_node(list, key);
for_each_list(list, iterator);
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_list(list);
}
// TODO: search for an index and return data
BOOST_AUTO_TEST_SUITE_END()

View File

@@ -5,6 +5,7 @@ EXPORTS
ENaddcurve = _ENaddcurve@4
ENaddlink = _ENaddlink@20
ENaddnode = _ENaddnode@12
ENadddemand = _ENadddemand@16
ENaddpattern = _ENaddpattern@4
ENaddrule = _ENaddrule@4
ENclearreport = _ENclearreport@0
@@ -14,6 +15,7 @@ EXPORTS
ENcopyreport = _ENcopyreport@4
ENdeletecontrol = _ENdeletecontrol@4
ENdeletecurve = _ENdeletecurve@4
ENdeletedemand = _ENdeletedemand@8
ENdeletelink = _ENdeletelink@8
ENdeletenode = _ENdeletenode@8
ENdeletepattern = _ENdeletepattern@4
@@ -31,6 +33,7 @@ EXPORTS
ENgetcurvelen = _ENgetcurvelen@8
ENgetcurvetype = _ENgetcurvetype@8
ENgetcurvevalue = _ENgetcurvevalue@16
ENgetdemandindex = _ENgetdemandindex@12
ENgetdemandmodel = _ENgetdemandmodel@16
ENgetdemandname = _ENgetdemandname@12
ENgetdemandpattern = _ENgetdemandpattern@12