SecurityAnalysisResultBuilder.java
/**
* Copyright (c) 2018, RTE (http://www.rte-france.com)
* 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;
import com.google.common.collect.ImmutableList;
import com.powsybl.contingency.Contingency;
import com.powsybl.loadflow.LoadFlowResult;
import com.powsybl.security.interceptors.SecurityAnalysisInterceptor;
import com.powsybl.security.interceptors.SecurityAnalysisResultContext;
import com.powsybl.security.results.*;
import com.powsybl.security.results.OperatorStrategyResult;
import com.powsybl.security.strategy.OperatorStrategy;
import java.util.*;
/**
* Facilitates the creation of security analysis results.
* <p>
* Encapsulates filtering of limit violations with a provided {@link LimitViolationFilter},
* as well as notifications to {@link SecurityAnalysisInterceptor}s.
*
* @author Sylvain Leclerc {@literal <sylvain.leclerc at rte-france.com>}
*/
public class SecurityAnalysisResultBuilder {
private final LimitViolationFilter filter;
private final SecurityAnalysisResultContext context;
private final List<SecurityAnalysisInterceptor> interceptors;
// Below are volatile objects used for building the actual complete result
private PreContingencyResult preContingencyResult;
private final List<PostContingencyResult> postContingencyResults = Collections.synchronizedList(new ArrayList<>());
private final List<OperatorStrategyResult> operatorStrategyResults = Collections.synchronizedList(new ArrayList<>());
public SecurityAnalysisResultBuilder(LimitViolationFilter filter, SecurityAnalysisResultContext context,
Collection<SecurityAnalysisInterceptor> interceptors) {
this.filter = Objects.requireNonNull(filter);
this.context = Objects.requireNonNull(context);
this.interceptors = ImmutableList.copyOf(interceptors);
this.preContingencyResult = new PreContingencyResult();
}
public SecurityAnalysisResultBuilder(LimitViolationFilter filter, SecurityAnalysisResultContext context) {
this(filter, context, Collections.emptyList());
}
private void addPostContingencyResult(PostContingencyResult result) {
postContingencyResults.add(Objects.requireNonNull(result));
}
/**
* Initiates the creation of the result for N situation.
*
* @return a {@link PreContingencyResultBuilder} instance.
*/
public PreContingencyResultBuilder preContingency() {
return new PreContingencyResultBuilder(context);
}
/**
* Initiates the creation of the result for N situation
*
* @param preContingencyResultContext the context used when create the result
* @return a {@link PreContingencyResultBuilder} instance.
*/
public PreContingencyResultBuilder preContingency(SecurityAnalysisResultContext preContingencyResultContext) {
return new PreContingencyResultBuilder(preContingencyResultContext);
}
/**
* Initiates the creation of the result for one {@link Contingency}.
*
* @param contingency the contingency for which a result should be created
* @return a {@link PostContingencyResultBuilder} instance.
*/
public PostContingencyResultBuilder contingency(Contingency contingency) {
return new PostContingencyResultBuilder(contingency, context);
}
/**
* Initiates the creation of the result for one {@link Contingency}
*
* @param contingency the contingency for which a result should be created
* @param postContingencyResultContext the context used when create the result
* @return a {@link PostContingencyResultBuilder} instance.
*/
public PostContingencyResultBuilder contingency(Contingency contingency, SecurityAnalysisResultContext postContingencyResultContext) {
return new PostContingencyResultBuilder(contingency, postContingencyResultContext);
}
/**
* Initiates the creation of the result for one {@link OperatorStrategy}.
*
* @param strategy the operator strategy for which a result should be created
* @return a {@link OperatorStrategyResultBuilder} instance.
*/
public OperatorStrategyResultBuilder operatorStrategy(OperatorStrategy strategy) {
return operatorStrategy(strategy, context);
}
/**
* Initiates the creation of the result for one {@link OperatorStrategy}.
*
* @param strategy the operator strategy for which a result should be created
* @return a {@link OperatorStrategyResultBuilder} instance.
*/
public OperatorStrategyResultBuilder operatorStrategy(OperatorStrategy strategy, SecurityAnalysisResultContext strategyContext) {
return new OperatorStrategyResultBuilder(strategy, strategyContext);
}
/**
* Finalizes the result.
*
* @return the N situation result builder
*/
public SecurityAnalysisResult build() {
if (preContingencyResult == null) {
throw new IllegalStateException("Pre-contingency result is not yet defined, cannot build security analysis result.");
}
SecurityAnalysisResult res = new SecurityAnalysisResult(preContingencyResult, postContingencyResults, operatorStrategyResults);
res.setNetworkMetadata(new NetworkMetadata(context.getNetwork()));
interceptors.forEach(i -> i.onSecurityAnalysisResult(res, context));
return res;
}
/**
* Base class for the pre and post contingency builders.
*/
public abstract class AbstractLimitViolationsResultBuilder<B extends AbstractLimitViolationsResultBuilder<B>> {
protected final List<BranchResult> branchResults = new ArrayList<>();
protected final List<BusResult> busResults = new ArrayList<>();
protected final List<ThreeWindingsTransformerResult> threeWindingsTransformerResults = new ArrayList<>();
protected final List<LimitViolation> violations = new ArrayList<>();
protected final SecurityAnalysisResultContext resultContext;
/**
* Initiates a result builder with a {@link SecurityAnalysisResultContext}.
*
* @param resultContext The context would be used when creation result or as default context when a limit violation added.
*/
private AbstractLimitViolationsResultBuilder(SecurityAnalysisResultContext resultContext) {
this.resultContext = Objects.requireNonNull(resultContext);
}
/**
* Adds a {@link LimitViolation} to the builder.
* The default result context would be supplied to interceptors.
*
* @param violation
* @return
*/
public B addViolation(LimitViolation violation) {
addViolation(violation, resultContext);
return (B) this;
}
/**
* Adds a {@link LimitViolation} to the builder with a context.
*
* @param violation the context would be supplied to interceptors.
* @return
*/
public B addViolation(LimitViolation violation, SecurityAnalysisResultContext limitViolationContext) {
Objects.requireNonNull(limitViolationContext);
violations.add(Objects.requireNonNull(violation));
interceptors.forEach(i -> i.onLimitViolation(violation, limitViolationContext));
return (B) this;
}
public B addViolations(List<LimitViolation> violations, SecurityAnalysisResultContext limitViolationContext) {
Objects.requireNonNull(violations).forEach(limitViolation -> addViolation(limitViolation, limitViolationContext));
return (B) this;
}
public B addViolations(List<LimitViolation> violations) {
return addViolations(violations, resultContext);
}
public B addBranchResult(BranchResult branchResult) {
this.branchResults.add(branchResult);
return (B) this;
}
public B addBusResult(BusResult busResult) {
this.busResults.add(busResult);
return (B) this;
}
public B addThreeWindingsTransformerResult(ThreeWindingsTransformerResult threeWindingsTransformerResult) {
this.threeWindingsTransformerResults.add(threeWindingsTransformerResult);
return (B) this;
}
}
/**
* Builder for the pre-contingency result
*/
public class PreContingencyResultBuilder extends AbstractLimitViolationsResultBuilder<PreContingencyResultBuilder> {
private LoadFlowResult.ComponentResult.Status status = LoadFlowResult.ComponentResult.Status.CONVERGED;
PreContingencyResultBuilder(SecurityAnalysisResultContext resultContext) {
super(resultContext);
}
public PreContingencyResultBuilder setStatus(LoadFlowResult.ComponentResult.Status status) {
this.status = status;
return this;
}
/**
* Finalize the creation of the PreContingencyResult instance
*
* @return the parent {@link SecurityAnalysisResultBuilder} instance.
*/
public SecurityAnalysisResultBuilder endPreContingency() {
List<LimitViolation> filteredViolations = filter.apply(violations, context.getNetwork());
preContingencyResult = new PreContingencyResult(status, new LimitViolationsResult(filteredViolations), new NetworkResult(branchResults, busResults, threeWindingsTransformerResults));
interceptors.forEach(i -> i.onPreContingencyResult(preContingencyResult, resultContext));
return SecurityAnalysisResultBuilder.this;
}
}
public class PostContingencyResultBuilder extends AbstractLimitViolationsResultBuilder<PostContingencyResultBuilder> {
private final Contingency contingency;
private PostContingencyComputationStatus status;
private ConnectivityResult connectivityResult;
PostContingencyResultBuilder(Contingency contingency, SecurityAnalysisResultContext resultContext) {
super(Objects.requireNonNull(resultContext));
this.contingency = Objects.requireNonNull(contingency);
}
@Override
public PostContingencyResultBuilder addViolation(LimitViolation violation, SecurityAnalysisResultContext limitViolationContext) {
Objects.requireNonNull(limitViolationContext);
violations.add(Objects.requireNonNull(violation));
interceptors.forEach(i -> i.onLimitViolation(contingency, violation, limitViolationContext));
return this;
}
public PostContingencyResultBuilder setStatus(PostContingencyComputationStatus status) {
this.status = status;
return this;
}
public PostContingencyResultBuilder setConnectivityResult(ConnectivityResult connectivityResult) {
this.connectivityResult = connectivityResult;
return this;
}
/**
* Finalize the creation of the PostContingencyResult instance
*
* @return the parent {@link SecurityAnalysisResultBuilder} instance.
*/
public SecurityAnalysisResultBuilder endContingency() {
List<LimitViolation> filteredViolations = filter.apply(violations, context.getNetwork());
PostContingencyResult res = new PostContingencyResult(contingency, status, filteredViolations,
branchResults, busResults, threeWindingsTransformerResults, connectivityResult);
interceptors.forEach(i -> i.onPostContingencyResult(res, resultContext));
addPostContingencyResult(res);
return SecurityAnalysisResultBuilder.this;
}
}
public class OperatorStrategyResultBuilder {
private final OperatorStrategy strategy;
private final List<OperatorStrategyResult.ConditionalActionsResult> conditionalActionsResult = new ArrayList<>();
SecurityAnalysisResultContext resultContext;
OperatorStrategyResultBuilder(OperatorStrategy strategy, SecurityAnalysisResultContext resultContext) {
this.strategy = Objects.requireNonNull(strategy);
this.resultContext = resultContext;
}
public ConditionalActionsResultBuilder newConditionalActionsResult(String conditionalActionsId) {
return new ConditionalActionsResultBuilder(conditionalActionsId, resultContext);
}
/**
* Finalize the creation of the OperatorStrategyResult instance
*
* @return the parent {@link SecurityAnalysisResultBuilder} instance.
*/
public SecurityAnalysisResultBuilder endOperatorStrategy() {
OperatorStrategyResult res = new OperatorStrategyResult(strategy, conditionalActionsResult);
//TODO: call to interceptors
operatorStrategyResults.add(res);
return SecurityAnalysisResultBuilder.this;
}
public class ConditionalActionsResultBuilder extends AbstractLimitViolationsResultBuilder<ConditionalActionsResultBuilder> {
private final String conditionalActionsId;
private PostContingencyComputationStatus status = PostContingencyComputationStatus.CONVERGED;
ConditionalActionsResultBuilder(String conditionalActionsId, SecurityAnalysisResultContext resultContext) {
super(Objects.requireNonNull(resultContext));
this.conditionalActionsId = conditionalActionsId;
}
@Override
public ConditionalActionsResultBuilder addViolation(LimitViolation violation, SecurityAnalysisResultContext limitViolationContext) {
Objects.requireNonNull(limitViolationContext);
violations.add(Objects.requireNonNull(violation));
//TODO: call to interceptors
return this;
}
public ConditionalActionsResultBuilder setStatus(PostContingencyComputationStatus status) {
this.status = status;
return this;
}
/**
* Finalize the creation of the Conditional actions result instance
*
* @return the parent {@link SecurityAnalysisResultBuilder} instance.
*/
public OperatorStrategyResultBuilder endConditionalActions() {
List<LimitViolation> filteredViolations = filter.apply(violations, context.getNetwork());
LimitViolationsResult limitViolationsResult = new LimitViolationsResult(filteredViolations);
NetworkResult networkResult = new NetworkResult(branchResults, busResults, threeWindingsTransformerResults);
conditionalActionsResult.add(new OperatorStrategyResult.ConditionalActionsResult(conditionalActionsId, status, limitViolationsResult, networkResult));
return OperatorStrategyResultBuilder.this;
}
}
}
}