From 19c0d314ba9dd164f77b3b34977a00729b3d53d1 Mon Sep 17 00:00:00 2001 From: Abel Heinsbroek Date: Wed, 29 Oct 2025 11:24:32 +0100 Subject: [PATCH 1/9] add ENgeterrornode method to retrieve the node index of the node causing ill-conditioned error --- include/epanet2.h | 2 ++ include/epanet2_2.h | 10 ++++++++++ src/epanet.c | 10 ++++++++++ src/epanet2.c | 5 +++++ src/errors.dat | 1 + src/hydsolver.c | 5 +++++ src/types.h | 4 +++- 7 files changed, 36 insertions(+), 1 deletion(-) diff --git a/include/epanet2.h b/include/epanet2.h index 042d6c6a..4f36a25c 100644 --- a/include/epanet2.h +++ b/include/epanet2.h @@ -457,6 +457,8 @@ extern "C" { int DLLEXPORT ENsetruleenabled(int index, int enabled); + int DLLEXPORT ENgeterrornode(int *errorNode); + #if defined(__cplusplus) } #endif diff --git a/include/epanet2_2.h b/include/epanet2_2.h index eca50957..c6121926 100644 --- a/include/epanet2_2.h +++ b/include/epanet2_2.h @@ -413,6 +413,15 @@ typedef struct Project *EN_Project; */ int DLLEXPORT EN_closeH(EN_Project ph); + + /** + @brief Retrieves the index of the node giving an ill-conditioned error + @param ph an EPANET project handle. + @param + @return an error code. + */ + int DLLEXPORT EN_geterrornode(EN_Project ph, int *errorNode); + /*=================================================================== Water Quality Analysis Functions @@ -1953,6 +1962,7 @@ typedef struct Project *EN_Project; */ int DLLEXPORT EN_setruleenabled(EN_Project ph, int index, int enabled); + #if defined(__cplusplus) } #endif diff --git a/src/epanet.c b/src/epanet.c index 7714ae47..0231096c 100644 --- a/src/epanet.c +++ b/src/epanet.c @@ -662,6 +662,15 @@ int DLLEXPORT EN_usehydfile(EN_Project p, const char *filename) return errcode; } +int DLLEXPORT EN_geterrornode(EN_Project p, int *errorNode) +{ + if(!p->Openflag) return 102; + if(!p->hydraul.IsIllConditioned) return 265; + + *errorNode = p->hydraul.ErrNode; + return 0; +} + /******************************************************************** Water Quality Analysis Functions @@ -5941,3 +5950,4 @@ int DLLEXPORT EN_setruleenabled(EN_Project p, int index, int enabled) } + diff --git a/src/epanet2.c b/src/epanet2.c index 2bbc75fe..8308b11f 100644 --- a/src/epanet2.c +++ b/src/epanet2.c @@ -959,3 +959,8 @@ int DLLEXPORT ENsetruleenabled(int index, int enabled) { return EN_setruleenabled(_defaultProject, index, enabled); } + +int DLLEXPORT ENgeterrornode(int *errorNode) +{ + return EN_geterrornode(_defaultProject, errorNode); +} diff --git a/src/errors.dat b/src/errors.dat index 65b9519b..d7b1229a 100644 --- a/src/errors.dat +++ b/src/errors.dat @@ -66,6 +66,7 @@ DAT(261,"attempt to delete a node or link contained in a control") DAT(262,"attempt to modify network structure while solver is active") DAT(263,"node is not a tank") DAT(264,"link is not a valve") +DAT(265,"network is not ill-conditioned") // File errors DAT(301,"identical file names") diff --git a/src/hydsolver.c b/src/hydsolver.c index e5969bc9..25e07672 100644 --- a/src/hydsolver.c +++ b/src/hydsolver.c @@ -105,6 +105,9 @@ int hydsolve(Project *pr, int *iter, double *relerr) hyd->DeficientNodes = 0; hyd->DemandReduction = 0.0; + // initialize ill conditioned status + hyd->IsIllConditioned = FALSE; + // Repeat iterations until convergence or trial limit is exceeded. // (ExtraIter used to increase trials in case of status cycling.) if (rpt->Statflag == FULL) writerelerr(pr, 0, 0); @@ -191,6 +194,8 @@ int hydsolve(Project *pr, int *iter, double *relerr) // Iterations ended - report any errors. if (errcode > 0) { + hyd->IsIllConditioned = TRUE; + hyd->ErrNode = sm->Order[errcode]; // store error in project for retrieval later writehyderr(pr, sm->Order[errcode]); // Ill-conditioned matrix error errcode = 110; } diff --git a/src/types.h b/src/types.h index 8c2bfcd7..98df2a8e 100755 --- a/src/types.h +++ b/src/types.h @@ -793,7 +793,9 @@ typedef struct { OpenHflag, // Hydraulic system opened flag Haltflag, // Flag to halt simulation DeficientNodes, // Number of pressure deficient nodes - HasLeakage; // TRUE if project has non-zero leakage parameters + HasLeakage, // TRUE if project has non-zero leakage parameters + IsIllConditioned, // TRUE if project is ill-conditioned + ErrNode; // Node causing ill-conditioned network error Sleakage *Leakage; // Array of node leakage parameters From 1404ea05899f6ec5b9be75b343d4583d16420b1e Mon Sep 17 00:00:00 2001 From: Abel Heinsbroek Date: Wed, 29 Oct 2025 11:34:27 +0100 Subject: [PATCH 2/9] add to EN_getstatistic instead of separate function --- include/epanet2.h | 2 -- include/epanet2_2.h | 9 --------- include/epanet2_enums.h | 3 ++- src/epanet.c | 15 ++++++--------- src/epanet2.c | 7 +------ src/hydsolver.c | 2 +- 6 files changed, 10 insertions(+), 28 deletions(-) diff --git a/include/epanet2.h b/include/epanet2.h index 4f36a25c..042d6c6a 100644 --- a/include/epanet2.h +++ b/include/epanet2.h @@ -457,8 +457,6 @@ extern "C" { int DLLEXPORT ENsetruleenabled(int index, int enabled); - int DLLEXPORT ENgeterrornode(int *errorNode); - #if defined(__cplusplus) } #endif diff --git a/include/epanet2_2.h b/include/epanet2_2.h index c6121926..b7c1b89b 100644 --- a/include/epanet2_2.h +++ b/include/epanet2_2.h @@ -413,15 +413,6 @@ typedef struct Project *EN_Project; */ int DLLEXPORT EN_closeH(EN_Project ph); - - /** - @brief Retrieves the index of the node giving an ill-conditioned error - @param ph an EPANET project handle. - @param - @return an error code. - */ - int DLLEXPORT EN_geterrornode(EN_Project ph, int *errorNode); - /*=================================================================== Water Quality Analysis Functions diff --git a/include/epanet2_enums.h b/include/epanet2_enums.h index 2da2e688..b1a9153e 100644 --- a/include/epanet2_enums.h +++ b/include/epanet2_enums.h @@ -158,7 +158,8 @@ typedef enum { EN_MASSBALANCE = 4, //!< Cumulative water quality mass balance ratio EN_DEFICIENTNODES = 5, //!< Number of pressure deficient nodes EN_DEMANDREDUCTION = 6, //!< % demand reduction at pressure deficient nodes - EN_LEAKAGELOSS = 7 //!< % flow lost to system leakage + EN_LEAKAGELOSS = 7, //!< % flow lost to system leakage + EN_ERRORNODE = 8 //!< Index of node causing ill-conditioned error } EN_AnalysisStatistic; /// Types of network objects diff --git a/src/epanet.c b/src/epanet.c index 0231096c..a4b07b11 100644 --- a/src/epanet.c +++ b/src/epanet.c @@ -662,15 +662,6 @@ int DLLEXPORT EN_usehydfile(EN_Project p, const char *filename) return errcode; } -int DLLEXPORT EN_geterrornode(EN_Project p, int *errorNode) -{ - if(!p->Openflag) return 102; - if(!p->hydraul.IsIllConditioned) return 265; - - *errorNode = p->hydraul.ErrNode; - return 0; -} - /******************************************************************** Water Quality Analysis Functions @@ -1107,6 +1098,12 @@ int DLLEXPORT EN_getstatistic(EN_Project p, int type, double *value) case EN_MASSBALANCE: *value = p->quality.MassBalance.ratio; break; + case EN_ERRORNODE: + // check if hydraulic solver is ill conditioned + if(!p->hydraul.IsIllConditioned) return 265; + *value = p->hydraul.ErrNode; + break; + default: *value = 0.0; return 251; diff --git a/src/epanet2.c b/src/epanet2.c index 8308b11f..7d55f384 100644 --- a/src/epanet2.c +++ b/src/epanet2.c @@ -958,9 +958,4 @@ int DLLEXPORT ENgetruleenabled(int index, int *out_enabled) int DLLEXPORT ENsetruleenabled(int index, int enabled) { return EN_setruleenabled(_defaultProject, index, enabled); -} - -int DLLEXPORT ENgeterrornode(int *errorNode) -{ - return EN_geterrornode(_defaultProject, errorNode); -} +} \ No newline at end of file diff --git a/src/hydsolver.c b/src/hydsolver.c index 25e07672..49b93bdd 100644 --- a/src/hydsolver.c +++ b/src/hydsolver.c @@ -195,7 +195,7 @@ int hydsolve(Project *pr, int *iter, double *relerr) if (errcode > 0) { hyd->IsIllConditioned = TRUE; - hyd->ErrNode = sm->Order[errcode]; // store error in project for retrieval later + hyd->ErrNode = sm->Order[errcode]; // Store node causing ill-conditioned error writehyderr(pr, sm->Order[errcode]); // Ill-conditioned matrix error errcode = 110; } From 1627aed67a576f0680d4798bfbedc4e85f2f2c7a Mon Sep 17 00:00:00 2001 From: Abel Heinsbroek Date: Wed, 29 Oct 2025 11:51:33 +0100 Subject: [PATCH 3/9] fix tests --- tests/test_report.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_report.cpp b/tests/test_report.cpp index b026f1f7..fdc603f4 100644 --- a/tests/test_report.cpp +++ b/tests/test_report.cpp @@ -42,7 +42,7 @@ BOOST_FIXTURE_TEST_CASE(test_rprt_anlysstats, FixtureOpenClose) BOOST_CHECK(check_cdd_double(test, ref, 3)); double temp; - error = EN_getstatistic(ph, 8, &temp); + error = EN_getstatistic(ph, 9, &temp); BOOST_CHECK(error == 251); } From 16cb09aa75eae7c88925e766c00f2159a413a492 Mon Sep 17 00:00:00 2001 From: Abel Heinsbroek Date: Thu, 30 Oct 2025 07:14:19 +0100 Subject: [PATCH 4/9] remove custom error, isillconditioned, initialize errnode variable --- src/epanet.c | 4 +++- src/errors.dat | 1 - src/hydsolver.c | 5 ++--- src/types.h | 1 - 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/epanet.c b/src/epanet.c index a4b07b11..a2411719 100644 --- a/src/epanet.c +++ b/src/epanet.c @@ -1100,7 +1100,9 @@ int DLLEXPORT EN_getstatistic(EN_Project p, int type, double *value) break; case EN_ERRORNODE: // check if hydraulic solver is ill conditioned - if(!p->hydraul.IsIllConditioned) return 265; + if(p->hydraul.ErrNode == 0) { + return 203; + } *value = p->hydraul.ErrNode; break; diff --git a/src/errors.dat b/src/errors.dat index d7b1229a..65b9519b 100644 --- a/src/errors.dat +++ b/src/errors.dat @@ -66,7 +66,6 @@ DAT(261,"attempt to delete a node or link contained in a control") DAT(262,"attempt to modify network structure while solver is active") DAT(263,"node is not a tank") DAT(264,"link is not a valve") -DAT(265,"network is not ill-conditioned") // File errors DAT(301,"identical file names") diff --git a/src/hydsolver.c b/src/hydsolver.c index 49b93bdd..52a74d8e 100644 --- a/src/hydsolver.c +++ b/src/hydsolver.c @@ -105,8 +105,8 @@ int hydsolve(Project *pr, int *iter, double *relerr) hyd->DeficientNodes = 0; hyd->DemandReduction = 0.0; - // initialize ill conditioned status - hyd->IsIllConditioned = FALSE; + // initialize error node + hyd->ErrNode = 0; // Repeat iterations until convergence or trial limit is exceeded. // (ExtraIter used to increase trials in case of status cycling.) @@ -194,7 +194,6 @@ int hydsolve(Project *pr, int *iter, double *relerr) // Iterations ended - report any errors. if (errcode > 0) { - hyd->IsIllConditioned = TRUE; hyd->ErrNode = sm->Order[errcode]; // Store node causing ill-conditioned error writehyderr(pr, sm->Order[errcode]); // Ill-conditioned matrix error errcode = 110; diff --git a/src/types.h b/src/types.h index 98df2a8e..68f4ebfb 100755 --- a/src/types.h +++ b/src/types.h @@ -794,7 +794,6 @@ typedef struct { Haltflag, // Flag to halt simulation DeficientNodes, // Number of pressure deficient nodes HasLeakage, // TRUE if project has non-zero leakage parameters - IsIllConditioned, // TRUE if project is ill-conditioned ErrNode; // Node causing ill-conditioned network error Sleakage *Leakage; // Array of node leakage parameters From b0fc769374cf69c97bd8ba3713b0dfcc7063ce73 Mon Sep 17 00:00:00 2001 From: Abel Heinsbroek Date: Mon, 3 Nov 2025 14:48:10 +0100 Subject: [PATCH 5/9] remove node not found error --- src/epanet.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/epanet.c b/src/epanet.c index a2411719..f7209354 100644 --- a/src/epanet.c +++ b/src/epanet.c @@ -1099,10 +1099,6 @@ int DLLEXPORT EN_getstatistic(EN_Project p, int type, double *value) *value = p->quality.MassBalance.ratio; break; case EN_ERRORNODE: - // check if hydraulic solver is ill conditioned - if(p->hydraul.ErrNode == 0) { - return 203; - } *value = p->hydraul.ErrNode; break; From 6fac98af86c452548965cac3638829cb8ce57cc9 Mon Sep 17 00:00:00 2001 From: Abel Heinsbroek Date: Tue, 2 Dec 2025 14:59:09 +0100 Subject: [PATCH 6/9] include dll as appveyor artifact --- appveyor.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/appveyor.yml b/appveyor.yml index dd74d643..1c26dcbb 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -77,6 +77,8 @@ test_script: on_success: - cd %TEST_HOME%\benchmark - appveyor PushArtifact receipt.json + - cd %BUILD_HOME\lib + - appveyor PushArtifact epanet2.dll on_failure: - cd %TEST_HOME%\benchmark From 7b4224b7a58323a53625aa535c969b1cac05d275 Mon Sep 17 00:00:00 2001 From: Abel Heinsbroek Date: Tue, 2 Dec 2025 15:13:42 +0100 Subject: [PATCH 7/9] include dll as appveyor artifact - fix --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 1c26dcbb..e0dacd4b 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -77,7 +77,7 @@ test_script: on_success: - cd %TEST_HOME%\benchmark - appveyor PushArtifact receipt.json - - cd %BUILD_HOME\lib + - cd %BUILD_HOME%\lib - appveyor PushArtifact epanet2.dll on_failure: From fbac9692ddf63f2b17c1d693af4228848fbea592 Mon Sep 17 00:00:00 2001 From: Abel Heinsbroek Date: Tue, 2 Dec 2025 15:17:02 +0100 Subject: [PATCH 8/9] include dll as appveyor artifact - fix2 --- appveyor.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/appveyor.yml b/appveyor.yml index e0dacd4b..309f5655 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -77,6 +77,7 @@ test_script: on_success: - cd %TEST_HOME%\benchmark - appveyor PushArtifact receipt.json + - cd %EPANET_HOME% - cd %BUILD_HOME%\lib - appveyor PushArtifact epanet2.dll From 126516d0d4b6c664bb424d876d0f7f99ee3e25f8 Mon Sep 17 00:00:00 2001 From: Abel Heinsbroek Date: Tue, 2 Dec 2025 15:32:14 +0100 Subject: [PATCH 9/9] include dll as appveyor artifact - fix3 --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 309f5655..893c89d7 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -78,7 +78,7 @@ on_success: - cd %TEST_HOME%\benchmark - appveyor PushArtifact receipt.json - cd %EPANET_HOME% - - cd %BUILD_HOME%\lib + - cd %BUILD_HOME%\bin\Release - appveyor PushArtifact epanet2.dll on_failure: