Skip to content

Commit

Permalink
Cse valid/glsk merged (#92)
Browse files Browse the repository at this point in the history
* Change CSE GLSK import from DOM parsing to Jaxb unmarshalling

Signed-off-by: Vincent BOCHET <vincent.bochet@rte-france.com>

* Implement CSE GLSK import for export corner

Signed-off-by: Vincent BOCHET <vincent.bochet@rte-france.com>

* Variable naming and code improvement

Signed-off-by: Vincent BOCHET <vincent.bochet@rte-france.com>

* Update XSD for GLSK files to match functional specifications

Signed-off-by: Vincent BOCHET <vincent.bochet@rte-france.com>

* Fix inversion between timeseries and timeseriesexport for inarea and outarea countries

Signed-off-by: Vincent BOCHET <vincent.bochet@rte-france.com>

* Add tests for block and node wrappers & for merged GLSK files import

Signed-off-by: Vincent BOCHET <vincent.bochet@rte-france.com>

* Fix gsk-document.xsd import for schema validation

Signed-off-by: Vincent BOCHET <vincent.bochet@rte-france.com>

* Adapt GlskDocumentImporter for CSE Import EC and CSE Valid EC

Signed-off-by: Vincent BOCHET <vincent.bochet@rte-france.com>

* Rename some variables

Signed-off-by: Vincent BOCHET <vincent.bochet@rte-france.com>

---------

Signed-off-by: Vincent BOCHET <vincent.bochet@rte-france.com>
  • Loading branch information
vbochetRTE authored Mar 29, 2023
1 parent 4e2ad61 commit e73f815
Show file tree
Hide file tree
Showing 21 changed files with 12,459 additions and 242 deletions.
35 changes: 35 additions & 0 deletions glsk/glsk-document-cse/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,32 @@
<name>GLSK document - CSE format</name>
<description>Model of GLSK according to CSE format with its importer.</description>

<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>jaxb2-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>xjc</goal>
</goals>
</execution>
</executions>
<configuration>
<sources>
<source>src/main/resources/xsd/etso-code-lists.xsd</source>
<source>src/main/resources/xsd/etso-core-cmpts.xsd</source>
<source>src/main/resources/xsd/gsk-document.xsd</source>
</sources>
<xjbSources>
<source>src/main/resources/xjb/binding.xjb</source>
</xjbSources>
</configuration>
</plugin>
</plugins>
</build>

<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
Expand Down Expand Up @@ -48,6 +74,15 @@
<artifactId>slf4j-simple</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Copyright (c) 2023, 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/.
*/
package com.powsybl.glsk.cse;

import xsd.etso_core_cmpts.QuantityType;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
* @author Vincent BOCHET {@literal <vincent.bochet at rte-france.com>}
*/
public final class BlockWrapper {
private final Object block;

public BlockWrapper(Object block) {
this.block = block;
}

public Object getBlock() {
return block;
}

public Optional<BigInteger> getOrder() {
try {
BigInteger order = (BigInteger) block.getClass().getDeclaredField("order").get(block);
return Optional.ofNullable(order);
} catch (NoSuchFieldException | IllegalAccessException e) {
return Optional.empty();
}
}

public Optional<BigDecimal> getMaximumShift() {
try {
QuantityType maximumShift = (QuantityType) block.getClass().getDeclaredField("maximumShift").get(block);
return Optional.ofNullable(maximumShift).map(QuantityType::getV);
} catch (NoSuchFieldException | IllegalAccessException e) {
return Optional.empty();
}
}

public Optional<BigDecimal> getFactor() {
try {
QuantityType factor = (QuantityType) block.getClass().getDeclaredField("factor").get(block);
return Optional.ofNullable(factor).map(QuantityType::getV);
} catch (NoSuchFieldException | IllegalAccessException e) {
return Optional.empty();
}
}

public Optional<List<NodeWrapper>> getNodeList() {
try {
List<Object> objectList = (List<Object>) block.getClass().getDeclaredField("node").get(block);
return Optional.ofNullable(objectList)
.map(list -> list.stream().map(NodeWrapper::new).collect(Collectors.toList()));
} catch (NoSuchFieldException | IllegalAccessException e) {
return Optional.empty();
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, RTE (http://www.rte-france.com)
* Copyright (c) 2023, 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/.
Expand All @@ -9,72 +9,156 @@
import com.powsybl.glsk.api.GlskDocument;
import com.powsybl.glsk.api.GlskPoint;
import com.powsybl.glsk.api.util.converters.GlskPointScalableConverter;
import com.powsybl.glsk.commons.CountryEICode;
import com.powsybl.glsk.commons.GlskException;
import com.powsybl.glsk.commons.ZonalData;
import com.powsybl.glsk.commons.ZonalDataChronology;
import com.powsybl.glsk.commons.ZonalDataImpl;
import com.powsybl.iidm.modification.scalable.Scalable;
import com.powsybl.iidm.network.Country;
import com.powsybl.iidm.network.Network;
import com.powsybl.sensitivity.SensitivityVariableSet;
import jakarta.xml.bind.JAXBContext;
import jakarta.xml.bind.JAXBException;
import jakarta.xml.bind.JAXBIntrospector;
import jakarta.xml.bind.Unmarshaller;
import org.apache.commons.lang3.NotImplementedException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.SAXException;

import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import java.io.InputStream;
import java.net.URL;
import java.time.Instant;
import java.util.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.stream.Collectors;

/**
* @author Sebastien Murgey {@literal <sebastien.murgey at rte-france.com>}
* @author Vincent BOCHET {@literal <vincent.bochet at rte-france.com>}
*/
public final class CseGlskDocument implements GlskDocument {
private static final Logger LOGGER = LoggerFactory.getLogger(CseGlskDocument.class);
private static final String LINEAR_GLSK_NOT_HANDLED = "CSE GLSK document does not handle Linear GLSK conversion";
private static final String COUNTRIES_IN_AREA_KEY = "countriesInArea";
private static final String COUNTRIES_OUT_AREA_KEY = "countriesOutArea";

/**
* list of GlskPoint in the given Glsk document
*/
private final Map<String, List<GlskPoint>> cseGlskPoints = new TreeMap<>();

public static CseGlskDocument importGlsk(Document document) {
return new CseGlskDocument(document);
}

public static CseGlskDocument importGlsk(InputStream inputStream) {
public static CseGlskDocument importGlsk(InputStream inputStream, boolean useCalculationDirections) {
try {
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
documentBuilderFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
documentBuilderFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
documentBuilderFactory.setAttribute(XMLConstants.FEATURE_SECURE_PROCESSING, Boolean.TRUE);
documentBuilderFactory.setNamespaceAware(true);

Document document = documentBuilderFactory.newDocumentBuilder().parse(inputStream);
document.getDocumentElement().normalize();
return new CseGlskDocument(document);
} catch (IOException | SAXException | ParserConfigurationException e) {
JAXBContext jaxbContext = JAXBContext.newInstance(ObjectFactory.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();

// Setup schema validator
SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
URL glskSchemaResource = CseGlskDocument.class.getResource("/xsd/gsk-document.xsd");
if (glskSchemaResource != null) {
Schema glskSchema = sf.newSchema(glskSchemaResource);
unmarshaller.setSchema(glskSchema);
} else {
LOGGER.warn("Unable to find GLSK Schema definition file. GLSK file will be imported without schema validation.");
}

// Unmarshal xml file
GSKDocument nativeGskDocument = (GSKDocument) JAXBIntrospector.getValue(unmarshaller.unmarshal(inputStream));
return new CseGlskDocument(nativeGskDocument, useCalculationDirections);
} catch (SAXException e) {
throw new GlskException("Unable to import CSE GLSK file: Schema validation failed.", e);
} catch (JAXBException e) {
throw new GlskException("Unable to import CSE GLSK file.", e);
}
}

private CseGlskDocument(Document document) {
NodeList timeSeriesNodeList = document.getElementsByTagName("TimeSeries");
for (int i = 0; i < timeSeriesNodeList.getLength(); i++) {
if (timeSeriesNodeList.item(i).getNodeType() == Node.ELEMENT_NODE) {
Element timeSeriesElement = (Element) timeSeriesNodeList.item(i);
GlskPoint glskPoint = new CseGlskPoint(timeSeriesElement);
cseGlskPoints.computeIfAbsent(glskPoint.getSubjectDomainmRID(), area -> new ArrayList<>());
cseGlskPoints.get(glskPoint.getSubjectDomainmRID()).add(glskPoint);
}
private CseGlskDocument(GSKDocument nativeGskDocument, boolean useCalculationDirections) {
// Computation of "standard" and "export" timeseries
Map<String, List<GlskPoint>> cseGlskPointsStandard = getGlskPointsFromTimeSeries(nativeGskDocument.getTimeSeries());
Map<String, List<GlskPoint>> cseGlskPointsExport = getGlskPointsFromTimeSeries(nativeGskDocument.getTimeSeriesExport());

if (!useCalculationDirections || calculationDirectionsAbsent(nativeGskDocument)) {
// "default" mode : all countries are considered in full import (use TimeSeries for all)
this.cseGlskPoints.putAll(cseGlskPointsStandard);
} else {
// Extract CalculationDirections
List<CalculationDirectionType> calculationDirections = nativeGskDocument.getCalculationDirections().get(0).getCalculationDirection();
Map<String, List<String>> countriesInAndOutArea = getCountriesInAndOutArea(calculationDirections);

// Use data from cseGlskPointsStandard or cseGlskPointsExport depending on CalculationDirections
fillGlskPointsForExportCorner(cseGlskPointsStandard, cseGlskPointsExport, countriesInAndOutArea);
}
}

private static Map<String, List<GlskPoint>> getGlskPointsFromTimeSeries(List<TimeSeriesType> timeSeries) {
Map<String, List<GlskPoint>> cseGlskPointsPerArea = new TreeMap<>();
if (timeSeries == null) {
return cseGlskPointsPerArea;
}

timeSeries.stream()
.map(CseGlskPoint::new)
.forEach(glskPoint -> {
cseGlskPointsPerArea.computeIfAbsent(glskPoint.getSubjectDomainmRID(), area -> new ArrayList<>());
cseGlskPointsPerArea.get(glskPoint.getSubjectDomainmRID()).add(glskPoint);
});
return cseGlskPointsPerArea;
}

private static boolean calculationDirectionsAbsent(GSKDocument nativeGskDocument) {
return nativeGskDocument.getCalculationDirections() == null
|| nativeGskDocument.getCalculationDirections().isEmpty()
|| nativeGskDocument.getCalculationDirections().get(0).getCalculationDirection() == null
|| nativeGskDocument.getCalculationDirections().get(0).getCalculationDirection().isEmpty();
}

private static Map<String, List<String>> getCountriesInAndOutArea(List<CalculationDirectionType> calculationDirections) {
String italyEIC = new CountryEICode(Country.IT).getCode();
List<String> countriesInArea = new ArrayList<>();
List<String> countriesOutArea = new ArrayList<>();

calculationDirections.stream()
.map(cd -> cd.getInArea().getV())
.filter(countryEIC -> !countryEIC.equals(italyEIC))
.forEach(countriesInArea::add);

countriesInArea.add(italyEIC);

calculationDirections.stream()
.map(cd -> cd.getOutArea().getV())
.filter(countryEIC -> !countryEIC.equals(italyEIC))
.forEach(countriesOutArea::add);

return Map.of(COUNTRIES_IN_AREA_KEY, countriesInArea,
COUNTRIES_OUT_AREA_KEY, countriesOutArea);
}

private void fillGlskPointsForExportCorner(Map<String, List<GlskPoint>> cseGlskPointsStandard,
Map<String, List<GlskPoint>> cseGlskPointsExport,
Map<String, List<String>> countriesInAndOutArea) {
countriesInAndOutArea.get(COUNTRIES_IN_AREA_KEY).forEach(eic -> {
List<GlskPoint> cseGlskPoint = cseGlskPointsExport.getOrDefault(eic, cseGlskPointsStandard.get(eic));
this.cseGlskPoints.computeIfAbsent(eic, area -> new ArrayList<>());
this.cseGlskPoints.get(eic).addAll(cseGlskPoint);
});

countriesInAndOutArea.get(COUNTRIES_OUT_AREA_KEY).forEach(eic -> {
List<GlskPoint> cseGlskPoint = cseGlskPointsStandard.get(eic);
this.cseGlskPoints.computeIfAbsent(eic, area -> new ArrayList<>());
this.cseGlskPoints.get(eic).addAll(cseGlskPoint);
});
}

@Override
public List<String> getZones() {
return new ArrayList<>(cseGlskPoints.keySet());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,12 @@ public class CseGlskDocumentImporter extends AbstractGlskDocumentImporter implem

@Override
public GlskDocument importGlsk(InputStream inputStream) {
if (document != null) {
return CseGlskDocument.importGlsk(document);
}
return CseGlskDocument.importGlsk(inputStream);
return importGlsk(inputStream, false);
}

@Override
public GlskDocument importGlsk(InputStream inputStream, boolean useCalculationDirections) {
return CseGlskDocument.importGlsk(inputStream, useCalculationDirections);
}

@Override
Expand Down
Loading

0 comments on commit e73f815

Please sign in to comment.