-
Notifications
You must be signed in to change notification settings - Fork 49
Security analysis detailed result handler #3625
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,107 @@ | ||
| /** | ||
| * Copyright (c) 2025, Coreso SA (https://www.coreso.eu/) and TSCNET Services GmbH (https://www.tscnet.eu/) | ||
| * This Source Code Form is subject to the terms of the Mozilla Public | ||
| * License, v. 2.0. If a copy of the MPL was not distributed with this | ||
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||
| * SPDX-License-Identifier: MPL-2.0 | ||
| */ | ||
| package com.powsybl.security.results; | ||
|
|
||
| import com.powsybl.contingency.Contingency; | ||
| import com.powsybl.security.strategy.OperatorStrategy; | ||
|
|
||
| import java.util.ArrayList; | ||
| import java.util.HashMap; | ||
| import java.util.List; | ||
| import java.util.Map; | ||
| import java.util.function.BiConsumer; | ||
|
|
||
| /** | ||
| * @author Bertrand Rix {@literal <bertrand.rix at artelys.com>} | ||
| */ | ||
| public class InMemoryResultHandler implements SecurityAnalysisResultHandler { | ||
|
|
||
| public static class StateResult { | ||
|
|
||
| private final List<BranchResult> branchResultList; | ||
| private final List<ThreeWindingsTransformerResult> threeWindingsTransformerResultList; | ||
| private final List<BusResult> busResultList; | ||
|
|
||
| StateResult() { | ||
| branchResultList = new ArrayList<>(); | ||
| threeWindingsTransformerResultList = new ArrayList<>(); | ||
| busResultList = new ArrayList<>(); | ||
| } | ||
|
|
||
| List<BranchResult> getBranchResultList() { | ||
| return branchResultList; | ||
| } | ||
|
|
||
| List<ThreeWindingsTransformerResult> getThreeWindingsTransformerResultList() { | ||
| return threeWindingsTransformerResultList; | ||
| } | ||
|
|
||
| List<BusResult> getBusResultList() { | ||
| return busResultList; | ||
| } | ||
|
|
||
| void addBranchResult(BranchResult branchResult) { | ||
| branchResultList.add(branchResult); | ||
| } | ||
|
|
||
| void addThreeWindingsTransformerResult(ThreeWindingsTransformerResult threeWindingsTransformerResult) { | ||
| threeWindingsTransformerResultList.add(threeWindingsTransformerResult); | ||
| } | ||
|
|
||
| void addBusResult(BusResult busResult) { | ||
| busResultList.add(busResult); | ||
| } | ||
| } | ||
|
|
||
| private final StateResult baseCaseResult; | ||
| private final Map<String, StateResult> postContingencyResults; | ||
| private final Map<String, StateResult> operatorStrategyResults; | ||
|
|
||
| public InMemoryResultHandler() { | ||
| baseCaseResult = new StateResult(); | ||
| postContingencyResults = new HashMap<>(); | ||
| operatorStrategyResults = new HashMap<>(); | ||
| } | ||
|
|
||
| @Override | ||
| public void writeBranchResult(Contingency contingency, OperatorStrategy operatorStrategy, BranchResult branchResult) { | ||
| registerResult(contingency, operatorStrategy, branchResult, StateResult::addBranchResult); | ||
| } | ||
|
|
||
| @Override | ||
| public void writeThreeWindingsTransformerResult(Contingency contingency, OperatorStrategy operatorStrategy, ThreeWindingsTransformerResult threeWindingsTransformerResult) { | ||
| registerResult(contingency, operatorStrategy, threeWindingsTransformerResult, StateResult::addThreeWindingsTransformerResult); | ||
| } | ||
|
|
||
| @Override | ||
| public void writeBusResult(Contingency contingency, OperatorStrategy operatorStrategy, BusResult busResult) { | ||
| registerResult(contingency, operatorStrategy, busResult, StateResult::addBusResult); | ||
| } | ||
|
|
||
| StateResult getBaseCaseResult() { | ||
| return baseCaseResult; | ||
| } | ||
|
|
||
| Map<String, StateResult> getPostContingencyResults() { | ||
| return postContingencyResults; | ||
| } | ||
|
|
||
| Map<String, StateResult> getOperatorStrategyResults() { | ||
| return operatorStrategyResults; | ||
| } | ||
|
|
||
| <T> void registerResult(Contingency contingency, OperatorStrategy operatorStrategy, T result, BiConsumer<StateResult, T> resultAdder) { | ||
| if (contingency == null) { | ||
| resultAdder.accept(baseCaseResult, result); | ||
| } else if (operatorStrategy == null) { | ||
| resultAdder.accept(postContingencyResults.computeIfAbsent(contingency.getId(), k -> new StateResult()), result); | ||
| } else { | ||
| resultAdder.accept(operatorStrategyResults.computeIfAbsent(operatorStrategy.getId(), k -> new StateResult()), result); | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| /** | ||
| * Copyright (c) 2025, Coreso SA (https://www.coreso.eu/) and TSCNET Services GmbH (https://www.tscnet.eu/) | ||
| * This Source Code Form is subject to the terms of the Mozilla Public | ||
| * License, v. 2.0. If a copy of the MPL was not distributed with this | ||
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||
| * SPDX-License-Identifier: MPL-2.0 | ||
| */ | ||
| package com.powsybl.security.results; | ||
|
|
||
| import com.powsybl.contingency.Contingency; | ||
| import com.powsybl.security.strategy.OperatorStrategy; | ||
|
|
||
| /** | ||
| * @author Bertrand Rix {@literal <bertrand.rix at artelys.com>} | ||
| */ | ||
| public interface SecurityAnalysisResultHandler { | ||
|
|
||
| void writeBranchResult(Contingency contingency, OperatorStrategy operatorStrategy, BranchResult branchResult); | ||
|
|
||
| void writeThreeWindingsTransformerResult(Contingency contingency, OperatorStrategy operatorStrategy, ThreeWindingsTransformerResult branchResult); | ||
|
|
||
| void writeBusResult(Contingency contingency, OperatorStrategy operatorStrategy, BusResult busResult); | ||
|
|
||
| } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To align with the suggestion in #3626 (that extends the sensitivity writer for multi-component, better failure diagnosis and interrupt) you could add // numCC and numCs are used for contingencies that have equipments in multiple components void writeSynchronousComponentStatus(int numCC, int numCS, LoadFlowResult.ComponentResult.Status, String statusText,); // Called at the end of the computation if the computation has not been interrupted
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To turn this in to a way to get result in streaming, you could also add methods for listening to violations.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @obrix yes, here you only streamed only a part of the SecurityAnalysisResult API. We should fully convert it even for violations. |
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why a list and not only one ?
If there was only one, we coud mimic the sensi API: no result handler provided means you return an in memory result.
Handler provided means you return void and send the data to the writer.
This view implies that the writer supports also the violation results, not only the monitored objects.
Then if it is useful to have serveral handlers, we would provided the implementation of a SecurityAnalysisResultMultiplexer that would dispatch the calls to a list of handlers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes I agree, if some kind of multiplexing is needed the user has everything available to do it himself anyway. So ok for a single handler API.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
An handler provided would mean we do not fill the result objects then ? NetworkResult in PreContingencyResult / PostContingencyResult would remain empty ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here is the Sensitivity API. We could use the same idea for security + writer
No writer -> returns a Result. Internally it creates a writer that creates the result object. The provider only knows the writer.
public SensitivityAnalysisResult run(Network network,
String workingVariantId,
List factors,
SensitivityAnalysisRunParameters runParameters)
A writer -> returns void. The user code handles that data as needed, without memory overhead, for example if the result is directly streamed into a database or a python dataframe.
public void run(Network network,
String workingVariantId,
SensitivityFactorReader factorReader,
SensitivityResultWriter resultWriter,
SensitivityAnalysisRunParameters runParameters) {
runAsync(network, workingVariantId, factorReader, resultWriter, runParameters).join();
}