Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,14 @@
import org.springframework.util.CollectionUtils;
import org.springframework.web.client.HttpClientErrorException;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import java.util.function.Function;

import static com.powsybl.loadflow.LoadFlowResult.ComponentResult.Status.NO_CALCULATION;
import static org.gridsuite.computation.service.NotificationService.getFailedMessage;
import static org.gridsuite.securityanalysis.server.service.SecurityAnalysisService.COMPUTATION_TYPE;

Expand All @@ -65,7 +63,6 @@
public class SecurityAnalysisWorkerService extends AbstractWorkerService<SecurityAnalysisResult, SecurityAnalysisRunContext, SecurityAnalysisParametersDTO, SecurityAnalysisResultService> {
private static final Logger LOGGER = LoggerFactory.getLogger(SecurityAnalysisWorkerService.class);
private final ActionsService actionsService;

private final LimitReductionService limitReductionService;

private Function<String, SecurityAnalysis.Runner> securityAnalysisFactorySupplier;
Expand Down Expand Up @@ -110,6 +107,10 @@ protected String getComputationType() {

@Override
protected CompletableFuture<SecurityAnalysisResult> getCompletableFuture(SecurityAnalysisRunContext runContext, String provider, UUID resultUuid) {
if (runContext.getContingencies().stream().allMatch(contingencyInfos -> contingencyInfos.getContingency() == null)) {
return CompletableFuture.completedFuture(
new SecurityAnalysisResult(new LimitViolationsResult(Collections.emptyList()), NO_CALCULATION, Collections.emptyList()));
}
Comment on lines +110 to +113
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

NO_CALCULATION is introduced but may be persisted as DIVERGED.

With the new early return at Line 110, saveResult still maps any non-CONVERGED load-flow status to SecurityAnalysisStatus.DIVERGED (Line 228 onward). This makes no-contingency runs indistinguishable from real divergence.

💡 Proposed fix
@@
     protected void saveResult(Network network, AbstractResultContext<SecurityAnalysisRunContext> resultContext, SecurityAnalysisResult result) {
+        LoadFlowResult.ComponentResult.Status lfStatus = result.getPreContingencyResult().getStatus();
+        SecurityAnalysisStatus saStatus =
+                lfStatus == LoadFlowResult.ComponentResult.Status.CONVERGED
+                        ? SecurityAnalysisStatus.CONVERGED
+                        : (lfStatus == NO_CALCULATION
+                                ? SecurityAnalysisStatus.NO_CALCULATION
+                                : SecurityAnalysisStatus.DIVERGED);
         resultService.insert(network,
                 resultContext.getResultUuid(),
                 result,
-                result.getPreContingencyResult().getStatus() == LoadFlowResult.ComponentResult.Status.CONVERGED
-                        ? SecurityAnalysisStatus.CONVERGED
-                        : SecurityAnalysisStatus.DIVERGED);
+                saStatus);
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/org/gridsuite/securityanalysis/server/service/SecurityAnalysisWorkerService.java`
around lines 110 - 113, The early-return for all-null contingencies sets the
result status to NO_CALCULATION but downstream saveResult still maps any
non-CONVERGED loadflow status to SecurityAnalysisStatus.DIVERGED, making
NO_CALCULATION indistinguishable; update the code path in the saveResult logic
(or the method that maps LoadFlowResult/status to SecurityAnalysisStatus) to
explicitly check for the NO_CALCULATION sentinel (from
SecurityAnalysisWorkerService/runContext result creation) and map it to a
distinct SecurityAnalysisStatus (e.g., NO_CALCULATION) instead of DIVERGED,
ensuring functions like saveResult, mapLoadflowStatusToSecurityStatus, or
wherever SecurityAnalysisStatus is derived are updated to handle NO_CALCULATION
specially.

SecurityAnalysis.Runner securityAnalysisRunner = securityAnalysisFactorySupplier.apply(provider);
String variantId = runContext.getVariantId() != null ? runContext.getVariantId() : VariantManagerConstants.INITIAL_VARIANT_ID;

Expand Down Expand Up @@ -177,6 +178,10 @@ protected void preRun(SecurityAnalysisRunContext runContext) {
actionsService.getContingencyList(runContext.getParameters().contingencyListUuids(), runContext.getNetworkUuid(), runContext.getVariantId())
);
runContext.setContingencies(contingencies);
if (contingencies.stream().allMatch(contingencyInfos -> contingencyInfos.getContingency() == null)) {
logNoContingencies(runContext);
return;
}
} catch (IllegalArgumentException e) {
throw new SecurityAnalysisException(SecurityAnalysisBusinessErrorCode.CONTINGENCY_LIST_CONFIG_EMPTY, "The configuration does not contain any contingency.");
} catch (HttpClientErrorException.NotFound e) {
Expand Down Expand Up @@ -290,4 +295,11 @@ private void logContingencyEquipmentsNotConnected(SecurityAnalysisRunContext run
});
}

private void logNoContingencies(SecurityAnalysisRunContext runContext) {
runContext.getReportNode().newReportNode()
.withMessageTemplate("security.analysis.server.noContingency")
.withSeverity(TypedValue.WARN_SEVERITY)
.add();
}

}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
security.analysis.server.notFoundEquipments = Equipments not found
security.analysis.server.contingencyEquipmentNotFound = Cannot find the following equipments ${elementsIds} in contingency ${contingencyId}
security.analysis.server.notConnectedEquipments = Equipments not connected
security.analysis.server.noContingency = The configuration does not contain any contingency.
security.analysis.server.contingencyEquipmentNotConnected = The following equipments ${elementsIds} in contingency ${contingencyId} are not connected
Loading