com.powsybl.glsk.cse.CseGlskDocument Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of powsybl-glsk-document-cse Show documentation
Show all versions of powsybl-glsk-document-cse Show documentation
Model of GLSK according to CSE format with its importer.
The newest version!
/*
* 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 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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.SAXException;
import javax.xml.XMLConstants;
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.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 }
* @author Vincent BOCHET {@literal }
*/
public final class CseGlskDocument implements GlskDocument {
private static final Logger LOGGER = LoggerFactory.getLogger(CseGlskDocument.class);
private static final String DATA_CHRONOLOGY_NOT_HANDLED = "CSE GLSK document only supports hourly data";
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> cseGlskPoints = new TreeMap<>();
public static CseGlskDocument importGlsk(InputStream inputStream, boolean useCalculationDirections, boolean validateSchema) {
try {
JAXBContext jaxbContext = JAXBContext.newInstance(ObjectFactory.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
if (validateSchema) {
// 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(GSKDocument nativeGskDocument, boolean useCalculationDirections) {
// Computation of "standard" and "export" timeseries
Map> cseGlskPointsStandard = getGlskPointsFromTimeSeries(nativeGskDocument.getTimeSeries());
Map> 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 calculationDirections = nativeGskDocument.getCalculationDirections().get(0).getCalculationDirection();
Map> countriesInAndOutArea = getCountriesInAndOutArea(calculationDirections);
// Use data from cseGlskPointsStandard or cseGlskPointsExport depending on CalculationDirections
fillGlskPointsForExportCorner(cseGlskPointsStandard, cseGlskPointsExport, countriesInAndOutArea);
}
}
private static Map> getGlskPointsFromTimeSeries(List timeSeries) {
Map> 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> getCountriesInAndOutArea(List calculationDirections) {
String italyEIC = new CountryEICode(Country.IT).getCode();
List countriesInArea = new ArrayList<>();
List 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> cseGlskPointsStandard,
Map> cseGlskPointsExport,
Map> countriesInAndOutArea) {
countriesInAndOutArea.get(COUNTRIES_IN_AREA_KEY).forEach(eic -> {
List 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 cseGlskPoint = cseGlskPointsStandard.get(eic);
this.cseGlskPoints.computeIfAbsent(eic, area -> new ArrayList<>());
this.cseGlskPoints.get(eic).addAll(cseGlskPoint);
});
}
@Override
public List getZones() {
return new ArrayList<>(cseGlskPoints.keySet());
}
@Override
public List getGlskPoints(String zone) {
return cseGlskPoints.getOrDefault(zone, Collections.emptyList());
}
@Override
public ZonalData getZonalGlsks(Network network, Instant instant) {
throw new NotImplementedException(DATA_CHRONOLOGY_NOT_HANDLED);
}
@Override
public ZonalDataChronology getZonalGlsksChronology(Network network) {
throw new NotImplementedException(DATA_CHRONOLOGY_NOT_HANDLED);
}
@Override
public ZonalData getZonalScalable(Network network) {
Map zonalData = new HashMap<>();
for (Map.Entry> entry : cseGlskPoints.entrySet()) {
String area = entry.getKey();
// There is always only one GlskPoint for a zone
GlskPoint zonalGlskPoint = entry.getValue().get(0);
if (isHybridCseGlskPoint(zonalGlskPoint)) {
//if we are here then there are two glskPoints, and we want to order them by putting the one with "order = 1" first
List scalables = zonalGlskPoint.getGlskShiftKeys().stream()
.sorted(Comparator.comparingInt(sk -> ((CseGlskShiftKey) sk).getOrder() == 1 ? 0 : 1))
.map(sk -> GlskPointScalableConverter.convert(network, List.of(sk)))
.collect(Collectors.toList());
zonalData.put(area, Scalable.upDown(Scalable.stack(scalables.get(0), scalables.get(1)), scalables.get(1)));
} else {
zonalData.put(area, GlskPointScalableConverter.convert(network, zonalGlskPoint));
}
}
return new ZonalDataImpl<>(zonalData);
}
private boolean isHybridCseGlskPoint(GlskPoint zonalGlskPoint) {
// If 2 shift are present, and one has order 1 and a maximum shift and is of type PropGlsk
return zonalGlskPoint.getGlskShiftKeys().size() == 2 && zonalGlskPoint.getGlskShiftKeys().stream().filter(CseGlskShiftKey.class::isInstance).map(CseGlskShiftKey.class::cast)
.anyMatch(cseGlskShiftKey -> cseGlskShiftKey.getOrder() == 1 && !Double.isNaN(cseGlskShiftKey.getMaximumShift()) && cseGlskShiftKey.getBusinessType().equals("B42"));
}
@Override
public ZonalData getZonalScalable(Network network, Instant instant) {
throw new NotImplementedException(DATA_CHRONOLOGY_NOT_HANDLED);
}
@Override
public ZonalDataChronology getZonalScalableChronology(Network network) {
throw new NotImplementedException(DATA_CHRONOLOGY_NOT_HANDLED);
}
}