Skip to content

Commit

Permalink
Merge branch 'main' into fix_terminal_modification_2wt
Browse files Browse the repository at this point in the history
  • Loading branch information
flomillot authored Sep 27, 2023
2 parents 5cebe06 + d807f4c commit 2059292
Show file tree
Hide file tree
Showing 2 changed files with 108 additions and 41 deletions.
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) {
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"),
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) {
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",
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

0 comments on commit 2059292

Please sign in to comment.