Skip to content

Commit

Permalink
explicit creation of default control area of type interchange
Browse files Browse the repository at this point in the history
Signed-off-by: Luma <zamarrenolm@aia.es>
  • Loading branch information
zamarrenolm committed Dec 16, 2024
1 parent cc60a53 commit 4f9d471
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 66 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,7 @@
import com.powsybl.cgmes.conversion.naming.NamingStrategy;
import com.powsybl.cgmes.conversion.naming.NamingStrategyFactory;
import com.powsybl.cgmes.extensions.CgmesMetadataModels;
import com.powsybl.cgmes.model.CgmesMetadataModel;
import com.powsybl.cgmes.model.CgmesMetadataModelImpl;
import com.powsybl.cgmes.model.CgmesNamespace;
import com.powsybl.cgmes.model.CgmesSubset;
import com.powsybl.cgmes.model.*;
import com.powsybl.commons.config.PlatformConfig;
import com.powsybl.commons.datasource.DataSource;
import com.powsybl.commons.exceptions.UncheckedXmlStreamException;
Expand All @@ -28,6 +25,7 @@
import com.powsybl.commons.xml.XmlUtil;
import com.powsybl.iidm.network.*;
import com.powsybl.triplestore.api.PropertyBag;
import com.powsybl.triplestore.api.PropertyBags;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -42,6 +40,8 @@
import java.util.stream.Collectors;

import static com.powsybl.cgmes.conversion.CgmesReports.inconsistentProfilesTPRequiredReport;
import static com.powsybl.cgmes.conversion.naming.CgmesObjectReference.Part.CONTROL_AREA;
import static com.powsybl.cgmes.conversion.naming.CgmesObjectReference.refTyped;

/**
* @author Luma Zamarreño {@literal <zamarrenolm at aia.es>}
Expand Down Expand Up @@ -78,6 +78,49 @@ public List<Parameter> getParameters() {
public void export(Network network, Properties parameters, DataSource dataSource, ReportNode reportNode) {
Objects.requireNonNull(network);

CgmesExportContext context = createContext(network, parameters, reportNode);

// Export the network
if (Parameter.readBoolean(getFormat(), parameters, CGM_EXPORT_PARAMETER, defaultValueConfig)) {
exportCGM(network, dataSource, context);
} else {
exportIGM(network, dataSource, context);
}
}

public void createDefaultControlAreaInterchange(Network network) {
createDefaultControlAreaInterchange(network, null);
}

public void createDefaultControlAreaInterchange(Network network, Properties parameters) {
Objects.requireNonNull(network);

CgmesExportContext context = createContext(network, parameters, ReportNode.NO_OP);

String controlAreaId = context.getNamingStrategy().getCgmesId(refTyped(network), CONTROL_AREA);
Area area = network.newArea()
.setAreaType("ControlAreaTypeKind.Interchange")
.setId(controlAreaId)
.setName("Network")
.add();
ReferenceDataProvider referenceDataProvider = context.getReferenceDataProvider();
if (referenceDataProvider != null && referenceDataProvider.getSourcingActor().containsKey(CgmesNames.ENERGY_IDENT_CODE_EIC)) {
area.addAlias(referenceDataProvider.getSourcingActor().get(CgmesNames.ENERGY_IDENT_CODE_EIC), CgmesNames.ENERGY_IDENT_CODE_EIC);
}
double currentInterchange = 0;
Set<String> boundaryDcNodes = getBoundaryDcNodes(referenceDataProvider);
for (DanglingLine danglingLine : CgmesExportUtil.getBoundaryDanglingLines(network)) {
// Our exchange should be referred the boundary
area.newAreaBoundary()
.setAc(isAcBoundary(danglingLine, boundaryDcNodes))
.setBoundary(danglingLine.getBoundary())
.add();
currentInterchange += danglingLine.getBoundary().getP();
}
area.setInterchangeTarget(currentInterchange);
}

private CgmesExportContext createContext(Network network, Properties parameters, ReportNode reportNode) {
// Determine reference data (boundaries, base voltages and other sourcing references) for the export
String sourcingActorName = Parameter.readString(getFormat(), parameters, SOURCING_ACTOR_PARAMETER, defaultValueConfig);
String countryName = getCountry(network);
Expand All @@ -90,12 +133,30 @@ public void export(Network network, Properties parameters, DataSource dataSource
CgmesExportContext context = new CgmesExportContext(network, referenceDataProvider, namingStrategy);
addParametersToContext(context, parameters, reportNode, referenceDataProvider);

// Export the network
if (Parameter.readBoolean(getFormat(), parameters, CGM_EXPORT_PARAMETER, defaultValueConfig)) {
exportCGM(network, dataSource, context);
return context;
}

private Set<String> getBoundaryDcNodes(ReferenceDataProvider referenceDataProvider) {
if (referenceDataProvider == null) {
return Collections.emptySet();
}
PropertyBags boundaryNodes = referenceDataProvider.getBoundaryNodes();
if (boundaryNodes == null) {
return Collections.emptySet();
} else {
exportIGM(network, dataSource, context);
return boundaryNodes.stream()
.filter(node -> node.containsKey("topologicalNodeDescription") && node.get("topologicalNodeDescription").startsWith("HVDC"))
.map(node -> node.getId(CgmesNames.TOPOLOGICAL_NODE))
.collect(Collectors.toSet());
}
}

private boolean isAcBoundary(DanglingLine danglingLine, Set<String> boundaryDcNodes) {
String dlBoundaryNode = danglingLine.getProperty(Conversion.CGMES_PREFIX_ALIAS_PROPERTIES + CgmesNames.TOPOLOGICAL_NODE_BOUNDARY);
if (dlBoundaryNode != null) {
return !boundaryDcNodes.contains(dlBoundaryNode);
}
return true;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import com.powsybl.commons.report.ReportNode;
import com.powsybl.iidm.network.*;
import com.powsybl.iidm.network.Identifiable;
import com.powsybl.triplestore.api.PropertyBags;
import org.apache.commons.lang3.tuple.Pair;

import java.net.URLEncoder;
Expand Down Expand Up @@ -155,6 +154,10 @@ public CgmesExportContext(Network network, ReferenceDataProvider referenceDataPr
}
}

public ReferenceDataProvider getReferenceDataProvider() {
return referenceDataProvider;
}

private CgmesTopologyKind networkTopologyKind(Network network) {
for (VoltageLevel vl : network.getVoltageLevels()) {
if (vl.getTopologyKind().equals(TopologyKind.NODE_BREAKER)) {
Expand All @@ -181,7 +184,6 @@ public void addIidmMappings(Network network) {
addIidmMappingsStaticVarCompensators(network);
addIidmMappingsEndsAndTapChangers(network);
addIidmMappingsEquivalentInjection(network);
addIidmMappingsControlArea(network);
}

private void addIidmMappingsSubstations(Network network) {
Expand Down Expand Up @@ -532,62 +534,6 @@ private void addIidmMappingsEquivalentInjection(Network network) {
}
}

private void addIidmMappingsControlArea(Network network) {
// If no control area exists, create one for the whole network, containing the dangling lines as boundaries,
// but only if the network does not contain subnetworks
long numControlAreas = network.getAreaStream().filter(a -> a.getAreaType().equals("ControlAreaTypeKind.Interchange")).count();
int numSubnetworks = network.getSubnetworks().size();
if (numControlAreas == 0 && numSubnetworks == 0) {
createDefaultControlArea(network);
}
}

private void createDefaultControlArea(Network network) {
String controlAreaId = namingStrategy.getCgmesId(refTyped(network), CONTROL_AREA);
Area area = network.newArea()
.setAreaType("ControlAreaTypeKind.Interchange")
.setId(controlAreaId)
.setName("Network")
.add();
if (referenceDataProvider != null && referenceDataProvider.getSourcingActor().containsKey(CgmesNames.ENERGY_IDENT_CODE_EIC)) {
area.addAlias(referenceDataProvider.getSourcingActor().get(CgmesNames.ENERGY_IDENT_CODE_EIC), CgmesNames.ENERGY_IDENT_CODE_EIC);
}
double currentInterchange = 0;
Set<String> boundaryDcNodes = getBoundaryDcNodes();
for (DanglingLine danglingLine : CgmesExportUtil.getBoundaryDanglingLines(network)) {
// Our exchange should be referred the boundary
area.newAreaBoundary()
.setAc(isAcBoundary(danglingLine, boundaryDcNodes))
.setBoundary(danglingLine.getBoundary())
.add();
currentInterchange += danglingLine.getBoundary().getP();
}
area.setInterchangeTarget(currentInterchange);
}

private Set<String> getBoundaryDcNodes() {
if (referenceDataProvider == null) {
return Collections.emptySet();
}
PropertyBags boundaryNodes = referenceDataProvider.getBoundaryNodes();
if (boundaryNodes == null) {
return Collections.emptySet();
} else {
return boundaryNodes.stream()
.filter(node -> node.containsKey("topologicalNodeDescription") && node.get("topologicalNodeDescription").startsWith("HVDC"))
.map(node -> node.getId(CgmesNames.TOPOLOGICAL_NODE))
.collect(Collectors.toSet());
}
}

private boolean isAcBoundary(DanglingLine danglingLine, Set<String> boundaryDcNodes) {
String dlBoundaryNode = danglingLine.getProperty(Conversion.CGMES_PREFIX_ALIAS_PROPERTIES + CgmesNames.TOPOLOGICAL_NODE_BOUNDARY);
if (dlBoundaryNode != null) {
return !boundaryDcNodes.contains(dlBoundaryNode);
}
return true;
}

public int getCimVersion() {
return cim.getVersion();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1383,6 +1383,13 @@ private static void writeAcdcConverterDCTerminal(String id, String conductingEqu
}

private static void writeControlAreas(String energyAreaId, Network network, String cimNamespace, String euNamespace, XMLStreamWriter writer, CgmesExportContext context) throws XMLStreamException {
// Log a warning if we are a stand-alone network (an IGM) and no control area is being exported
long numControlAreas = network.getAreaStream().filter(a -> a.getAreaType().equals("ControlAreaTypeKind.Interchange")).count();
int numSubnetworks = network.getSubnetworks().size();
if (numControlAreas == 0 && numSubnetworks == 0) {
LOG.warn("No control area of type interchange is being exported");
}

for (Area area : network.getAreas()) {
if (area.getAreaType().equals("ControlAreaTypeKind.Interchange")) {
writeControlArea(area, energyAreaId, cimNamespace, euNamespace, writer, context);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -672,5 +672,34 @@ void testCanGeneratorControl() throws IOException {
}
}

@Test
void networkWithoutControlAreaInterchange() throws IOException {
Network network = DanglingLineNetworkFactory.create();
assertEquals(0, network.getAreaCount());

try (FileSystem fs = Jimfs.newFileSystem(Configuration.unix())) {
Path tmpDir = Files.createDirectory(fs.getPath("/temp"));
String baseName = "dl-net";

// Exporting with default behaviour, no default control area is written
Path tmpDirNoCA = tmpDir.resolve("network-no-ca");
Files.createDirectories(tmpDirNoCA);
network.write("CGMES", null, tmpDirNoCA.resolve(baseName));
Network networkNoCA = Network.read(new GenericReadOnlyDataSource(tmpDirNoCA, baseName));
assertEquals(0, networkNoCA.getAreaCount());

// Explicit creation of a default control area
new CgmesExport().createDefaultControlAreaInterchange(network);

// Check that exported files now have a control area definition
Path tmpDirWithCA = tmpDir.resolve("network-with-ca");
Files.createDirectories(tmpDirWithCA);
network.write("CGMES", null, tmpDirWithCA.resolve(baseName));
Network networkWithCA = Network.read(new GenericReadOnlyDataSource(tmpDirWithCA, baseName));
assertEquals(1, networkWithCA.getAreaCount());
assertEquals(1, networkWithCA.getAreas().iterator().next().getAreaBoundaryStream().count());
}
}

private static final double EPSILON = 1e-10;
}

0 comments on commit 4f9d471

Please sign in to comment.