Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature / Binary search in Automatic Tap Changer #668

Merged
merged 59 commits into from
Aug 6, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
699f8a9
branch init
Jerry-Jinfeng-Guo Jul 9, 2024
29ee28d
Merge branch 'main' into feature/binary-search-tap-changer
Jerry-Jinfeng-Guo Jul 9, 2024
d6e362f
Merge branch 'main' into feature/binary-search-tap-changer
Jerry-Jinfeng-Guo Jul 11, 2024
1e81cd4
added `fast_any`
Jerry-Jinfeng-Guo Jul 11, 2024
2608b23
[skip ci] intermediate commit
Jerry-Jinfeng-Guo Jul 11, 2024
4d0b142
Merge branch 'main' into feature/binary-search-tap-changer
Jerry-Jinfeng-Guo Jul 11, 2024
8b942fb
[skip ci] added binary search members
Jerry-Jinfeng-Guo Jul 12, 2024
623f6a4
minor clean
Jerry-Jinfeng-Guo Jul 13, 2024
69dc30f
Merge branch 'main' into feature/binary-search-tap-changer
Jerry-Jinfeng-Guo Jul 22, 2024
51392aa
basic logic in place
Jerry-Jinfeng-Guo Jul 22, 2024
e8a5315
Merge branch 'main' into feature/binary-search-tap-changer
Jerry-Jinfeng-Guo Jul 22, 2024
096ab0f
clean up
Jerry-Jinfeng-Guo Jul 22, 2024
cd59565
compiler complains
Jerry-Jinfeng-Guo Jul 22, 2024
90e6b1d
Merge branch 'main' into feature/binary-search-tap-changer
Jerry-Jinfeng-Guo Jul 23, 2024
bed2dff
[skip ci] binary search logic moved away from wrapper; debugging
Jerry-Jinfeng-Guo Jul 23, 2024
5353991
Merge branch 'main' into feature/binary-search-tap-changer
Jerry-Jinfeng-Guo Jul 23, 2024
6472cd1
1 werror fix
Jerry-Jinfeng-Guo Jul 23, 2024
56a8c0e
3 more werror
Jerry-Jinfeng-Guo Jul 23, 2024
a0319be
[skip ci] wip
Jerry-Jinfeng-Guo Jul 24, 2024
dabd3e1
single transformer finished; TODO pack the indices for the binary_sea…
Jerry-Jinfeng-Guo Jul 25, 2024
663de80
binary search implementation + tests done; next to add a benchmark un…
Jerry-Jinfeng-Guo Jul 25, 2024
b5ce206
cleaned up a bit
Jerry-Jinfeng-Guo Jul 25, 2024
8c3d25d
sonar cloud
Jerry-Jinfeng-Guo Jul 25, 2024
8e91795
addressing comments; debugging validation tests
Jerry-Jinfeng-Guo Jul 26, 2024
81507d3
format
Jerry-Jinfeng-Guo Jul 26, 2024
1d48228
intermediate commit debugging batch validation
Jerry-Jinfeng-Guo Jul 26, 2024
c775dd8
code refactored; validation tests fixed;
Jerry-Jinfeng-Guo Jul 27, 2024
8d4bf5c
clang-tidy
Jerry-Jinfeng-Guo Jul 27, 2024
55df84b
resolve code smells
Jerry-Jinfeng-Guo Jul 28, 2024
37e5784
clang tidy
Jerry-Jinfeng-Guo Jul 28, 2024
a21c62a
unit test for number of pf runs binary search vs scan-line
Jerry-Jinfeng-Guo Jul 28, 2024
ee07a29
format
Jerry-Jinfeng-Guo Jul 28, 2024
6c0282c
one last sonar cloud code smell
Jerry-Jinfeng-Guo Jul 29, 2024
5f7876b
Sonar Cloud, attempt 2.0
Jerry-Jinfeng-Guo Jul 29, 2024
b7647d6
give up on Sonar
Jerry-Jinfeng-Guo Jul 29, 2024
6ee24b2
address most of the comments;
Jerry-Jinfeng-Guo Jul 30, 2024
cccc2ac
added enum and related interface logic
Jerry-Jinfeng-Guo Jul 31, 2024
31bad9c
fixed batch mode error; fixed c api test error
Jerry-Jinfeng-Guo Jul 31, 2024
a50c7f7
doc; TODO: python validation test don't know the error yet
Jerry-Jinfeng-Guo Jul 31, 2024
4689516
address comment and fix python side
Jerry-Jinfeng-Guo Aug 1, 2024
27db414
sonar clouds; few depth warnings are ignored
Jerry-Jinfeng-Guo Aug 1, 2024
5f786a6
sonar clouds, 2.0
Jerry-Jinfeng-Guo Aug 1, 2024
f87da45
sonar cloud 2.1
Jerry-Jinfeng-Guo Aug 1, 2024
68af3ff
make `fast_any` default; updated code and doc
Jerry-Jinfeng-Guo Aug 1, 2024
fca830e
addressed comments
Jerry-Jinfeng-Guo Aug 2, 2024
220801f
sonar clownd
Jerry-Jinfeng-Guo Aug 2, 2024
bbb51ab
addressed comments
Jerry-Jinfeng-Guo Aug 2, 2024
ba8da6e
removed SearchMethod from python enum; optimizer now assigns search m…
Jerry-Jinfeng-Guo Aug 2, 2024
270b5d5
revert changes written to the notebook
Jerry-Jinfeng-Guo Aug 2, 2024
664796a
clang-tidy
Jerry-Jinfeng-Guo Aug 2, 2024
caeee05
resolved one code smell
Jerry-Jinfeng-Guo Aug 2, 2024
612dd8c
docs.
Jerry-Jinfeng-Guo Aug 2, 2024
4781c39
addressing most of the comments from 05-aug
Jerry-Jinfeng-Guo Aug 5, 2024
a930bc0
[skip ci] included the other proposed comment
Jerry-Jinfeng-Guo Aug 5, 2024
7aa5bc4
addressing final comments from 05-aug
Jerry-Jinfeng-Guo Aug 5, 2024
14c4606
Merge branch 'main' into feature/binary-search-tap-changer
Jerry-Jinfeng-Guo Aug 5, 2024
3df1cf3
fotmat
Jerry-Jinfeng-Guo Aug 5, 2024
3ed8aed
unused import
Jerry-Jinfeng-Guo Aug 5, 2024
4674159
updated the notebook and part of the code.
Jerry-Jinfeng-Guo Aug 6, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/examples/Transformer Examples.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -1585,7 +1585,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.2"
"version": "3.10.14"
mgovers marked this conversation as resolved.
Show resolved Hide resolved
},
"vscode": {
"interpreter": {
Expand Down
20 changes: 10 additions & 10 deletions docs/user_manual/calculations.md
Jerry-Jinfeng-Guo marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -650,7 +650,7 @@ Internally, to achieve an optimal regulated tap position, the control algorithm

| strategy | initial tap position | exploitation direction | search method | description |
| ----------------------------------------------------------------------------------------------------------- | -------------------- | ---------------------- | ------------- | ------------------------------------------------------------------------------------- |
| {py:class}`TapChangingStrategy.any_valid_tap <power_grid_model.enum.TapChangingStrategy.any_valid_tap>` | current tap position | no exploitation | scanline | Find any tap position that gives a control side voltage within the `u_band` |
| {py:class}`TapChangingStrategy.any_valid_tap <power_grid_model.enum.TapChangingStrategy.any_valid_tap>` | current tap position | no exploitation | linear_search | Find any tap position that gives a control side voltage within the `u_band` |
| {py:class}`TapChangingStrategy.min_voltage_tap <power_grid_model.enum.TapChangingStrategy.min_voltage_tap>` | `tap_max` | step up | binary_search | Find the tap position that gives the lowest control side voltage within the `u_band` |
| {py:class}`TapChangingStrategy.max_voltage_tap <power_grid_model.enum.TapChangingStrategy.max_voltage_tap>` | `tap_min` | step down | binary_search | Find the tap position that gives the highest control side voltage within the `u_band` |
| {py:class}`TapChangingStrategy.fast_any_tap <power_grid_model.enum.TapChangingStrategy.fast_any_tap>` | current tap position | no exploitation | binary_search | Find any tap position that gives a control side voltage within the `u_band` |
Expand All @@ -659,21 +659,21 @@ Internally, to achieve an optimal regulated tap position, the control algorithm

Given the discrete nature of the finite tap ranges, we use the following search methods to find the next tap position along the exploitation direction.

| Search method | Description | Default |
| ----------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | -------- |
| {py:class}`SearchMethod.scanline <power_grid_model.enum.SearchMethod.scanline>` | In the context of finite discrete range search, go one value at a time. | |
| {py:class}`SearchMethod.binary_search <power_grid_model.enum.SearchMethod.binary_search>` | In the context of finite discrete range search, go half the remaining range at a time. | &#10004; |
| Search method | Description |
| ------------- | -------------------------------------------------------------------------------------- |
| linear search | In the context of finite discrete range search, go one value at a time. |
| binary search | In the context of finite discrete range search, go half the remaining range at a time. |
Jerry-Jinfeng-Guo marked this conversation as resolved.
Show resolved Hide resolved

Compatibility of search methods and tap changing strategies

| | {py:class}`TapChangingStrategy.any_valid_tap <power_grid_model.enum.TapChangingStrategy.any_valid_tap>` | {py:class}`TapChangingStrategy.min_voltage_tap <power_grid_model.enum.TapChangingStrategy.min_voltage_tap>` | {py:class}`TapChangingStrategy.max_voltage_tap <power_grid_model.enum.TapChangingStrategy.max_voltage_tap>` | {py:class}`TapChangingStrategy.fast_any_tap <power_grid_model.enum.TapChangingStrategy.fast_any_tap>` |
| ----------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
| {py:class}`SearchMethod.scanline <power_grid_model.enum.SearchMethod.scanline>` | &#10004; | &#10004; | &#10004; | &#10008; |
| {py:class}`SearchMethod.binary_search <power_grid_model.enum.SearchMethod.binary_search>` | &#10008; | &#10004; | &#10004; | &#10004; |
| | {py:class}`TapChangingStrategy.any_valid_tap <power_grid_model.enum.TapChangingStrategy.any_valid_tap>` | {py:class}`TapChangingStrategy.min_voltage_tap <power_grid_model.enum.TapChangingStrategy.min_voltage_tap>` | {py:class}`TapChangingStrategy.max_voltage_tap <power_grid_model.enum.TapChangingStrategy.max_voltage_tap>` | {py:class}`TapChangingStrategy.fast_any_tap <power_grid_model.enum.TapChangingStrategy.fast_any_tap>` |
| ------------- | ------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
| linear search | &#10004; | &#10004; | &#10004; | &#10008; |
Jerry-Jinfeng-Guo marked this conversation as resolved.
Show resolved Hide resolved
| binary search | &#10008; | &#10004; default | &#10004; default | &#10004; |


```{note}
Note that the combination of `any_valid_tap` with `binary_search` and `fast_any_tap` with `scanline` are not acceptable and exceptions will be thrown.
Note that the combination of `any_valid_tap` with `binary_search` and `fast_any_tap` with `linear_search` are not acceptable and exceptions will be thrown.
```

## Batch Calculations
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ enum class OptimizerStrategy : IntS { // Conventions for optimization strategies
};

enum class SearchMethod : IntS { // Which type of tap search method for finite element optimization process
scanline = 0, // use scanline method: one step per iteration
linear_search = 0, // use linear_search method: one step per iteration
binary_search = 1, // use binary search: half a tap range at a time
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -725,25 +725,44 @@ class TapPositionOptimizerImpl<std::tuple<TransformerTypes...>, StateCalculator,
public:
TapPositionOptimizerImpl(Calculator calculator, StateUpdater updater, OptimizerStrategy strategy,
meta_data::MetaData const& meta_data,
SearchMethod tap_search = SearchMethod::binary_search)
: meta_data_{&meta_data},
calculate_{std::move(calculator)},
update_{std::move(updater)},
strategy_{strategy},
tap_search_{tap_search} {
auto const is_supported = [&strategy, &tap_search] {
std::optional<SearchMethod> tap_search = std::nullopt)
: meta_data_{&meta_data}, calculate_{std::move(calculator)}, update_{std::move(updater)}, strategy_{strategy} {
auto const is_supported = [&strategy](std::optional<SearchMethod> const& tap_search) {
if (!tap_search) {
return true;
}
switch (strategy) {
case OptimizerStrategy::any:
return tap_search == SearchMethod::scanline;
return tap_search == SearchMethod::linear_search;
case OptimizerStrategy::fast_any:
return tap_search == SearchMethod::binary_search;
default:
return true;
}
};
if (!is_supported()) {

if (!is_supported(tap_search)) {
throw TapSearchStrategyIncompatibleError{
"Search method is incompatible with optimization strategy: ", strategy_, tap_search_};
"Search method is incompatible with optimization strategy: ", strategy_, tap_search.value()};
}

if (!tap_search) {
switch (strategy) {
case OptimizerStrategy::any:
tap_search_ = SearchMethod::linear_search;
break;
case OptimizerStrategy::fast_any:
case OptimizerStrategy::local_maximum:
case OptimizerStrategy::global_maximum:
case OptimizerStrategy::local_minimum:
case OptimizerStrategy::global_minimum:
tap_search_ = SearchMethod::binary_search;
break;
default:
throw MissingCaseForEnumError{"TapPositionOptimizer::TapPositionOptimizerImpl", tap_search.value()};
Jerry-Jinfeng-Guo marked this conversation as resolved.
Show resolved Hide resolved
}
} else {
tap_search_ = tap_search.value();
}
}

Expand Down Expand Up @@ -795,7 +814,7 @@ class TapPositionOptimizerImpl<std::tuple<TransformerTypes...>, StateCalculator,
CalculationMethod method) -> MathOutput<ResultType> {
pilot_run(regulator_order);

if (auto result = iterate_with_fallback(state, regulator_order, method, SearchMethod::scanline);
if (auto result = iterate_with_fallback(state, regulator_order, method, SearchMethod::linear_search);
Jerry-Jinfeng-Guo marked this conversation as resolved.
Show resolved Hide resolved
strategy_ == OptimizerStrategy::any) {
return produce_output(regulator_order, std::move(result));
}
Expand Down Expand Up @@ -825,7 +844,7 @@ class TapPositionOptimizerImpl<std::tuple<TransformerTypes...>, StateCalculator,
-> ResultType {
SearchMethod const actual_search = search.value_or(tap_search_);
Jerry-Jinfeng-Guo marked this conversation as resolved.
Show resolved Hide resolved
auto fallback = [this, &state, &regulator_order, &method, &actual_search] {
std::ignore = iterate(state, regulator_order, CalculationMethod::linear, SearchMethod::scanline);
std::ignore = iterate(state, regulator_order, CalculationMethod::linear, SearchMethod::linear_search);
return iterate(state, regulator_order, method, actual_search);
};

Expand Down Expand Up @@ -885,7 +904,7 @@ class TapPositionOptimizerImpl<std::tuple<TransformerTypes...>, StateCalculator,
switch (search) {
case SearchMethod::binary_search:
return adjust_transformer_bs(regulator, state, solver_output, update_data, options);
case SearchMethod::scanline:
case SearchMethod::linear_search:
return adjust_transformer_scan(regulator, state, solver_output, update_data);
default:
throw MissingCaseForEnumError{"TapPositionOptimizer::adjust_transformer", search};
Expand Down
13 changes: 0 additions & 13 deletions src/power_grid_model/enum.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,19 +92,6 @@ class TapChangingStrategy(IntEnum):
"""


class SearchMethod(IntEnum):
"""Search Methods"""

scanline = 0
"""
Linear search
"""
binary_search = 1
"""
Binary search
"""


class MeasuredTerminalType(IntEnum):
"""The type of asset measured by a (power) sensor"""

Expand Down
6 changes: 3 additions & 3 deletions tests/cpp_unit_tests/test_optimizer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -190,10 +190,10 @@ struct OptimizerStrategySearchSide {
ControlSide side{};
};

constexpr auto search_methods = [] { return std::array{SearchMethod::scanline, SearchMethod::binary_search}; }();
constexpr auto search_methods = [] { return std::array{SearchMethod::linear_search, SearchMethod::binary_search}; }();

constexpr auto strategy_search_and_sides = [] {
// regular any strategy is only used in combination with scanline search
// regular any strategy is only used in combination with linear_search search
size_t const options_size = strategies.size() * tap_sides.size() * search_methods.size() - search_methods.size();
std::array<OptimizerStrategySearchSide, options_size> result;
size_t idx{};
Expand All @@ -217,7 +217,7 @@ struct OptStrategyMethodSearch {
};

constexpr auto strategy_method_and_searches = [] {
// regular any strategy is only used in combination with scanline search
// regular any strategy is only used in combination with linear_search search
size_t const options_size =
strategies.size() * calculation_methods.size() * search_methods.size() - search_methods.size();
std::array<OptStrategyMethodSearch, options_size> result;
Expand Down
12 changes: 6 additions & 6 deletions tests/cpp_unit_tests/test_tap_position_optimizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -699,7 +699,7 @@ TEST_CASE("Test Tap position optimizer") {

SUBCASE("empty state") {
state.components.set_construction_complete();
auto optimizer = get_optimizer(OptimizerStrategy::any, SearchMethod::scanline);
auto optimizer = get_optimizer(OptimizerStrategy::any, SearchMethod::linear_search);
auto result = optimizer.optimize(state, CalculationMethod::default_method);
CHECK(result.solver_output.size() == 1);
CHECK(result.solver_output[0].method == CalculationMethod::default_method);
Expand Down Expand Up @@ -1079,7 +1079,7 @@ TEST_CASE("Test Tap position optimizer") {

auto update_data = TransformerTapRegulatorUpdate{.id = 4, .u_set = 0.4, .u_band = 0.0};

// tap pos will jump between 3 and 4 in scanline method
// tap pos will jump between 3 and 4 in linear_search method
state_b.tap_min = 1;
state_b.tap_max = 5;
state_b.tap_pos = 5;
Expand All @@ -1095,14 +1095,14 @@ TEST_CASE("Test Tap position optimizer") {
state_b.tap_side = tap_side;
state_a.tap_side = tap_side;

auto optimizer = get_optimizer(strategy, SearchMethod::scanline);
auto optimizer = get_optimizer(strategy, SearchMethod::linear_search);
auto const cached_state = state; // NOSONAR
CHECK_THROWS_AS(optimizer.optimize(state, CalculationMethod::default_method), MaxIterationReached);
CHECK(twoStatesEqual(cached_state, state));
}
}

SUBCASE("Binary search vs scanline optimization for tap changer") {
SUBCASE("Binary search vs linear_search optimization for tap changer") {
state_b.tap_min = IntS{-100};
state_b.tap_max = IntS{100};

Expand All @@ -1123,7 +1123,7 @@ TEST_CASE("Test Tap position optimizer") {
state_b.tap_side = tap_side;
state_a.tap_side = tap_side;

auto optimizer_scan = get_optimizer(strategy, SearchMethod::scanline);
auto optimizer_scan = get_optimizer(strategy, SearchMethod::linear_search);
auto optimizer_bs = get_optimizer(strategy, SearchMethod::binary_search);

auto const result = optimizer_scan.optimize(state, CalculationMethod::default_method);
Expand All @@ -1132,7 +1132,7 @@ TEST_CASE("Test Tap position optimizer") {
auto const result_bs = optimizer_bs.optimize(state, CalculationMethod::default_method);
auto const bs_number_of_pf_runs = optimizer_bs.get_total_iterations();

CHECK(scan_number_of_pf_runs >= bs_number_of_pf_runs); // ToDo: In need of a more complex grid
CHECK(scan_number_of_pf_runs >= bs_number_of_pf_runs);
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions tests/cpp_validation_tests/test_validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ std::map<std::string, OptimizerStrategy, std::less<>> const optimizer_strategy_m
{"fast_any_tap", OptimizerStrategy::fast_any}};

std::map<std::string, SearchMethod, std::less<>> const optimizer_search_mapping = {
{"scanline", SearchMethod::scanline}, {"binary_search", SearchMethod::binary_search}};
{"linear_search", SearchMethod::linear_search}, {"binary_search", SearchMethod::binary_search}};

// case parameters
struct CaseParam {
Expand Down Expand Up @@ -353,7 +353,7 @@ CalculationFunc calculation_func(CaseParam const& param) {
: OptimizerType::automatic_tap_adjustment;
options.optimizer_strategy = optimizer_strategy_mapping.at(param.tap_changing_strategy);
if (options.optimizer_strategy == OptimizerStrategy::any) {
options.search_method = SearchMethod::scanline;
options.search_method = SearchMethod::linear_search;
} else {
options.search_method = optimizer_search_mapping.at(param.search_method);
}
Expand Down
Loading