/* ****************************************************************************** Project: OWA EPANET Version: 2.2 Module: project.c Description: project data management routines Authors: see AUTHORS Copyright: see AUTHORS License: see LICENSE Last Updated: 11/15/2019 ****************************************************************************** */ #include #include #include #include //*** For the Windows SDK _tempnam function ***// #ifdef _WIN32 #include #endif #include "types.h" #include "funcs.h" int openfiles(Project *pr, const char *f1, const char *f2, const char *f3) /*---------------------------------------------------------------- ** Input: f1 = pointer to name of input file ** f2 = pointer to name of report file ** f3 = pointer to name of binary output file ** Output: none ** Returns: error code ** Purpose: opens input & report files **---------------------------------------------------------------- */ { // Initialize file pointers to NULL pr->parser.InFile = NULL; pr->report.RptFile = NULL; pr->outfile.OutFile = NULL; pr->outfile.HydFile = NULL; pr->outfile.TmpOutFile = NULL; // Save file names strncpy(pr->parser.InpFname, f1, MAXFNAME); strncpy(pr->report.Rpt1Fname, f2, MAXFNAME); strncpy(pr->outfile.OutFname, f3, MAXFNAME); if (strlen(f3) > 0) pr->outfile.Outflag = SAVE; else { pr->outfile.Outflag = SCRATCH; strcpy(pr->outfile.OutFname, pr->TmpOutFname); } // Check that file names are not identical if (strlen(f1) > 0 && (strcomp(f1, f2) || strcomp(f1, f3))) return 301; if (strlen(f3) > 0 && strcomp(f2, f3)) return 301; // Attempt to open input and report files if (strlen(f1) > 0) { if ((pr->parser.InFile = fopen(f1, "rt")) == NULL) return 302; } if (strlen(f2) == 0) pr->report.RptFile = stdout; else { pr->report.RptFile = fopen(f2, "wt"); /*if (pr->report.RptFile == NULL) return 303;*/ } writelogo(pr); return 0; } int openhydfile(Project *pr) /*---------------------------------------------------------------- ** Input: none ** Output: none ** Returns: error code ** Purpose: opens file that saves hydraulics solution **---------------------------------------------------------------- */ { const int Nnodes = pr->network.Nnodes; const int Ntanks = pr->network.Ntanks; const int Nlinks = pr->network.Nlinks; const int Nvalves = pr->network.Nvalves; const int Npumps = pr->network.Npumps; INT4 nsize[6]; // Temporary array INT4 magic; INT4 version; int errcode = 0; // If HydFile currently open, then close it if its not a scratch file if (pr->outfile.HydFile != NULL) { if (pr->outfile.Hydflag == SCRATCH) return 0; fclose(pr->outfile.HydFile); pr->outfile.HydFile = NULL; } // Use Hydflag to determine the type of hydraulics file to use. // Write error message if the file cannot be opened. pr->outfile.HydFile = NULL; switch (pr->outfile.Hydflag) { case SCRATCH: strcpy(pr->outfile.HydFname, pr->TmpHydFname); pr->outfile.HydFile = fopen(pr->outfile.HydFname, "w+b"); break; case SAVE: pr->outfile.HydFile = fopen(pr->outfile.HydFname, "w+b"); break; case USE: pr->outfile.HydFile = fopen(pr->outfile.HydFname, "rb"); break; } if (pr->outfile.HydFile == NULL) return 305; // If a previous hydraulics solution is not being used, then // save the current network size parameters to the file. if (pr->outfile.Hydflag != USE) { magic = MAGICNUMBER; version = ENGINE_VERSION; nsize[0] = Nnodes; nsize[1] = Nlinks; nsize[2] = Ntanks; nsize[3] = Npumps; nsize[4] = Nvalves; nsize[5] = (int)pr->times.Dur; fwrite(&magic, sizeof(INT4), 1, pr->outfile.HydFile); fwrite(&version, sizeof(INT4), 1, pr->outfile.HydFile); fwrite(nsize, sizeof(INT4), 6, pr->outfile.HydFile); } // If a previous hydraulics solution is being used, then // make sure its network size parameters match those of // the current network if (pr->outfile.Hydflag == USE) { fread(&magic, sizeof(INT4), 1, pr->outfile.HydFile); if (magic != MAGICNUMBER) return 306; fread(&version, sizeof(INT4), 1, pr->outfile.HydFile); if (version != ENGINE_VERSION) return 306; if (fread(nsize, sizeof(INT4), 6, pr->outfile.HydFile) < 6) return 306; if (nsize[0] != Nnodes || nsize[1] != Nlinks || nsize[2] != Ntanks || nsize[3] != Npumps || nsize[4] != Nvalves || nsize[5] != pr->times.Dur ) return 306; pr->outfile.SaveHflag = TRUE; } // Save current position in hydraulics file // where storage of hydraulic results begins pr->outfile.HydOffset = ftell(pr->outfile.HydFile); return errcode; } int openoutfile(Project *pr) /*---------------------------------------------------------------- ** Input: none ** Output: none ** Returns: error code ** Purpose: opens binary output file. **---------------------------------------------------------------- */ { int errcode = 0; // Close output file if already opened closeoutfile(pr); // Try to open binary output file pr->outfile.OutFile = fopen(pr->outfile.OutFname, "w+b"); if (pr->outfile.OutFile == NULL) return 304; // Save basic network data & energy usage results ERRCODE(savenetdata(pr)); pr->outfile.OutOffset1 = ftell(pr->outfile.OutFile); ERRCODE(saveenergy(pr)); pr->outfile.OutOffset2 = ftell(pr->outfile.OutFile); // Open temporary file if computing time series statistic if (!errcode) { if (pr->report.Tstatflag != SERIES) { pr->outfile.TmpOutFile = fopen(pr->TmpStatFname, "w+b"); if (pr->outfile.TmpOutFile == NULL) errcode = 304; } else pr->outfile.TmpOutFile = pr->outfile.OutFile; } return errcode; } void closeoutfile(Project *pr) /*---------------------------------------------------------------- ** Input: none ** Output: none ** Purpose: closes binary output file. **---------------------------------------------------------------- */ { if (pr->outfile.TmpOutFile != pr->outfile.OutFile) { if (pr->outfile.TmpOutFile != NULL) { fclose(pr->outfile.TmpOutFile); pr->outfile.TmpOutFile = NULL; } } if (pr->outfile.OutFile != NULL) { if (pr->outfile.OutFile == pr->outfile.TmpOutFile) { pr->outfile.TmpOutFile = NULL; } fclose(pr->outfile.OutFile); pr->outfile.OutFile = NULL; } } void initpointers(Project *pr) /*---------------------------------------------------------------- ** Input: none ** Output: none ** Purpose: initializes data array pointers to NULL **---------------------------------------------------------------- */ { Network* nw = &pr->network; nw->Nnodes = 0; nw->Ntanks = 0; nw->Njuncs = 0; nw->Nlinks = 0; nw->Npipes = 0; nw->Npumps = 0; nw->Nvalves = 0; nw->Ncontrols = 0; nw->Nrules = 0; nw->Npats = 0; nw->Ncurves = 0; pr->hydraul.NodeDemand = NULL; pr->hydraul.NodeHead = NULL; pr->hydraul.LinkFlow = NULL; pr->hydraul.LinkStatus = NULL; pr->hydraul.LinkSetting = NULL; pr->hydraul.OldStatus = NULL; pr->hydraul.P = NULL; pr->hydraul.Y = NULL; pr->hydraul.Xflow = NULL; pr->quality.NodeQual = NULL; pr->quality.PipeRateCoeff = NULL; pr->network.Node = NULL; pr->network.Link = NULL; pr->network.Tank = NULL; pr->network.Pump = NULL; pr->network.Valve = NULL; pr->network.Pattern = NULL; pr->network.Curve = NULL; pr->network.Control = NULL; pr->network.Adjlist = NULL; pr->network.NodeHashTable = NULL; pr->network.LinkHashTable = NULL; pr->hydraul.smatrix.Aii = NULL; pr->hydraul.smatrix.Aij = NULL; pr->hydraul.smatrix.F = NULL; pr->hydraul.smatrix.Order = NULL; pr->hydraul.smatrix.Row = NULL; pr->hydraul.smatrix.Ndx = NULL; pr->hydraul.smatrix.XLNZ = NULL; pr->hydraul.smatrix.NZSUB = NULL; pr->hydraul.smatrix.LNZ = NULL; initrules(pr); } int allocdata(Project *pr) /*---------------------------------------------------------------- ** Input: none ** Output: none ** Returns: error code ** Purpose: allocates memory for network data structures **---------------------------------------------------------------- */ { int n; int errcode = 0; // Allocate node & link ID hash tables pr->network.NodeHashTable = hashtable_create(); pr->network.LinkHashTable = hashtable_create(); ERRCODE(MEMCHECK(pr->network.NodeHashTable)); ERRCODE(MEMCHECK(pr->network.LinkHashTable)); // Allocate memory for network nodes //************************************************************* // NOTE: Because network components of a given type are indexed // starting from 1, their arrays must be sized 1 // element larger than the number of components. //************************************************************* if (!errcode) { n = pr->parser.MaxNodes + 1; pr->network.Node = (Snode *)calloc(n, sizeof(Snode)); pr->hydraul.NodeDemand = (double *)calloc(n, sizeof(double)); pr->hydraul.NodeHead = (double *)calloc(n, sizeof(double)); pr->quality.NodeQual = (double *)calloc(n, sizeof(double)); ERRCODE(MEMCHECK(pr->network.Node)); ERRCODE(MEMCHECK(pr->hydraul.NodeDemand)); ERRCODE(MEMCHECK(pr->hydraul.NodeHead)); ERRCODE(MEMCHECK(pr->quality.NodeQual)); } // Allocate memory for network links if (!errcode) { n = pr->parser.MaxLinks + 1; pr->network.Link = (Slink *)calloc(n, sizeof(Slink)); pr->hydraul.LinkFlow = (double *)calloc(n, sizeof(double)); pr->hydraul.LinkSetting = (double *)calloc(n, sizeof(double)); pr->hydraul.LinkStatus = (StatusType *)calloc(n, sizeof(StatusType)); ERRCODE(MEMCHECK(pr->network.Link)); ERRCODE(MEMCHECK(pr->hydraul.LinkFlow)); ERRCODE(MEMCHECK(pr->hydraul.LinkSetting)); ERRCODE(MEMCHECK(pr->hydraul.LinkStatus)); } // Allocate memory for tanks, sources, pumps, valves, and controls // (memory for Patterns and Curves arrays expanded as each is added) if (!errcode) { pr->network.Tank = (Stank *)calloc(pr->parser.MaxTanks + 1, sizeof(Stank)); pr->network.Pump = (Spump *)calloc(pr->parser.MaxPumps + 1, sizeof(Spump)); pr->network.Valve = (Svalve *)calloc(pr->parser.MaxValves + 1, sizeof(Svalve)); pr->network.Control = (Scontrol *)calloc(pr->parser.MaxControls + 1, sizeof(Scontrol)); ERRCODE(MEMCHECK(pr->network.Tank)); ERRCODE(MEMCHECK(pr->network.Pump)); ERRCODE(MEMCHECK(pr->network.Valve)); ERRCODE(MEMCHECK(pr->network.Control)); } // Initialize pointers used in nodes and links if (!errcode) { for (n = 0; n <= pr->parser.MaxNodes; n++) { pr->network.Node[n].D = NULL; // node demand pr->network.Node[n].S = NULL; // node source pr->network.Node[n].Comment = NULL; } for (n = 0; n <= pr->parser.MaxLinks; n++) { pr->network.Link[n].Vertices = NULL; pr->network.Link[n].Comment = NULL; } } // Allocate memory for rule base (see RULES.C) if (!errcode) errcode = allocrules(pr); return errcode; } void freedata(Project *pr) /*---------------------------------------------------------------- ** Input: none ** Output: none ** Purpose: frees memory allocated for network data structures. **---------------------------------------------------------------- */ { int j; // Free memory for computed results free(pr->hydraul.NodeDemand); free(pr->hydraul.NodeHead); free(pr->hydraul.LinkFlow); free(pr->hydraul.LinkSetting); free(pr->hydraul.LinkStatus); free(pr->quality.NodeQual); // Free memory used for nodal adjacency lists freeadjlists(&pr->network); // Free memory for node data if (pr->network.Node != NULL) { for (j = 1; j <= pr->network.Nnodes; j++) { // 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); } free(pr->network.Node); } // Free memory for link data if (pr->network.Link != NULL) { for (j = 1; j <= pr->network.Nlinks; j++) { freelinkvertices(&pr->network.Link[j]); free(pr->network.Link[j].Comment); } } free(pr->network.Link); // Free memory for other network objects free(pr->network.Tank); free(pr->network.Pump); free(pr->network.Valve); free(pr->network.Control); // Free memory for time patterns if (pr->network.Pattern != NULL) { for (j = 0; j <= pr->network.Npats; j++) { free(pr->network.Pattern[j].F); free(pr->network.Pattern[j].Comment); } free(pr->network.Pattern); } // Free memory for curves if (pr->network.Curve != NULL) { // There is no Curve[0] for (j = 1; j <= pr->network.Ncurves; j++) { free(pr->network.Curve[j].X); free(pr->network.Curve[j].Y); free(pr->network.Curve[j].Comment); } free(pr->network.Curve); } // Free memory for rule base (see RULES.C) freerules(pr); // Free hash table memory if (pr->network.NodeHashTable != NULL) { hashtable_free(pr->network.NodeHashTable); } if (pr->network.LinkHashTable != NULL) { hashtable_free(pr->network.LinkHashTable); } } 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 addlinkvertex(Slink *link, double x, double y) /*---------------------------------------------------------------- ** Input: link = pointer to a network link ** x = x-coordinate of a new vertex ** y = y-coordiante of a new vertex ** Returns: an error code ** Purpose: adds to a link's collection of vertex points. **---------------------------------------------------------------- */ { static int CHUNKSIZE = 5; int n; Pvertices vertices; if (link->Vertices == NULL) { vertices = (struct Svertices *) malloc(sizeof(struct Svertices)); if (vertices == NULL) return 101; vertices->Npts = 0; vertices->Capacity = CHUNKSIZE; vertices->X = (double *) calloc(vertices->Capacity, sizeof(double)); vertices->Y = (double *) calloc(vertices->Capacity, sizeof(double)); link->Vertices = vertices; } vertices = link->Vertices; if (vertices->Npts >= vertices->Capacity) { vertices->Capacity += CHUNKSIZE; vertices->X =(double*) realloc(vertices->X, vertices->Capacity * sizeof(double)); vertices->Y = (double*)realloc(vertices->Y, vertices->Capacity * sizeof(double)); } if (vertices->X == NULL || vertices->Y == NULL) return 101; n = vertices->Npts; vertices->X[n] = x; vertices->Y[n] = y; vertices->Npts++; return 0; } void freelinkvertices(Slink *link) /*---------------------------------------------------------------- ** Input: vertices = list of link vertex points ** Output: none ** Purpose: frees the memory used for a link's list of vertices. **---------------------------------------------------------------- */ { if (link->Vertices) { free(link->Vertices->X); free(link->Vertices->Y); free(link->Vertices); link->Vertices = NULL; } } int buildadjlists(Network *net) /* **-------------------------------------------------------------- ** Input: none ** Output: returns error code ** Purpose: builds linked list of links adjacent to each node **-------------------------------------------------------------- */ { int i, j, k; int errcode = 0; Padjlist alink; // Create an array of adjacency lists freeadjlists(net); net->Adjlist = (Padjlist *)calloc(net->Nnodes + 1, sizeof(Padjlist)); if (net->Adjlist == NULL) return 101; for (i = 0; i <= net->Nnodes; i++) net->Adjlist[i] = NULL; // For each link, update adjacency lists of its end nodes for (k = 1; k <= net->Nlinks; k++) { i = net->Link[k].N1; j = net->Link[k].N2; // Include link in start node i's list alink = (struct Sadjlist *) malloc(sizeof(struct Sadjlist)); if (alink == NULL) { errcode = 101; break; } alink->node = j; alink->link = k; alink->next = net->Adjlist[i]; net->Adjlist[i] = alink; // Include link in end node j's list alink = (struct Sadjlist *) malloc(sizeof(struct Sadjlist)); if (alink == NULL) { errcode = 101; break; } alink->node = i; alink->link = k; alink->next = net->Adjlist[j]; net->Adjlist[j] = alink; } if (errcode) freeadjlists(net); return errcode; } void freeadjlists(Network *net) /* **-------------------------------------------------------------- ** Input: none ** Output: none ** Purpose: frees memory used for nodal adjacency lists **-------------------------------------------------------------- */ { int i; Padjlist alink; if (net->Adjlist == NULL) return; for (i = 0; i <= net->Nnodes; i++) { for (alink = net->Adjlist[i]; alink != NULL; alink = net->Adjlist[i]) { net->Adjlist[i] = alink->next; free(alink); } } FREE(net->Adjlist); } int incontrols(Project *pr, int objType, int index) /*---------------------------------------------------------------- ** Input: objType = type of object (either NODE or LINK) ** index = the object's index ** Output: none ** Returns: 1 if any controls contain the object; 0 if not ** Purpose: determines if any simple or rule-based controls ** contain a particular node or link. **---------------------------------------------------------------- */ { Network *net = &pr->network; int i, ruleObject; Spremise *premise; Saction *action; // Check simple controls for (i = 1; i <= net->Ncontrols; i++) { if (objType == NODE && net->Control[i].Node == index) return 1; if (objType == LINK && net->Control[i].Link == index) return 1; } // Check rule-based controls for (i = 1; i <= net->Nrules; i++) { // Convert objType to a rule object type if (objType == NODE) ruleObject = 6; else ruleObject = 7; // Check rule's premises premise = net->Rule[i].Premises; while (premise != NULL) { if (ruleObject == premise->object && premise->index == index) return 1; premise = premise->next; } // Rule actions only need to be checked for link objects if (objType == LINK) { // Check rule's THEN actions action = net->Rule[i].ThenActions; while (action != NULL) { if (action->link == index) return 1; action = action->next; } // Check rule's ELSE actions action = net->Rule[i].ElseActions; while (action != NULL) { if (action->link == index) return 1; action = action->next; } } } return 0; } int valvecheck(Project *pr, int index, int type, int j1, int j2) /* **-------------------------------------------------------------- ** Input: index = link index ** type = valve type ** j1 = index of upstream node ** j2 = index of downstream node ** Output: returns an error code ** Purpose: checks for illegal connections between valves **-------------------------------------------------------------- */ { Network *net = &pr->network; int k, vj1, vj2; LinkType vtype; Slink *link; Svalve *valve; if (type == PRV || type == PSV || type == FCV) { // Can't be connected to a fixed grade node if (j1 > net->Njuncs || j2 > net->Njuncs) return 219; // Examine each existing valve for (k = 1; k <= net->Nvalves; k++) { valve = &net->Valve[k]; if (valve->Link == index) continue; link = &net->Link[valve->Link]; vj1 = link->N1; vj2 = link->N2; vtype = link->Type; // Cannot have two PRVs sharing downstream nodes or in series if (vtype == PRV && type == PRV) { if (vj2 == j2 || vj2 == j1 || vj1 == j2) return 220; } // Cannot have two PSVs sharing upstream nodes or in series if (vtype == PSV && type == PSV) { if (vj1 == j1 || vj1 == j2 || vj2 == j1) return 220; } // Cannot have PSV connected to downstream node of PRV if (vtype == PSV && type == PRV && vj1 == j2) return 220; if (vtype == PRV && type == PSV && vj2 == j1) return 220; // Cannot have PSV connected to downstream node of FCV // nor have PRV connected to upstream node of FCV if (vtype == FCV && type == PSV && vj2 == j1) return 220; if (vtype == FCV && type == PRV && vj1 == j2) return 220; if (vtype == PSV && type == FCV && vj1 == j2) return 220; if (vtype == PRV && type == FCV && vj2 == j1) return 220; } } return 0; } int findnode(Network *network, char *id) /*---------------------------------------------------------------- ** Input: id = node ID ** Output: none ** Returns: index of node with given ID, or 0 if ID not found ** Purpose: uses hash table to find index of node with given ID **---------------------------------------------------------------- */ { return (hashtable_find(network->NodeHashTable, id)); } int findlink(Network *network, char *id) /*---------------------------------------------------------------- ** Input: id = link ID ** Output: none ** Returns: index of link with given ID, or 0 if ID not found ** Purpose: uses hash table to find index of link with given ID **---------------------------------------------------------------- */ { return (hashtable_find(network->LinkHashTable, id)); } int findtank(Network *network, int index) /*---------------------------------------------------------------- ** Input: index = node index ** Output: none ** Returns: index of tank with given node id, or NOTFOUND if tank not found ** Purpose: for use in the deletenode function **---------------------------------------------------------------- */ { int i; for (i = 1; i <= network->Ntanks; i++) { if (network->Tank[i].Node == index) return i; } return NOTFOUND; } int findpump(Network *network, int index) /*---------------------------------------------------------------- ** Input: index = link ID ** Output: none ** Returns: index of pump with given link id, or NOTFOUND if pump not found ** Purpose: for use in the deletelink function **---------------------------------------------------------------- */ { int i; for (i = 1; i <= network->Npumps; i++) { if (network->Pump[i].Link == index) return i; } return NOTFOUND; } int findvalve(Network *network, int index) /*---------------------------------------------------------------- ** Input: index = link ID ** Output: none ** Returns: index of valve with given link id, or NOTFOUND if valve not found ** Purpose: for use in the deletelink function **---------------------------------------------------------------- */ { int i; for (i = 1; i <= network->Nvalves; i++) { if (network->Valve[i].Link == index) return i; } return NOTFOUND; } int findpattern(Network *network, char *id) /*---------------------------------------------------------------- ** Input: id = time pattern ID ** Output: none ** Returns: time pattern index, or -1 if pattern not found ** Purpose: finds index of time pattern given its ID **---------------------------------------------------------------- */ { int i; // Don't forget to include the "dummy" pattern 0 in the search for (i = 0; i <= network->Npats; i++) { if (strcmp(id, network->Pattern[i].ID) == 0) return i; } return -1; } int findcurve(Network *network, char *id) /*---------------------------------------------------------------- ** Input: id = data curve ID ** Output: none ** Returns: data curve index, or 0 if curve not found ** Purpose: finds index of data curve given its ID **---------------------------------------------------------------- */ { int i; for (i = 1; i <= network->Ncurves; i++) { if (strcmp(id, network->Curve[i].ID) == 0) return i; } return 0; } void adjustpattern(int *pat, int index) /*---------------------------------------------------------------- ** Local function that modifies a reference to a deleted time pattern **---------------------------------------------------------------- */ { if (*pat == index) *pat = 0; else if (*pat > index) (*pat)--; } void adjustpatterns(Network *network, int index) /*---------------------------------------------------------------- ** Input: index = index of time pattern being deleted ** Output: none ** Purpose: modifies references made to a deleted time pattern **---------------------------------------------------------------- */ { int j; Pdemand demand; Psource source; // Adjust patterns used by junctions for (j = 1; j <= network->Nnodes; j++) { // Adjust demand patterns for (demand = network->Node[j].D; demand != NULL; demand = demand->next) { adjustpattern(&demand->Pat, index); } // Adjust WQ source patterns source = network->Node[j].S; if (source) adjustpattern(&source->Pat, index); } // Adjust patterns used by reservoir tanks for (j = 1; j <= network->Ntanks; j++) { adjustpattern(&network->Tank[j].Pat, index); } // Adjust patterns used by pumps for (j = 1; j <= network->Npumps; j++) { adjustpattern(&network->Pump[j].Upat, index); adjustpattern(&network->Pump[j].Epat, index); } } void adjustcurve(int *curve, int index) /*---------------------------------------------------------------- ** Local function that modifies a reference to a deleted data curve **---------------------------------------------------------------- */ { if (*curve == index) *curve = 0; else if (*curve > index) (*curve)--; } void adjustcurves(Network *network, int index) /*---------------------------------------------------------------- ** Input: index = index of data curve being deleted ** Output: none ** Purpose: modifies references made to a deleted data curve **---------------------------------------------------------------- */ { int j, k, setting; // Adjust tank volume curves for (j = 1; j <= network->Ntanks; j++) { adjustcurve(&network->Tank[j].Vcurve, index); } // Adjust pump curves for (j = 1; j <= network->Npumps; j++) { adjustcurve(&network->Pump[j].Hcurve, index); adjustcurve(&network->Pump[j].Ecurve, index); } // Adjust GPV curves for (j = 1; j <= network->Nvalves; j++) { k = network->Valve[j].Link; if (network->Link[k].Type == GPV) { setting = INT(network->Link[k].Kc); adjustcurve(&setting, index); network->Link[k].Kc = setting; } } } int adjustpumpparams(Project *pr, int curveIndex) /*---------------------------------------------------------------- ** Input: curveIndex = index of a data curve ** Output: returns an error code ** Purpose: updates head curve parameters for pumps using a ** curve whose data have been modified. **---------------------------------------------------------------- */ { Network *network = &pr->network; double *Ucf = pr->Ucf; int j, err = 0; Spump *pump; // Check each pump for (j = 1; j <= network->Npumps; j++) { // Pump uses curve as head curve pump = &network->Pump[j]; if ( curveIndex == pump->Hcurve) { // Update its head curve parameters pump->Ptype = NOCURVE; err = updatepumpparams(pr, curveIndex); if (err > 0) break; // Convert parameters to internal units if (pump->Ptype == POWER_FUNC) { pump->H0 /= Ucf[HEAD]; pump->R *= (pow(Ucf[FLOW], pump->N) / Ucf[HEAD]); } pump->Q0 /= Ucf[FLOW]; pump->Qmax /= Ucf[FLOW]; pump->Hmax /= Ucf[HEAD]; } } return err; } int resizecurve(Scurve *curve, int size) /*---------------------------------------------------------------- ** Input: curve = a data curve object ** size = desired number of curve data points ** Output: error code ** Purpose: resizes a data curve to a desired size **---------------------------------------------------------------- */ { double *x; double *y; if (curve->Capacity < size) { x = (double *)realloc(curve->X, size * sizeof(double)); if (x == NULL) return 101; y = (double *)realloc(curve->Y, size * sizeof(double)); if (y == NULL) { free(x); return 101; } curve->X = x; curve->Y = y; curve->Capacity = size; } return 0; } int getcomment(Network *network, int object, int index, char *comment) //---------------------------------------------------------------- // Input: object = a type of network object // index = index of the specified object // comment = the object's comment string // Output: error code // Purpose: gets the comment string assigned to an object. //---------------------------------------------------------------- { char *currentcomment; // Get pointer to specified object's comment switch (object) { case NODE: if (index < 1 || index > network->Nnodes) return 251; currentcomment = network->Node[index].Comment; break; case LINK: if (index < 1 || index > network->Nlinks) return 251; currentcomment = network->Link[index].Comment; break; case TIMEPAT: if (index < 1 || index > network->Npats) return 251; currentcomment = network->Pattern[index].Comment; break; case CURVE: if (index < 1 || index > network->Ncurves) return 251; currentcomment = network->Curve[index].Comment; break; default: strcpy(comment, ""); return 251; } // Copy the object's comment to the returned string if (currentcomment) strcpy(comment, currentcomment); else comment[0] = '\0'; return 0; } int setcomment(Network *network, int object, int index, const char *newcomment) //---------------------------------------------------------------- // Input: object = a type of network object // index = index of the specified object // newcomment = new comment string // Output: error code // Purpose: sets the comment string of an object. //---------------------------------------------------------------- { char *comment; switch (object) { case NODE: if (index < 1 || index > network->Nnodes) return 251; comment = network->Node[index].Comment; network->Node[index].Comment = xstrcpy(&comment, newcomment, MAXMSG); return 0; case LINK: if (index < 1 || index > network->Nlinks) return 251; comment = network->Link[index].Comment; network->Link[index].Comment = xstrcpy(&comment, newcomment, MAXMSG); return 0; case TIMEPAT: if (index < 1 || index > network->Npats) return 251; comment = network->Pattern[index].Comment; network->Pattern[index].Comment = xstrcpy(&comment, newcomment, MAXMSG); return 0; case CURVE: if (index < 1 || index > network->Ncurves) return 251; comment = network->Curve[index].Comment; network->Curve[index].Comment = xstrcpy(&comment, newcomment, MAXMSG); return 0; default: return 251; } } int namevalid(const char *name) //---------------------------------------------------------------- // Input: name = name used to ID an object // Output: returns TRUE if name is valid, FALSE if not // Purpose: checks that an object's ID name is valid. //---------------------------------------------------------------- { size_t n = strlen(name); if (n < 1 || n > MAXID || strpbrk(name, " ;") || name[0] == '"') return FALSE; return TRUE; } void getTmpName(char *fname) //---------------------------------------------------------------- // Input: fname = file name string // Output: an unused file name // Purpose: creates a temporary file name with an "en" prefix // or a blank name if an error occurs. //---------------------------------------------------------------- { #if W30 char* name = NULL; // --- use Windows _tempnam function to get a pointer to an // unused file name that begins with "en" strcpy(fname, ""); name = _tempnam(NULL, "en"); if (name) { // --- copy the file name to fname if (strlen(name) < MAXFNAME) strncpy(fname, name, MAXFNAME); // --- free the pointer returned by _tempnam free(name); } // --- for non-Windows systems: #else // --- use system function mkstemp() to create a temporary file name /* int f = -1; strcpy(fname, "enXXXXXX"); f = mkstemp(fname); close(f); remove(fname); */ char* name = NULL; // --- use Windows _tempnam function to get a pointer to an // unused file name that begins with "en" strcpy(fname, ""); name = _tempnam(NULL, "en"); if (name) { // --- copy the file name to fname if (strlen(name) < MAXFNAME) strncpy(fname, name, MAXFNAME); // --- free the pointer returned by _tempnam free(name); } /*strcpy(fname, "enXXXXXX"); FILE *f = fdopen(mkstemp(fname), "r"); if (f == NULL) strcpy(fname, ""); else fclose(f); remove(fname);*/ #endif } char *xstrcpy(char **s1, const char *s2, const size_t n) //---------------------------------------------------------------- // Input: s1 = destination string // s2 = source string // n = maximum size of strings // Output: none // Purpose: like strcpy except for dynamic strings. // Note: The calling program is responsible for ensuring that // s1 points to a valid memory location or is NULL. E.g., // the following code will likely cause a segment fault: // char *s; // s = xstrcpy(s, "Some text"); // while this would work correctly: // char *s = NULL; // s = xstrcpy(s, "Some text"); //---------------------------------------------------------------- { size_t n1 = 0, n2 = 0; // Find size of source string if (s2) n2 = strlen(s2); if (n2 > n) n2 = n; // Source string is empty -- free destination string if (n2 == 0) { free(*s1); *s1 = NULL; return NULL; } // See if size of destination string needs to grow if (*s1) n1 = strlen(*s1); if (n2 > n1) *s1 =(char*) realloc(*s1, (n2 + 1) * sizeof(char)); // Copy the source string into the destination string strncpy(*s1, s2, n2+1); return *s1; } int strcomp(const char *s1, const char *s2) /*--------------------------------------------------------------- ** Input: s1 = character string ** s2 = character string ** Output: none ** Returns: 1 if s1 is same as s2, 0 otherwise ** Purpose: case insensitive comparison of strings s1 & s2 **--------------------------------------------------------------- */ { int i; for (i = 0; UCHAR(s1[i]) == UCHAR(s2[i]); i++) { if (!s1[i + 1] && !s2[i + 1]) return 1; } return 0; } double interp(int n, double x[], double y[], double xx) /*---------------------------------------------------------------- ** Input: n = number of data pairs defining a curve ** x = x-data values of curve ** y = y-data values of curve ** xx = specified x-value ** Output: none ** Returns: y-value on curve at x = xx ** Purpose: uses linear interpolation to find y-value on a ** data curve corresponding to specified x-value. ** NOTE: does not extrapolate beyond endpoints of curve. **---------------------------------------------------------------- */ { int k, m; double dx, dy; m = n - 1; // Highest data index if (xx <= x[0]) return (y[0]); // xx off low end of curve for (k = 1; k <= m; k++) // Bracket xx on curve { if (x[k] >= xx) // Interp. over interval { dx = x[k] - x[k - 1]; dy = y[k] - y[k - 1]; if (ABS(dx) < TINY) return (y[k]); else return (y[k] - (x[k] - xx) * dy / dx); } } return (y[m]); // xx off high end of curve } char *geterrmsg(int errcode, char *msg) /*---------------------------------------------------------------- ** Input: errcode = error code ** Output: none ** Returns: pointer to string with error message ** Purpose: retrieves text of error message **---------------------------------------------------------------- */ { switch (errcode) { //#define DAT(code,string) case code: sprintf(msg, "%s", string); break; #define DAT(code,string) case code: strcpy(msg, string); break; #include "errors.dat" #undef DAT default: strcpy(msg, ""); } return (msg); } void errmsg(Project *pr, int errcode) /*---------------------------------------------------------------- ** Input: errcode = error code ** Output: none ** Purpose: writes error message to report file **---------------------------------------------------------------- */ { //°×ÔÆ·ÉÐÞ¸Ä return; char errmsg[MAXMSG + 1] = ""; if (errcode == 309) /* Report file write error - */ { /* Do not write msg to file. */ } else if (pr->report.RptFile != NULL && pr->report.Messageflag && errcode > 100) { sprintf(pr->Msg, "Error %d: %s", errcode, geterrmsg(errcode, errmsg)); writeline(pr, pr->Msg); } } void writewin(void(*vp)(char *), char *s) /*---------------------------------------------------------------- ** Input: text string ** Output: none ** Purpose: passes character string to viewprog() in ** application which calls the EPANET DLL **---------------------------------------------------------------- */ { //°×ÔÆ·ÉÐÞ¸Ä return; char progmsg[MAXMSG + 1]; if (vp != NULL) { strncpy(progmsg, s, MAXMSG); vp(progmsg); } }