Skip to content

ReportNode internationalization (I18n) #183

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

Merged
merged 7 commits into from
Jul 8, 2025
Merged
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
4 changes: 4 additions & 0 deletions balances-adjustment/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@
<groupId>com.powsybl</groupId>
<artifactId>powsybl-commons</artifactId>
</dependency>
<dependency>
<groupId>com.powsybl</groupId>
<artifactId>powsybl-entsoe-commons</artifactId>
</dependency>
<dependency>
<groupId>com.powsybl</groupId>
<artifactId>powsybl-iidm-extensions</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,11 @@ public CompletableFuture<BalanceComputationResult> run(Network network, String w
context.setIterationReportNode(iterationReportNode);

// Step 1: Perform the scaling
ReportNode scalingReportNode = iterationReportNode.newReportNode().withMessageTemplate("scaling", "Scaling").add();
ReportNode scalingReportNode = Reports.createScalingReporter(iterationReportNode);
context.getBalanceOffsets().forEach((area, offset) -> {
Scalable scalable = area.getScalable();
double done = scalable.scale(network, offset, parameters.getScalingParameters());
Reports.reportScaling(scalingReportNode, area.getName(), offset, done);
Reports.reportAreaScaling(scalingReportNode, area.getName(), offset, done);
LOGGER.info("Iteration={}, Scaling for area {}: offset={}, done={}", context.getIterationNum(), area.getName(), offset, done);
});

Expand All @@ -108,15 +108,12 @@ public CompletableFuture<BalanceComputationResult> run(Network network, String w
}
} else {
// Report that LoadFlow was skipped
iterationReportNode.newReportNode()
.withMessageTemplate("skipLoadFlow", "Load flow computation skipped")
.withSeverity(TypedValue.INFO_SEVERITY)
.add();
Reports.createSkipLoadFlowReport(iterationReportNode);
LOGGER.info("Iteration={}, LoadFlow computation skipped as per configuration", context.getIterationNum());
}

// Step 3: Compute balance and mismatch for each area
ReportNode mismatchReportNode = iterationReportNode.newReportNode().withMessageTemplate("mismatch", "Mismatch").add();
ReportNode mismatchReportNode = Reports.createMismatchReporter(iterationReportNode);
for (BalanceComputationArea area : areas) {
NetworkArea na = context.getNetworkArea(area);
double target = area.getTargetNetPosition();
Expand All @@ -139,7 +136,7 @@ public CompletableFuture<BalanceComputationResult> run(Network network, String w
}
} while (context.getIterationNum() < parameters.getMaxNumberIterations() && result.getStatus() != BalanceComputationResult.Status.SUCCESS);

ReportNode statusReportNode = reportNode.newReportNode().withMessageTemplate("status", "Status").add();
ReportNode statusReportNode = Reports.createStatusReporter(reportNode);
if (result.getStatus() == BalanceComputationResult.Status.SUCCESS) {
List<String> networkAreasName = areas.stream()
.map(BalanceComputationArea::getName).collect(Collectors.toList());
Expand Down Expand Up @@ -193,7 +190,7 @@ protected boolean isLoadFlowResultOk(BalanceComputationRunningContext context, f
return false;
}
final var cr = list.get(0);
ReportNode lfStatusReportNode = context.getIterationReportNode().newReportNode().withMessageTemplate("loadFlowStatus", "Checking load flow status").add();
ReportNode lfStatusReportNode = Reports.createLoadFlowStatusReporter(context.getIterationReportNode());
final var severity = cr.getStatus() == LoadFlowResult.ComponentResult.Status.CONVERGED ? TypedValue.INFO_SEVERITY : TypedValue.ERROR_SEVERITY;
Reports.reportLfStatus(lfStatusReportNode, cr.getConnectedComponentNum(), cr.getSynchronousComponentNum(), cr.getStatus().name(), severity);
return cr.getStatus() == LoadFlowResult.ComponentResult.Status.CONVERGED;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ public final class Reports {
private Reports() {
}

public static void reportScaling(ReportNode reportNode, String areaName, double offset, double done) {
reportNode.newReportNode().withMessageTemplate("areaScaling",
"Scaling for area ${areaName}: offset=${offset}, done=${done}")
public static void reportAreaScaling(ReportNode reportNode, String areaName, double offset, double done) {
reportNode.newReportNode()
.withMessageTemplate("entsoe.balances_adjustment.areaScaling")
.withUntypedValue(AREA_NAME, areaName)
.withUntypedValue("offset", offset)
.withUntypedValue("done", done)
Expand All @@ -34,8 +34,8 @@ public static void reportScaling(ReportNode reportNode, String areaName, double
}

public static void reportLfStatus(ReportNode reportNode, int networkNumCc, int networkNumSc, String status, TypedValue severity) {
reportNode.newReportNode().withMessageTemplate("lfStatus",
"Network CC${networkNumCc} SC${networkNumSc} Load flow complete with status '${status}'")
reportNode.newReportNode()
.withMessageTemplate("entsoe.balances_adjustment.lfStatus")
.withUntypedValue("networkNumCc", networkNumCc)
.withUntypedValue("networkNumSc", networkNumSc)
.withUntypedValue("status", status)
Expand All @@ -44,8 +44,8 @@ public static void reportLfStatus(ReportNode reportNode, int networkNumCc, int n
}

public static void reportAreaMismatch(ReportNode reportNode, String areaName, double mismatch, double target, double balance) {
reportNode.newReportNode().withMessageTemplate("areaMismatch",
"Mismatch for area ${areaName}: ${mismatch} (target=${target}, balance=${balance})")
reportNode.newReportNode()
.withMessageTemplate("entsoe.balances_adjustment.areaMismatch")
.withUntypedValue(AREA_NAME, areaName)
.withUntypedValue("mismatch", mismatch)
.withUntypedValue("target", target)
Expand All @@ -55,26 +55,58 @@ public static void reportAreaMismatch(ReportNode reportNode, String areaName, do
}

public static void reportBalancedAreas(ReportNode reportNode, List<String> networkAreasName, int iterationCount) {
reportNode.newReportNode().withMessageTemplate("balancedAreas",
"Areas ${networkAreasName} are balanced after ${iterationCount} iterations")
reportNode.newReportNode()
.withMessageTemplate("entsoe.balances_adjustment.balancedAreas")
.withUntypedValue("networkAreasName", networkAreasName.toString())
.withUntypedValue("iterationCount", iterationCount)
.withSeverity(TypedValue.INFO_SEVERITY)
.add();
}

public static void reportUnbalancedAreas(ReportNode reportNode, int iteration, BigDecimal totalMismatch) {
reportNode.newReportNode().withMessageTemplate("unbalancedAreas",
"Areas are unbalanced after ${iteration} iterations, total mismatch is ${totalMismatch}")
reportNode.newReportNode()
.withMessageTemplate("entsoe.balances_adjustment.unbalancedAreas")
.withUntypedValue(ITERATION, iteration)
.withUntypedValue("totalMismatch", totalMismatch.toString())
.withSeverity(TypedValue.ERROR_SEVERITY)
.add();
}

public static ReportNode createBalanceComputationIterationReporter(ReportNode reportNode, int iteration) {
return reportNode.newReportNode().withMessageTemplate("balanceComputation", "Balance Computation iteration '${iteration}'")
return reportNode.newReportNode()
.withMessageTemplate("entsoe.balances_adjustment.balanceComputation")
.withUntypedValue(ITERATION, iteration)
.add();
}

public static ReportNode createStatusReporter(ReportNode reportNode) {
return reportNode.newReportNode()
.withMessageTemplate("entsoe.balances_adjustment.status")
.add();
}

public static ReportNode createMismatchReporter(ReportNode iterationReportNode) {
return iterationReportNode.newReportNode()
.withMessageTemplate("entsoe.balances_adjustment.mismatch")
.add();
}

public static ReportNode createScalingReporter(ReportNode iterationReportNode) {
return iterationReportNode.newReportNode()
.withMessageTemplate("entsoe.balances_adjustment.scaling")
.add();
}

public static ReportNode createLoadFlowStatusReporter(ReportNode reportNode) {
return reportNode.newReportNode()
.withMessageTemplate("entsoe.balances_adjustment.loadFlowStatus")
.add();
}

public static void createSkipLoadFlowReport(ReportNode reportNode) {
reportNode.newReportNode()
.withMessageTemplate("entsoe.balances_adjustment.skipLoadflow")
.withSeverity(TypedValue.INFO_SEVERITY)
.add();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import com.powsybl.commons.report.ReportNode;
import com.powsybl.computation.ComputationManager;
import com.powsybl.computation.local.LocalComputationManager;
import com.powsybl.entsoe.commons.PowsyblEntsoeReportResourceBundle;
import com.powsybl.iidm.modification.scalable.Scalable;
import com.powsybl.iidm.network.*;
import com.powsybl.loadflow.*;
Expand Down Expand Up @@ -48,6 +49,7 @@ class BalanceComputationSimpleDcTest {
private Branch branchFrBe2;
private String initialState = "InitialState";
private String initialVariantNew = "InitialVariantNew";
private static final String TEST_BASE_NAME = "i18n.reports";

@BeforeEach
void setUp() {
Expand Down Expand Up @@ -238,7 +240,7 @@ void testSkipLoadFlowWithReportNode() throws IOException {

BalanceComputation balanceComputation = balanceComputationFactory.create(areas, loadFlowRunner, computationManager);

ReportNode reportNode = ReportNode.newRootReportNode().withMessageTemplate("testSkipLoadFlow", "Test skip load flow").build();
ReportNode reportNode = ReportNode.newRootReportNode().withResourceBundles(TEST_BASE_NAME, PowsyblEntsoeReportResourceBundle.BASE_NAME).withMessageTemplate("testSkipLoadFlow").build();
BalanceComputationResult result = balanceComputation.run(simpleNetwork, simpleNetwork.getVariantManager().getWorkingVariantId(), parameters, reportNode).join();

// Check that the report contains information about skipping load flow
Expand Down Expand Up @@ -338,7 +340,10 @@ void testBalancedNetworkAfter1ScalingReport() throws IOException {

BalanceComputation balanceComputation = balanceComputationFactory.create(areas, loadFlowRunner, computationManager);

ReportNode reportNode = ReportNode.newRootReportNode().withMessageTemplate("testBalancedNetworkReport", "Test balanced network report").build();
ReportNode reportNode = ReportNode.newRootReportNode()
.withResourceBundles(TEST_BASE_NAME, PowsyblEntsoeReportResourceBundle.BASE_NAME)
.withMessageTemplate("testBalancedNetworkReport")
.build();
balanceComputation.run(simpleNetwork, simpleNetwork.getVariantManager().getWorkingVariantId(), parameters, reportNode).join();
BalanceComputationAssert.assertReportEquals("/balancedNetworkReport.txt", reportNode);
}
Expand All @@ -351,7 +356,10 @@ void testUnBalancedNetworkReport() throws IOException {

BalanceComputation balanceComputation = balanceComputationFactory.create(areas, loadFlowRunner, computationManager);

ReportNode reportNode = ReportNode.newRootReportNode().withMessageTemplate("testUnbalancedNetworkReport", "Test unbalanced network report").build();
ReportNode reportNode = ReportNode.newRootReportNode()
.withResourceBundles(TEST_BASE_NAME, PowsyblEntsoeReportResourceBundle.BASE_NAME)
.withMessageTemplate("testUnbalancedNetworkReport")
.build();
balanceComputation.run(simpleNetwork, simpleNetwork.getVariantManager().getWorkingVariantId(), parameters, reportNode).join();
BalanceComputationAssert.assertReportEquals("/unbalancedNetworkReport.txt", reportNode);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
testBalancedNetworkReport = Test balanced network report
testUnbalancedNetworkReport = Test unbalanced network report
testSkipLoadFlow = Test skip load flow
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.powsybl.entsoe.commons;

/**
* Copyright (c) 2025, 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
*/

import com.google.auto.service.AutoService;
import com.powsybl.commons.report.ReportResourceBundle;

/**
* @author Alice Caron {@literal <alice.caron at rte-france.com>}
*/

@AutoService(ReportResourceBundle.class)
public final class PowsyblEntsoeReportResourceBundle implements ReportResourceBundle {

public static final String BASE_NAME = "com.powsybl.entsoe.commons.reports";

public String getBaseName() {
return BASE_NAME;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
entsoe.balances_adjustment.areaMismatch = Mismatch for area ${areaName}: ${mismatch} (target=${target}, balance=${balance})
entsoe.balances_adjustment.areaScaling = Scaling for area ${areaName}: offset=${offset}, done=${done}
entsoe.balances_adjustment.balanceComputation = Balance Computation iteration '${iteration}'
entsoe.balances_adjustment.balancedAreas = Areas ${networkAreasName} are balanced after ${iterationCount} iterations
entsoe.balances_adjustment.lfStatus = Network CC${networkNumCc} SC${networkNumSc} Load flow complete with status '${status}'
entsoe.balances_adjustment.loadFlowStatus = Checking load flow status
entsoe.balances_adjustment.mismatch = Mismatch
entsoe.balances_adjustment.scaling = Scaling
entsoe.balances_adjustment.skipLoadflow = Load flow computation skipped
entsoe.balances_adjustment.status = Status
entsoe.balances_adjustment.unbalancedAreas = Areas are unbalanced after ${iteration} iterations, total mismatch is ${totalMismatch}
entsoe.glsk.connectedToAnIsland = GLSK node is connected to an island
entsoe.glsk.nodeNotFound = GLSK node is not found in CGM
entsoe.glsk.noRunningGeneratorOrLoad = GLSK node is present but has no running Generator or Load
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/**
* Copyright (c) 2025, 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.glsk.commons;

import com.powsybl.commons.report.ReportNode;

import static com.powsybl.commons.report.TypedValue.WARN_SEVERITY;

/**
* @author Olivier Perrin {@literal <olivier.perrin at rte-france.com>}
*/
public final class GlskReports {

public static final String NODE_ID_KEY = "NodeId";
public static final String TYPE_KEY = "Type";
public static final String TSO_KEY = "TSO";

private GlskReports() {
}

public static void reportNodeNotFound(String nodeId, String type, String tso, ReportNode reportNode) {
reportNode.newReportNode()
.withMessageTemplate("entsoe.glsk.nodeNotFound")
.withTypedValue(NODE_ID_KEY, nodeId, "")
.withTypedValue(TYPE_KEY, type, "")
.withTypedValue(TSO_KEY, tso, "")
.withSeverity(WARN_SEVERITY)
.add();
}

public static void reportNoRunningGeneratorOrLoad(String nodeId, String type, String tso, ReportNode reportNode) {
reportNode.newReportNode()
.withMessageTemplate("entsoe.glsk.noRunningGeneratorOrLoad")
.withTypedValue(NODE_ID_KEY, nodeId, "")
.withTypedValue(TYPE_KEY, type, "")
.withTypedValue(TSO_KEY, tso, "")
.withSeverity(WARN_SEVERITY)
.add();
}

public static void reportConnectedToAnIsland(String nodeId, String type, String tso, ReportNode reportNode) {
reportNode.newReportNode()
.withMessageTemplate("entsoe.glsk.connectedToAnIsland")
.withTypedValue(NODE_ID_KEY, nodeId, "")
.withTypedValue(TYPE_KEY, type, "")
.withTypedValue(TSO_KEY, tso, "")
.withSeverity(WARN_SEVERITY)
.add();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import java.util.Map;
import java.util.stream.Collectors;

import static com.powsybl.commons.report.TypedValue.WARN_SEVERITY;
import static com.powsybl.glsk.commons.GlskReports.*;

/**
* @author Marc Erkol {@literal <marc.erkol at rte-france.com>}
Expand All @@ -30,12 +30,6 @@ class GlskQualityCheck {

private static final String LOAD = "A05";

public static final String NODE_ID_KEY = "NodeId";

public static final String TYPE_KEY = "Type";

public static final String TSO_KEY = "TSO";

public static void gskQualityCheck(GlskQualityCheckInput input, ReportNode reportNode) {
new GlskQualityCheck().generateReport(input, reportNode);
}
Expand Down Expand Up @@ -110,31 +104,15 @@ private void createMissingLoad(Network network, VoltageLevel voltageLevel, Strin

private void checkResource(GlskRegisteredResource registeredResource, Injection<?> injection, String type, Network network, String tso, ReportNode reportNode) {
if (injection == null) {

if (network.getBusBreakerView().getBus(registeredResource.getmRID()) == null) {
reportNode.newReportNode().withMessageTemplate("1", "GLSK node is not found in CGM")
.withTypedValue(NODE_ID_KEY, registeredResource.getmRID(), "")
.withTypedValue(TYPE_KEY, type, "")
.withTypedValue(TSO_KEY, tso, "")
.withSeverity(WARN_SEVERITY)
.add();
reportNodeNotFound(registeredResource.getmRID(), type, tso, reportNode);
} else {
reportNode.newReportNode().withMessageTemplate("2", "GLSK node is present but has no running Generator or Load")
.withTypedValue(NODE_ID_KEY, registeredResource.getmRID(), "")
.withTypedValue(TYPE_KEY, type, "")
.withTypedValue(TSO_KEY, tso, "")
.withSeverity(WARN_SEVERITY)
.add();
reportNoRunningGeneratorOrLoad(registeredResource.getmRID(), type, tso, reportNode);
}
} else {
if (!injection.getTerminal().isConnected()
|| !injection.getTerminal().getBusBreakerView().getBus().isInMainSynchronousComponent()) {
reportNode.newReportNode().withMessageTemplate("3", "GLSK node is connected to an island")
.withTypedValue(NODE_ID_KEY, registeredResource.getmRID(), "")
.withTypedValue(TYPE_KEY, type, "")
.withTypedValue(TSO_KEY, tso, "")
.withSeverity(WARN_SEVERITY)
.add();
reportConnectedToAnIsland(registeredResource.getmRID(), type, tso, reportNode);
}
}
}
Expand Down
Loading