Stormwater-Management-Model icon indicating copy to clipboard operation
Stormwater-Management-Model copied to clipboard

add storage node get/set (Setting functional curve coefficient)

Open bemcdonnell opened this issue 7 years ago • 13 comments

Issue Carried over from PySWMM: (https://github.com/OpenWaterAnalytics/pyswmm/issues/167)

@joaolponciano, let's discuss the implementation over here. Once the API is opened, it is easy to interface pyswmm with it.

bemcdonnell avatar Jul 24 '18 18:07 bemcdonnell

Awesome. So I was think how to do it, and I thought to take the parser used for get/set nodes parameters and modify it to fit into the storage parameters. Do you think this could work?

joaolponciano avatar Jul 24 '18 18:07 joaolponciano

The EPANET API has curve editing capabilities. Then the curve gets associated with a model element. This way all the different curve types are editable through the API.

So I would like to see more general curve editing capabilities implemented rather then something specific just for storage nodes. Make sense?

michaeltryby avatar Jul 25 '18 13:07 michaeltryby

A storage node or junction in SWMM5 can be a function with three parameter, AREA - A + B^C or a Storage Curve. Don't you need two API's : one for the Function and one for the Storage Curve (general API for Curves)

dickinsonre avatar Jul 25 '18 14:07 dickinsonre

@joaolponciano, I guess that I'm envisioning a few get/set functions for storage nodes.

For starters, specific to storage nodes the parser collects and populates the following properties pertaining to Storage Nodes:

int storage_readParams(int j, int k, char* tok[], int ntoks)
//
//  Input:   j = node index
//           k = storage unit index
//           tok[] = array of string tokens
//           ntoks = number of tokens
//  Output:  returns an error message
//  Purpose: reads a storage unit's properties from a tokenized line of input.
//
//  Format of input line is:
//     nodeID  elev  maxDepth  initDepth  FUNCTIONAL a1 a2 a0 aPond fEvap (infil)
//     nodeID  elev  maxDepth  initDepth  TABULAR    curveID  aPond fEvap (infil)
//

I envision the following functions available for a Node-Storage Object. The other properties such as elev, maxDepth, and initDepth shoudl already be available through swmm_[get/set]NodeParam()

swmm_getStorageCurve(int index, int *curve_index)
{
//output index of curve
}

swmm_setStorageCurve(int index, int curve_index)
{
//sets curve to TABULAR (if not already) and pushes index
// need validation
}

swmm_getStorageFunc(int index, double *a1, double *a2, double *a3)
{
//get function params
}

swmm_setStorageFunc(int index, double a1, double a2, double a3)
{
 //sets curve to FUNCTIONAL (if not already) and pushes parameters
// need validation
}

swmm_getStorageExfil(int index, int *rate)
{
//get constant exfiltration rate (not the same as current exfil rate)
}

swmm_setStorageExfil(int index, int rate)
{
//set Storage Exfil
// may need validation
}

swmm_getStorageEvap(int index, int *rate)
{
//get constant evap rate (not the same as current evap rate)
}

swmm_setStorageEvap(int index, int rate)
{
//set evap rate
// may need validation
}

Why don't you study swmm_[get/set]NodeParams() in toolkitAPI.c and see if you can come up with some strategies to extract these data through the functions described above.

bemcdonnell avatar Jul 25 '18 14:07 bemcdonnell

We could make the api more compact and functional by handling multiple properties with a single api call.

michaeltryby avatar Jul 25 '18 15:07 michaeltryby

@bemcdonnell, I've tried doing something similar to what is done for the nodes, but I'm not so sure if I have to implement the function curve equations again (and how to implement something about the tabular curve) and I have some doubts about the evaporation and exfil units. I thought something like that:

int DLLEXPORT swmm_getStorageParam(int index, int Param, double *value) // // Input: index = Index of desired ID // param = Parameter desired (Based on enum SM_StorageProperty) // Output: value = value to be output // Return: API Error // Purpose: Gets Storage Parameter { int errcode = 0; *value = 0; // Check if Open if(swmm_IsOpenFlag() == FALSE) { errcode = ERR_API_INPUTNOTOPEN; } // Check if object index is within bounds else if (index < 0 || index >= Nobjects[Storage]) { errcode = ERR_API_OBJECT_INDEX; } else { switch(Param) { case SM_STORAGECURVE: *value = Storage[index].aCurve * UCF(VOLUME); break; case SM_STORAGEFUNC: *value = Storage[index].functional * UCF(VOLUME); break; case SM_STORAGECOEFFICIENT: *value = Storage[index].aCoeff * UCF(LENGTH) * UCF(LENGTH); break; case SM_STORAGEEXFIL: *value = Storage[index].exfil * UCF(FLOW); break; case SM_STORAGEEVAP: *value = Storage[index].fEvap * UCF(EVAPRATE); break; default: errcode = ERR_API_OUTBOUNDS; break; } } return(errcode); }

// Set Storage Parameters

int DLLEXPORT swmm_setStorage Param(int index, int Param, double value) // // Input: index = Index of desired ID // param = Parameter desired (Based on enum SM_StorageProperty) // value = value to be input // Return: API Error // Purpose: Sets Storage Parameter { int errcode = 0; // Check if Open if(swmm_IsOpenFlag() == FALSE) { errcode = ERR_API_INPUTNOTOPEN; } // Check if Simulation is Running else if(swmm_IsStartedFlag() == TRUE) { errcode = ERR_API_SIM_NRUNNING; } // Check if object index is within bounds else if (index < 0 || index >= Nobjects[STORAGE]) { errcode = ERR_API_OBJECT_INDEX; } else { switch(Param) { case SM_STORAGECURVE: *value = Storage[index].aCurve * UCF(VOLUME); break; case SM_STORAGEFUNC: *value = Storage[index].functional * UCF(VOLUME); break; case SM_STORAGECOEFFICIENT: *value = Storage[index].aCoeff * UCF(LENGTH) * UCF(LENGTH); break; case SM_STORAGEEXFIL: *value = Storage[index].exfil * UCF(FLOW); break; case SM_STORAGEEVAP: *value = Storage[index].fEvap * UCF(EVAPRATE); break; default: errcode = ERR_API_OUTBOUNDS; break; } } // Need to re-validate //storage_validate(index) return(errcode); }

Thanks for your help and attention.

joaolponciano avatar Jul 27 '18 19:07 joaolponciano

@joaolponciano, go ahead and issue a pull request and we can discuss the implementation there. "Issues" are normally to discuss concepts.

bemcdonnell avatar Jul 27 '18 20:07 bemcdonnell

I have a question, is this all to model changing areas in a storage node over a period of years? I like the idea but if we had time series tied to all SWMM5 parameters then it might be easier to do this.

dickinsonre avatar Jul 27 '18 21:07 dickinsonre

@dickinsonre, it appears in the code block that this API function will be shut off during simulation time. Which I think is good. I would worry about creating or destroying mass if the node area were to change over time.

bemcdonnell avatar Jul 27 '18 22:07 bemcdonnell

1, A workaround would be to test for th node depth, if the storage node was dry and the pond increased in area there would not be any mass balance issues in a continuous simulation.

  1. Mass balance happens already in a ponded node, the volume and depth change all of the time but no worries as the volume is conserved for the next time.

dickinsonre avatar Jul 27 '18 22:07 dickinsonre

@bemcdonnell, I created a pull and insert that parser there.

joaolponciano avatar Jul 28 '18 22:07 joaolponciano

@joaolponciano, it looks like your pointed your PR to USEPA. Please point it to OpenWaterAnalytics/Stormwater-Management-Model ‘dev’ branch

bemcdonnell avatar Jul 28 '18 22:07 bemcdonnell

Sorry, I did not pay attention to this when I submitted the pull, I think it's now in the right place.

joaolponciano avatar Jul 30 '18 13:07 joaolponciano