Skip to content

Commit

Permalink
improve exception types for experimental feature
Browse files Browse the repository at this point in the history
Signed-off-by: Martijn Govers <Martijn.Govers@Alliander.com>
  • Loading branch information
mgovers committed May 15, 2024
1 parent 135a8d3 commit d582d92
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 41 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,30 @@ class PowerGridError : public std::exception {
std::string msg_;
};

template <typename T> 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 <std::same_as<TypeValuePair>... Options>
InvalidArguments(std::string const& method, std::string const& arguments) {
append_msg(method + " is not implemented for " + arguments + "!\n");
}

template <std::same_as<TypeValuePair>... 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 <typename T>
MissingCaseForEnumError(std::string const& method, const T& value)
: InvalidArguments{method, std::string{typeid(T).name()} + " #" + std::to_string(static_cast<IntS>(value))} {}
};

class ConflictVoltage : public PowerGridError {
Expand Down Expand Up @@ -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 <std::same_as<TypeValuePair>... 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 {
Expand Down
13 changes: 8 additions & 5 deletions power_grid_model_c/power_grid_model_c/src/model.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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",
Expand All @@ -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
Expand All @@ -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<CalculationMethod>(opt->calculation_method);
Expand Down
3 changes: 3 additions & 0 deletions src/power_grid_model/core/error_handling.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
ConflictVoltage,
IDNotFound,
IDWrongType,
InvalidArguments,
InvalidBranch,
InvalidBranch3,
InvalidCalculationMethod,
Expand All @@ -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"
Expand Down Expand Up @@ -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,
Expand Down
44 changes: 24 additions & 20 deletions src/power_grid_model/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,23 @@


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
error_messages: List[str]
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."""
Expand All @@ -35,74 +39,74 @@ 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):
"""Automatic tap changer with tap at LV side is unsupported for automatic tap changing calculation."""


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."""

0 comments on commit d582d92

Please sign in to comment.