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

change and add some logs in generator dispatch modification #332

Merged
merged 6 commits into from
Sep 26, 2023
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
Original file line number Diff line number Diff line change
Expand Up @@ -148,53 +148,69 @@ private static Double getGeneratorMarginalCost(Generator generator) {
return null;
}

private static List<Generator> computeAdjustableGenerators(Network network, Component component, List<String> generatorsWithFixedSupply,
List<SubstationsGeneratorsOrderingInfos> substationsGeneratorsOrderingInfos,
Reporter reporter) {
List<Generator> generatorsWithMarginalCost;

// get all generators in the component
List<Generator> generators = component.getBusStream().flatMap(Bus::getGeneratorStream).collect(Collectors.toList());

// remove generators with fixed supply
generators.removeIf(generator -> generatorsWithFixedSupply.contains(generator.getId()));
private static Map<Double, List<String>> getGeneratorsByMarginalCost(List<Generator> generators, Reporter reporter, String reporterSuffixKey) {
Map<Double, List<String>> generatorsByMarginalCost = new TreeMap<>();

// set targetP to 0
generators.forEach(generator -> generator.setTargetP(0.));

// get generators with marginal cost
generatorsWithMarginalCost = generators.stream().filter(generator -> {
Double marginalCost = getGeneratorMarginalCost(generator);
if (marginalCost == null) {
report(reporter, Integer.toString(component.getNum()), "MissingMarginalCostForGenerator", "The generator ${generator} does not have a marginal cost",
Map.of(GENERATOR, generator.getId()), TypedValue.WARN_SEVERITY);
}
return marginalCost != null;
}).collect(Collectors.toList());
List<Generator> generatorsWithMarginalCost = generators.stream()
.filter(generator -> getGeneratorMarginalCost(generator) != null)
.collect(Collectors.toList());
int nbNoCost = generators.size() - generatorsWithMarginalCost.size();
if (nbNoCost > 0) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Add this synthetic report before each individual generator without marginal cost report ?

report(reporter, reporterSuffixKey, "NbGeneratorsWithNoCost", "${nbNoCost} generator${isPlural} been discarded from generation dispatch because of missing marginal cost. Their active power set point has been set to 0",
Map.of("nbNoCost", nbNoCost,
"isPlural", nbNoCost > 1 ? "s have" : " has"),
Copy link
Contributor

Choose a reason for hiding this comment

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

"isPlural" is used for 5 times in this file, use a constant will be better ?

TypedValue.WARN_SEVERITY);
}
generators.stream()
.filter(generator -> getGeneratorMarginalCost(generator) == null)
.forEach(g -> report(reporter, reporterSuffixKey, "MissingMarginalCostForGenerator", "The generator ${generator} does not have a marginal cost",
Map.of(GENERATOR, g.getId()), TypedValue.TRACE_SEVERITY)
);

// build map of generators by marginal cost
generatorsWithMarginalCost.sort(Comparator.comparing(GenerationDispatch::getGeneratorMarginalCost));
Map<Double, List<String>> generatorsByMarginalCost = new TreeMap<>();
generatorsWithMarginalCost.forEach(g -> {
Double marginalCost = getGeneratorMarginalCost(g);
generatorsByMarginalCost.computeIfAbsent(marginalCost, k -> new ArrayList<>());
generatorsByMarginalCost.get(marginalCost).add(g.getId());
});

List<String> generatorsToReturn = new ArrayList<>();
return generatorsByMarginalCost;
}

// log substations not found
private static void reportUnknownSubstations(Network network, List<SubstationsGeneratorsOrderingInfos> substationsGeneratorsOrderingInfos, Reporter reporter, String reporterSuffixKey) {
if (!CollectionUtils.isEmpty(substationsGeneratorsOrderingInfos)) {
substationsGeneratorsOrderingInfos.forEach(sInfo ->
sInfo.getSubstationIds().forEach(sId -> {
Substation substation = network.getSubstation(sId);
if (substation == null) {
report(reporter, Integer.toString(component.getNum()), "SubstationNotFound", "Substation ${substation} not found",
Map.of(SUBSTATION, sId), TypedValue.WARN_SEVERITY);
}
}));
sInfo.getSubstationIds().forEach(sId -> {
Substation substation = network.getSubstation(sId);
if (substation == null) {
report(reporter, reporterSuffixKey, "SubstationNotFound", "Substation ${substation} not found",
Map.of(SUBSTATION, sId), TypedValue.WARN_SEVERITY);
}
}));
}
}

private static List<Generator> computeAdjustableGenerators(Network network, Component component, List<String> generatorsWithFixedSupply,
List<SubstationsGeneratorsOrderingInfos> substationsGeneratorsOrderingInfos,
Reporter reporter) {
List<String> generatorsToReturn = new ArrayList<>();
String reporterSuffixKey = Integer.toString(component.getNum());

// log substations not found
reportUnknownSubstations(network, substationsGeneratorsOrderingInfos, reporter, reporterSuffixKey);

// get all connected generators in the component
List<Generator> generators = component.getBusStream().flatMap(Bus::getGeneratorStream).collect(Collectors.toList());

// remove generators with fixed supply
generators.removeIf(generator -> generatorsWithFixedSupply.contains(generator.getId()));

Map<Double, List<String>> generatorsByMarginalCost = getGeneratorsByMarginalCost(generators, reporter, reporterSuffixKey);
generatorsByMarginalCost.forEach((mCost, gList) -> { // loop on generators of same cost
if (!CollectionUtils.isEmpty(substationsGeneratorsOrderingInfos)) { // substations hierarchy provided
// build mapGeneratorsBySubstationsList, that will contain all the generators with the same marginal cost as mCost contained in each list of substations
Expand Down Expand Up @@ -247,7 +263,7 @@ private static List<Generator> computeAdjustableGenerators(Network network, Comp
});

if (generatorsToReturn.isEmpty()) {
report(reporter, Integer.toString(component.getNum()), "NoAvailableAdjustableGenerator", "There is no adjustable generator",
report(reporter, reporterSuffixKey, "NoAvailableAdjustableGenerator", "There is no adjustable generator",
Map.of(), TypedValue.WARN_SEVERITY);
}

Expand All @@ -257,6 +273,7 @@ private static List<Generator> computeAdjustableGenerators(Network network, Comp
private static class GeneratorTargetPListener extends DefaultNetworkListener {
private final Reporter reporter;
private final String suffixKey;
private final List<Generator> updatedGenerators = new ArrayList<>();

GeneratorTargetPListener(Reporter reporter, String suffixKey) {
this.reporter = reporter;
Expand All @@ -265,11 +282,29 @@ private static class GeneratorTargetPListener extends DefaultNetworkListener {

@Override
public void onUpdate(Identifiable identifiable, String attribute, String variantId, Object oldValue, Object newValue) {
if (identifiable.getType() == IdentifiableType.GENERATOR &&
attribute.equals("targetP") &&
Double.compare((double) oldValue, (double) newValue) != 0) {
report(reporter, suffixKey, "GeneratorSetTargetP", "Generator ${generator} targetP : ${oldValue} MW --> ${newValue} MW",
Map.of(GENERATOR, identifiable.getId(), "oldValue", oldValue, "newValue", newValue), TypedValue.INFO_SEVERITY);
if (identifiable.getType() == IdentifiableType.GENERATOR && attribute.equals("targetP") && Double.compare((double) oldValue, (double) newValue) != 0) {
updatedGenerators.add((Generator) identifiable);
}
}

public void endReport(List<Generator> adjustableGenerators) {
// report updated generators
report(reporter, suffixKey, "TotalGeneratorSetTargetP", "The active power set points of ${nbUpdatedGenerator} generator${isPlural} have been updated as a result of generation dispatch",
Map.of("nbUpdatedGenerator", updatedGenerators.size(), "isPlural", updatedGenerators.size() > 1 ? "s" : ""), TypedValue.INFO_SEVERITY);
updatedGenerators.forEach(g -> report(reporter, suffixKey, "GeneratorSetTargetP", "The active power set point of generator ${generator} has been set to ${newValue} MW",
Map.of(GENERATOR, g.getId(), "newValue", g.getTargetP()), TypedValue.TRACE_SEVERITY));

// report unchanged generators
int nbUnchangedGenerators = adjustableGenerators.size() - updatedGenerators.size();
if (nbUnchangedGenerators > 0) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Add this synthetic report before each individual unchanged generator report ?

Copy link
Contributor Author

@dbraquart dbraquart Sep 25, 2023

Choose a reason for hiding this comment

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

also made for updated generators (synthetic before details)

List<String> updatedGeneratorsIds = updatedGenerators.stream().map(Identifiable::getId).toList();
report(reporter, suffixKey, "TotalGeneratorUnchangedTargetP", "${nbUnchangedGenerator} eligible generator${isPlural} not been selected by the merit order algorithm. Their active power set point has been set to 0",
Map.of("nbUnchangedGenerator", nbUnchangedGenerators,
"isPlural", nbUnchangedGenerators > 1 ? "s have" : " has"), TypedValue.INFO_SEVERITY);
adjustableGenerators.stream()
.filter(g -> !updatedGeneratorsIds.contains(g.getId()))
.forEach(g -> report(reporter, suffixKey, "GeneratorUnchangedTargetP", "Generator ${generator} has not been selected by the merit order algorithm. Its active power set point has been set to 0",
Map.of(GENERATOR, g.getId()), TypedValue.TRACE_SEVERITY));
}
}
}
Expand Down Expand Up @@ -379,13 +414,41 @@ private double reduceGeneratorMaxPValue(Generator generator,
return Math.max(generator.getMinP(), res * (1. - genFrequencyReserve / 100.));
}

private void reportDisconnectedGenerators(List<Generator> globalDisconnectedGenerators, int componentNum, Reporter reporter) {
List<Generator> componentDisconnectedGenerators = globalDisconnectedGenerators.stream()
.filter(g -> g.getTerminal().getBusView() != null && g.getTerminal().getBusView().getConnectableBus() != null &&
g.getTerminal().getBusView().getConnectableBus().getSynchronousComponent().getNum() == componentNum)
.toList();
if (!componentDisconnectedGenerators.isEmpty()) {
report(reporter, Integer.toString(componentNum), "TotalDisconnectedGenerator", "${nbDisconnectedGenerator} generator${isPlural} been discarded from generation dispatch because their are disconnected. Their active power set point remains unchanged",
Copy link
Contributor

Choose a reason for hiding this comment

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

Add the synthetic report before each individual disconnected generator report ?

Map.of("nbDisconnectedGenerator", componentDisconnectedGenerators.size(),
"isPlural", componentDisconnectedGenerators.size() > 1 ? "s have" : " has"),
TypedValue.INFO_SEVERITY);
componentDisconnectedGenerators.forEach(g ->
report(reporter, Integer.toString(componentNum), "DisconnectedGenerator", "Generator ${generator} has been discarded from generation dispatch because it is disconnected. Its active power set point remains unchanged",
Map.of(GENERATOR, g.getId()), TypedValue.TRACE_SEVERITY)
);
}
}

@Override
public void apply(Network network, Reporter subReporter) {
Collection<Component> synchronousComponents = network.getBusView().getBusStream()
.filter(Bus::isInMainConnectedComponent)
.map(Bus::getSynchronousComponent)
.collect(collectingAndThen(toCollection(() -> new TreeSet<>(comparingInt(Component::getNum))), ArrayList::new));

report(subReporter, "", "NbSynchronousComponents", "Network has ${scNumber} synchronous component${isPlural}: ${scList}",
Map.of("scNumber", synchronousComponents.size(),
"isPlural", synchronousComponents.size() > 1 ? "s" : "",
"scList", synchronousComponents.stream().map(sc -> "SC" + sc.getNum()).collect(Collectors.joining(", "))),
TypedValue.INFO_SEVERITY);

// all disconnected generators at network level (for report purpose)
List<Generator> disconnectedGenerators = network.getGeneratorStream()
.filter(g -> !g.getTerminal().isConnected())
.toList();

// get generators for which there will be no reduction of maximal power
List<String> generatorsWithoutOutage = collectGeneratorsWithoutOutage(network, subReporter);

Expand All @@ -402,6 +465,9 @@ public void apply(Network network, Reporter subReporter) {

Reporter powerToDispatchReporter = componentReporter.createSubReporter(POWER_TO_DISPATCH, POWER_TO_DISPATCH);

// log disconnected generators attached to this synchronous component
reportDisconnectedGenerators(disconnectedGenerators, componentNum, powerToDispatchReporter);

// get total value of connected loads in the connected component
double totalDemand = computeTotalDemand(component, generationDispatchInfos.getLossCoefficient());
report(powerToDispatchReporter, Integer.toString(componentNum), "TotalDemand", "The total demand is : ${totalDemand} MW",
Expand Down Expand Up @@ -449,6 +515,7 @@ public void apply(Network network, Reporter subReporter) {
Scalable scalable = Scalable.stack(generatorsScalable.toArray(Scalable[]::new));
realized = scalable.scale(network, totalAmountSupplyToBeDispatched, new ScalingParameters().setAllowsGeneratorOutOfActivePowerLimits(true));

listener.endReport(adjustableGenerators);
network.removeListener(listener);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -537,19 +537,19 @@ public void testGenerationDispatchWithMaxValueLessThanMinP() throws Exception {
assertLogMessage("The total amount of fixed supply is : 0.0 MW", "TotalAmountFixedSupply" + firstSynchronousComponentNum, reportService);
assertLogMessage("The HVDC balance is : 90.0 MW", "TotalOutwardHvdcFlow" + firstSynchronousComponentNum, reportService);
assertLogMessage("The total amount of supply to be dispatched is : 438.0 MW", "TotalAmountSupplyToBeDispatched" + firstSynchronousComponentNum, reportService);
assertLogNthMessage("Generator TEST1 targetP : 0.0 MW --> 40.375 MW", "GeneratorSetTargetP" + firstSynchronousComponentNum, reportService, 1);
assertLogNthMessage("Generator GTH1 targetP : 0.0 MW --> 80.0 MW", "GeneratorSetTargetP" + firstSynchronousComponentNum, reportService, 2);
assertLogNthMessage("Generator GTH2 targetP : 0.0 MW --> 146.0 MW", "GeneratorSetTargetP" + firstSynchronousComponentNum, reportService, 3);
assertLogNthMessage("The active power set point of generator TEST1 has been set to 40.375 MW", "GeneratorSetTargetP" + firstSynchronousComponentNum, reportService, 1);
assertLogNthMessage("The active power set point of generator GTH1 has been set to 80.0 MW", "GeneratorSetTargetP" + firstSynchronousComponentNum, reportService, 2);
assertLogNthMessage("The active power set point of generator GTH2 has been set to 146.0 MW", "GeneratorSetTargetP" + firstSynchronousComponentNum, reportService, 3);
assertLogMessage("The supply-demand balance could not be met : the remaining power imbalance is 171.625 MW", "SupplyDemandBalanceCouldNotBeMet" + firstSynchronousComponentNum, reportService);
int secondSynchronousComponentNum = getNetwork().getGenerator(GH1_ID).getTerminal().getBusView().getBus().getSynchronousComponent().getNum(); // GH1 is in second synchronous component
assertLogMessage("The total demand is : 240.0 MW", "TotalDemand" + secondSynchronousComponentNum, reportService);
assertLogMessage("The total amount of fixed supply is : 0.0 MW", "TotalAmountFixedSupply" + secondSynchronousComponentNum, reportService);
assertLogMessage("The HVDC balance is : -90.0 MW", "TotalOutwardHvdcFlow" + secondSynchronousComponentNum, reportService);
assertLogMessage("The total amount of supply to be dispatched is : 330.0 MW", "TotalAmountSupplyToBeDispatched" + secondSynchronousComponentNum, reportService);
assertLogNthMessage("Generator GH1 targetP : 0.0 MW --> 80.0 MW", "GeneratorSetTargetP" + secondSynchronousComponentNum, reportService, 1);
assertLogNthMessage("Generator GH2 targetP : 0.0 MW --> 60.0 MW", "GeneratorSetTargetP" + secondSynchronousComponentNum, reportService, 2);
assertLogNthMessage("Generator GH3 targetP : 0.0 MW --> 126.1 MW", "GeneratorSetTargetP" + secondSynchronousComponentNum, reportService, 3);
assertLogNthMessage("Generator ABC targetP : 0.0 MW --> 63.900000000000006 MW", "GeneratorSetTargetP" + secondSynchronousComponentNum, reportService, 4);
assertLogNthMessage("The active power set point of generator GH1 has been set to 80.0 MW", "GeneratorSetTargetP" + secondSynchronousComponentNum, reportService, 1);
assertLogNthMessage("The active power set point of generator GH2 has been set to 60.0 MW", "GeneratorSetTargetP" + secondSynchronousComponentNum, reportService, 2);
assertLogNthMessage("The active power set point of generator GH3 has been set to 126.1 MW", "GeneratorSetTargetP" + secondSynchronousComponentNum, reportService, 3);
assertLogNthMessage("The active power set point of generator ABC has been set to 63.900000000000006 MW", "GeneratorSetTargetP" + secondSynchronousComponentNum, reportService, 4);
assertLogMessage("The supply-demand balance could be met", "SupplyDemandBalanceCouldBeMet" + secondSynchronousComponentNum, reportService);

wireMockUtils.verifyGetRequest(stubIdForPmaxReduction, PATH, handleQueryParams(getNetworkUuid(), getGeneratorsWithoutOutageFilters123().stream().map(FilterEquipments::getFilterId).collect(Collectors.toList())), false);
Expand Down
Loading