From d582d9233b31527ef118c26b1b75c654869dd24a Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Wed, 15 May 2024 15:02:00 +0200 Subject: [PATCH] improve exception types for experimental feature Signed-off-by: Martijn Govers --- .../power_grid_model/common/exception.hpp | 40 ++++++++++------- .../power_grid_model_c/src/model.cpp | 13 +++--- src/power_grid_model/core/error_handling.py | 3 ++ src/power_grid_model/errors.py | 44 ++++++++++--------- 4 files changed, 59 insertions(+), 41 deletions(-) diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/common/exception.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/common/exception.hpp index d71fbb524..b9b3bb4c8 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/common/exception.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/common/exception.hpp @@ -21,11 +21,30 @@ class PowerGridError : public std::exception { std::string msg_; }; -template class MissingCaseForEnumError : public PowerGridError { +class InvalidArguments : public PowerGridError { public: - MissingCaseForEnumError(std::string const& method, const T& value) { - append_msg(method + " is not implemented for " + typeid(T).name() + " #" + std::to_string(IntS(value)) + "!\n"); + struct TypeValuePair { + std::string name; + std::string value; + }; + + template ... Options> + InvalidArguments(std::string const& method, std::string const& arguments) { + append_msg(method + " is not implemented for " + arguments + "!\n"); } + + template ... Options> + InvalidArguments(std::string const& method, Options... options) + : InvalidArguments{method, "the following combination of options"} { + (append_msg(" " + options.name + ": " + options.value + "\n"), ...); + } +}; + +class MissingCaseForEnumError : public InvalidArguments { + public: + template + MissingCaseForEnumError(std::string const& method, const T& value) + : InvalidArguments{method, std::string{typeid(T).name()} + " #" + std::to_string(static_cast(value))} {} }; class ConflictVoltage : public PowerGridError { @@ -193,20 +212,9 @@ class DatasetError : public PowerGridError { explicit DatasetError(std::string const& msg) { append_msg("Dataset error: " + msg); } }; -class ExperimentalFeature : public PowerGridError { +class ExperimentalFeature : public InvalidArguments { public: - struct TypeValuePair { - std::string name; - std::string value; - }; - - template ... Options> ExperimentalFeature(Options... options) { - append_msg("The following combination of options is experimental:"); - - (append_msg("\n " + options.name + ": " + options.value), ...); - - append_msg("\n Please enable experimental features if you wish to use them.\n"); - } + using InvalidArguments::InvalidArguments; }; class UnreachableHit : public PowerGridError { diff --git a/power_grid_model_c/power_grid_model_c/src/model.cpp b/power_grid_model_c/power_grid_model_c/src/model.cpp index 2bf5f4abe..e268aa2b6 100644 --- a/power_grid_model_c/power_grid_model_c/src/model.cpp +++ b/power_grid_model_c/power_grid_model_c/src/model.cpp @@ -60,7 +60,7 @@ void PGM_get_indexer(PGM_Handle* handle, PGM_PowerGridModel const* model, char c } namespace { -void check_experimental_features(PGM_Options const& opt) { +void check_calculate_experimental_features(PGM_Options const& opt) { using namespace std::string_literals; if (opt.calculation_type == PGM_power_flow) { @@ -70,6 +70,7 @@ void check_experimental_features(PGM_Options const& opt) { case PGM_tap_changing_strategy_min_voltage_tap: { // this option is experimental and should not be exposed to the user throw ExperimentalFeature{ + "PGM_calculate", ExperimentalFeature::TypeValuePair{.name = "PGM_CalculationType", .value = std::to_string(opt.calculation_type)}, ExperimentalFeature::TypeValuePair{.name = "PGM_TapChangingStrategy", @@ -81,14 +82,16 @@ void check_experimental_features(PGM_Options const& opt) { } } -void check_valid_options(PGM_Options const& opt) { +void check_calculate_valid_options(PGM_Options const& opt) { if (opt.tap_changing_strategy != PGM_tap_changing_strategy_disabled && opt.calculation_type != PGM_power_flow) { // illegal combination of options - throw MissingCaseForEnumError{"PGM_TapChangingStrategy", opt.tap_changing_strategy}; + throw InvalidArguments{"PGM_calculate", + InvalidArguments::TypeValuePair{.name = "PGM_TapChangingStrategy", + .value = std::to_string(opt.tap_changing_strategy)}}; } if (opt.experimental_features == PGM_experimental_features_disabled) { - check_experimental_features(opt); + check_calculate_experimental_features(opt); } } } // namespace @@ -110,7 +113,7 @@ void PGM_calculate(PGM_Handle* handle, PGM_PowerGridModel* model, PGM_Options co // call calculation try { - check_valid_options(*opt); + check_calculate_valid_options(*opt); // TODO(mgovers): changing this to narrow_cast is a breaking change auto const calculation_method = static_cast(opt->calculation_method); diff --git a/src/power_grid_model/core/error_handling.py b/src/power_grid_model/core/error_handling.py index 8f7016483..de2b2b37c 100644 --- a/src/power_grid_model/core/error_handling.py +++ b/src/power_grid_model/core/error_handling.py @@ -19,6 +19,7 @@ ConflictVoltage, IDNotFound, IDWrongType, + InvalidArguments, InvalidBranch, InvalidBranch3, InvalidCalculationMethod, @@ -45,6 +46,7 @@ PGM_SERIALIZATION_ERROR = 3 _MISSING_CASE_FOR_ENUM_RE = re.compile(r"(\w+) is not implemented for (\w+) #(-?\d+)!\n") +_INVALID_ARGUMENTS_RE = re.compile(r"(\w+) is not implemented for") # multiple different flavors _CONFLICT_VOLTAGE_RE = re.compile( r"Conflicting voltage for line (-?\d+)\n voltage at from node (-?\d+) is (.*)\n" r" voltage at to node (-?\d+) is (.*)\n" @@ -75,6 +77,7 @@ _ERROR_MESSAGE_PATTERNS = { _MISSING_CASE_FOR_ENUM_RE: MissingCaseForEnumError, + _INVALID_ARGUMENTS_RE: InvalidArguments, _CONFLICT_VOLTAGE_RE: ConflictVoltage, _INVALID_BRANCH_RE: InvalidBranch, _INVALID_BRANCH3_RE: InvalidBranch3, diff --git a/src/power_grid_model/errors.py b/src/power_grid_model/errors.py index 4243c66b2..d4e6be8c5 100644 --- a/src/power_grid_model/errors.py +++ b/src/power_grid_model/errors.py @@ -12,11 +12,11 @@ class PowerGridError(RuntimeError): - """Generic power grid error""" + """Generic power grid error.""" class PowerGridBatchError(PowerGridError): - """Error occurs in batch calculation""" + """Error occurs in batch calculation.""" failed_scenarios: np.ndarray succeeded_scenarios: np.ndarray @@ -24,7 +24,11 @@ class PowerGridBatchError(PowerGridError): errors: List[PowerGridError] -class MissingCaseForEnumError(PowerGridError): +class InvalidArguments(PowerGridError): + """A (combination of) input arguments is not valid.""" + + +class MissingCaseForEnumError(InvalidArguments): """An enum value is not covered in a for loop. This usually happens when an invalid combination of (enum) settings is provided.""" @@ -35,55 +39,55 @@ class ConflictVoltage(PowerGridError): class InvalidBranch(PowerGridError): - """A branch is invalid""" + """A branch is invalid.""" class InvalidBranch3(PowerGridError): - """A branch3 is invalid""" + """A branch3 is invalid.""" class InvalidTransformerClock(PowerGridError): - """Invalid transformer clock found""" + """Invalid transformer clock found.""" class SparseMatrixError(PowerGridError): - """Attempting to invert a non-invertible matrix""" + """Attempting to invert a non-invertible matrix.""" class NotObservableError(SparseMatrixError): - """Attempting to solve a non-observable system""" + """Attempting to solve a non-observable system.""" class IterationDiverge(PowerGridError): - """Unable to iteratively converge to an optimum within the set number of iterations and precision""" + """Unable to iteratively converge to an optimum within the set number of iterations and precision.""" class InvalidID(PowerGridError): - """An ID is invalid""" + """An ID is invalid.""" class ConflictID(InvalidID): - """Conflicting IDs found""" + """Conflicting IDs found.""" class IDNotFound(InvalidID): - """A reference to a non-existent ID was provided""" + """A reference to a non-existent ID was provided.""" class InvalidMeasuredObject(InvalidID): - """A provided measured object is invalid""" + """A provided measured object is invalid.""" class InvalidRegulatedObject(InvalidID): - """A provided regulated object is invalid""" + """A provided regulated object is invalid.""" class IDWrongType(InvalidID): - """A referenced ID points to a component that cannot be referenced here""" + """A referenced ID points to a component that cannot be referenced here.""" class InvalidCalculationMethod(PowerGridError): - """Invalid calculation method provided""" + """Invalid calculation method provided.""" class AutomaticTapCalculationError(PowerGridError): @@ -91,18 +95,18 @@ class AutomaticTapCalculationError(PowerGridError): class InvalidShortCircuitPhaseOrType(PowerGridError): - """Invalid (combination of) short circuit types and phase(s) provided""" + """Invalid (combination of) short circuit types and phase(s) provided.""" class PowerGridSerializationError(PowerGridError): - """Error occurs during (de-)serialization""" + """Error occurs during (de-)serialization.""" class PowerGridDatasetError(PowerGridError): - """Error occurs during dataset handling""" + """Error occurs during dataset handling.""" class PowerGridUnreachableHitError(PowerGridError): - """Supposedly unreachable code was hit + """Supposedly unreachable code was hit. This usually means a failed assumption and may be caused by a bug in the PGM library."""