DataExchanges.java
/*
* Copyright (c) 2020, 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.entsoe.cgmes.balances_adjustment.data_exchange;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.powsybl.commons.PowsyblException;
import com.powsybl.timeseries.*;
import org.threeten.extra.Interval;
import java.time.Duration;
import java.time.Instant;
import java.time.ZonedDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* @deprecated This module has not either been maintained nor used. We will remove it soon. Please report on Slack if you are using it.
* Pan European Verification Function (PEVF) &
* Common Grid Model Alignment (CGMA)
* data.
*
* @author Thomas Adam {@literal <tadam at silicom.fr>}
*/
@Deprecated(since = "2.14", forRemoval = true)
public class DataExchanges {
// RequireNonNull messages
private static final String INSTANT_CANNOT_BE_NULL = "Instant cannot be null";
private static final String ID_CANNOT_BE_NULL = "TimeSeriesId cannot be null";
private static final String IDS_CANNOT_BE_NULL = "TimeSeriesIds cannot be null";
/** Document identification. */
private final String mRID;
/** Version of the document. */
private final int revisionNumber;
/** The coded type of a document. The document type describes the principal characteristic of the document. */
private final StandardMessageType type;
/** The identification of the nature of process that the
document addresses. */
private final StandardProcessType processType;
/** The identification of the sender */
private final String senderId;
private final StandardCodingSchemeType senderCodingScheme;
/** The identification of the role played by a market player. */
private final StandardRoleType senderMarketRole;
/** The identification of a party in the energy market. */
private final String receiverId;
private final StandardCodingSchemeType receiverCodingScheme;
/** The identification of the role played by the a market player. */
private final StandardRoleType receiverMarketRole;
/** The date and time of the creation of the document. */
private final ZonedDateTime creationDate;
/** This information provides the start and end date and time of the period covered by the document. */
private final Interval period;
// Optional data
/** The identification of an individually predefined dataset in a
data base system (e. g. Verification Platform). */
private final String datasetMarketDocumentMRId;
/** The identification of the condition or position of the document with regard to its standing. A document may be intermediate or final. */
private final StandardStatusType docStatus;
/** The optimisation area of concern. */
private final String domainId;
private final StandardCodingSchemeType domainCodingScheme;
// Time Series
private final BiMap<String, DoubleTimeSeries> timeSeriesById = HashBiMap.create();
DataExchanges(String mRID, int revisionNumber, StandardMessageType type, StandardProcessType processType,
String senderId, StandardCodingSchemeType senderCodingScheme, StandardRoleType senderMarketRole,
String receiverId, StandardCodingSchemeType receiverCodingScheme, StandardRoleType receiverMarketRole,
ZonedDateTime creationDate, Interval period, String datasetMarketDocumentMRId, StandardStatusType docStatus, Map<String, StoredDoubleTimeSeries> timeSeriesById,
String domainId, StandardCodingSchemeType domainCodingScheme) {
this.mRID = Objects.requireNonNull(mRID, "mRID is missing");
this.revisionNumber = checkRevisionNumber(revisionNumber);
this.type = Objects.requireNonNull(type, "StandardMessageType is missing");
this.processType = Objects.requireNonNull(processType, "StandardMessageType is missing");
this.senderId = Objects.requireNonNull(senderId, "Sender mRID is missing");
this.senderCodingScheme = Objects.requireNonNull(senderCodingScheme, "Sender codingScheme is missing");
this.senderMarketRole = Objects.requireNonNull(senderMarketRole, "Sender role is missing");
this.receiverId = Objects.requireNonNull(receiverId, "Receiver mRID is missing");
this.receiverCodingScheme = Objects.requireNonNull(receiverCodingScheme, "Receiver codingScheme is missing");
this.receiverMarketRole = Objects.requireNonNull(receiverMarketRole, "Receiver role is missing");
this.creationDate = Objects.requireNonNull(creationDate, "Creation DateTime is missing");
this.period = Objects.requireNonNull(period, "Time interval is missing");
this.timeSeriesById.putAll(Objects.requireNonNull(timeSeriesById));
// Optional data
this.datasetMarketDocumentMRId = datasetMarketDocumentMRId;
this.docStatus = docStatus;
this.domainId = domainId;
this.domainCodingScheme = domainCodingScheme;
}
// MarketDocument metadata
public String getMRId() {
return mRID;
}
public int getRevisionNumber() {
return revisionNumber;
}
public StandardMessageType getType() {
return type;
}
public StandardProcessType getProcessType() {
return processType;
}
public String getSenderId() {
return senderId;
}
public StandardCodingSchemeType getSenderCodingScheme() {
return senderCodingScheme;
}
public StandardRoleType getSenderMarketRole() {
return senderMarketRole;
}
public String getReceiverId() {
return receiverId;
}
public StandardCodingSchemeType getReceiverCodingScheme() {
return receiverCodingScheme;
}
public StandardRoleType getReceiverMarketRole() {
return receiverMarketRole;
}
public ZonedDateTime getCreationDate() {
return creationDate;
}
public Interval getPeriod() {
return period;
}
// Optional metadata
public Optional<String> getDatasetMarketDocumentMRId() {
return Optional.ofNullable(datasetMarketDocumentMRId);
}
Optional<StandardStatusType> getDocStatus() {
return Optional.ofNullable(docStatus);
}
public Optional<String> getDomainId() {
return Optional.ofNullable(domainId);
}
public Optional<StandardCodingSchemeType> getDomainCodingScheme() {
return Optional.ofNullable(domainCodingScheme);
}
// Utilities
public Collection<DoubleTimeSeries> getTimeSeries() {
return Collections.unmodifiableCollection(timeSeriesById.values());
}
public DoubleTimeSeries getTimeSeries(String timeSeriesId) {
Objects.requireNonNull(timeSeriesId, ID_CANNOT_BE_NULL);
if (!timeSeriesById.containsKey(timeSeriesId)) {
throw new PowsyblException(String.format("TimeSeries '%s' not found", timeSeriesId));
}
return timeSeriesById.get(timeSeriesId);
}
public Stream<DoubleTimeSeries> getTimeSeriesWithDomainId(String inOutDomainId) {
Objects.requireNonNull(inOutDomainId);
return getTimeSeries().stream().filter(t -> {
Map<String, String> tags = t.getMetadata().getTags();
return inOutDomainId.equalsIgnoreCase(tags.get(DataExchangesConstants.IN_DOMAIN + "." + DataExchangesConstants.MRID)) ||
inOutDomainId.equalsIgnoreCase(tags.get(DataExchangesConstants.OUT_DOMAIN + "." + DataExchangesConstants.MRID));
});
}
public Stream<DoubleTimeSeries> getTimeSeriesStream(String inDomainId, String outDomainId) {
Objects.requireNonNull(inDomainId);
Objects.requireNonNull(outDomainId);
return getTimeSeries().stream().filter(t -> {
Map<String, String> tags = t.getMetadata().getTags();
return inDomainId.equalsIgnoreCase(tags.get(DataExchangesConstants.IN_DOMAIN + "." + DataExchangesConstants.MRID)) &&
outDomainId.equalsIgnoreCase(tags.get(DataExchangesConstants.OUT_DOMAIN + "." + DataExchangesConstants.MRID));
});
}
public Map<String, Double> getNetPositionsWithInDomainId(String inDomainId, Instant instant) {
return getNetPositionsWithInDomainId(inDomainId, instant, true);
}
public Map<String, Double> getNetPositionsWithInDomainId(String inDomainId, Instant instant, boolean exceptionOutOfBound) {
return getTimeSeriesWithDomainId(inDomainId)
.map(doubleTimeSeries -> {
Map<String, String> tags = doubleTimeSeries.getMetadata().getTags();
String tmpInDomainId = tags.get(DataExchangesConstants.IN_DOMAIN + "." + DataExchangesConstants.MRID);
String tmpOutDomainId = tags.get(DataExchangesConstants.OUT_DOMAIN + "." + DataExchangesConstants.MRID);
if (tmpInDomainId.equals(inDomainId)) {
return tmpOutDomainId;
} else if (tmpOutDomainId.equals(inDomainId)) {
return tmpInDomainId;
}
throw new AssertionError();
})
.distinct()
.collect(Collectors.toMap(Function.identity(), outDomainId -> getNetPosition(inDomainId, outDomainId, instant, exceptionOutOfBound)));
}
public List<DoubleTimeSeries> getTimeSeries(String inDomainId, String outDomainId) {
return getTimeSeriesStream(inDomainId, outDomainId).collect(Collectors.toList());
}
public double getNetPosition(String inDomainId, String outDomainId, Instant instant) {
return getNetPosition(inDomainId, outDomainId, instant, true);
}
public double getNetPosition(String inDomainId, String outDomainId, Instant instant, boolean exceptionOutOfBound) {
return getValuesAt(inDomainId, outDomainId, instant, exceptionOutOfBound).values().stream().reduce(0d, Double::sum)
- getValuesAt(outDomainId, inDomainId, instant, exceptionOutOfBound).values().stream().reduce(0d, Double::sum);
}
public Map<String, Double> getValuesAt(Instant instant) {
Objects.requireNonNull(instant, INSTANT_CANNOT_BE_NULL);
return timeSeriesById.keySet().stream()
.collect(Collectors.toMap(id -> id, id -> getValueAt(getTimeSeries(id), instant, true)));
}
public double getValueAt(String timeSeriesId, Instant instant) {
return getValueAt(timeSeriesId, instant, true);
}
public double getValueAt(String timeSeriesId, Instant instant, boolean exceptionOutOfBound) {
Objects.requireNonNull(instant, INSTANT_CANNOT_BE_NULL);
final DoubleTimeSeries timeSeries = getTimeSeries(timeSeriesId);
return getValueAt(timeSeries, instant, exceptionOutOfBound);
}
public Map<String, Double> getValuesAt(List<String> timeSeriesIds, Instant instant) {
Objects.requireNonNull(timeSeriesIds, IDS_CANNOT_BE_NULL);
Objects.requireNonNull(instant, INSTANT_CANNOT_BE_NULL);
return timeSeriesIds.stream()
.collect(Collectors.toMap(id -> id, id -> getValueAt(getTimeSeries(id), instant, true)));
}
public Map<String, Double> getValuesAt(String inDomainId, String outDomainId, Instant instant) {
return getValuesAt(inDomainId, outDomainId, instant, true);
}
private Map<String, Double> getValuesAt(String inDomainId, String outDomainId, Instant instant, boolean exceptionOutOfBound) {
Objects.requireNonNull(inDomainId);
Objects.requireNonNull(outDomainId);
Objects.requireNonNull(instant, INSTANT_CANNOT_BE_NULL);
return getTimeSeriesStream(inDomainId, outDomainId).collect(Collectors.toMap(t -> timeSeriesById.inverse().get(t), t -> getValueAt(t, instant, exceptionOutOfBound)));
}
/**
* @deprecated Use {@link #getValuesAt(String[], Instant)} instead.
*/
@Deprecated
public Map<String, Double> getValueAt(String[] timeSeriesIds, Instant instant) {
return getValuesAt(timeSeriesIds, instant);
}
public Map<String, Double> getValuesAt(String[] timeSeriesIds, Instant instant) {
return getValuesAt(Arrays.asList(timeSeriesIds), instant);
}
private double getValueAt(DoubleTimeSeries timeSeries, Instant instant, boolean exceptionOutOfBound) {
RegularTimeSeriesIndex index = (RegularTimeSeriesIndex) timeSeries.getMetadata().getIndex();
var start = Instant.ofEpochMilli(index.getStartTime());
var end = Instant.ofEpochMilli(index.getEndTime());
if (instant.isBefore(start) || instant.isAfter(end) || instant.equals(end)) {
if (exceptionOutOfBound) {
throw new PowsyblException(String.format("%s '%s' is out of bound [%s, %s[", timeSeries.getMetadata().getName(), instant, start, end));
}
return 0;
} else {
long spacing = index.getSpacing();
var elapsed = Duration.between(start, instant);
long point = elapsed.toMillis() / spacing;
return timeSeries.toArray()[(int) point];
}
}
private static int checkRevisionNumber(int revisionNumber) {
if (revisionNumber < 0 || revisionNumber > 100) {
throw new IllegalArgumentException("Bad revision number value " + revisionNumber);
}
return revisionNumber;
}
}