All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.powsybl.metrix.mapping.TimeSeriesMapper Maven / Gradle / Ivy

There is a newer version: 2.6.0
Show newest version
/*
 * 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/.
 * SPDX-License-Identifier: MPL-2.0
 */
package com.powsybl.metrix.mapping;

import com.powsybl.iidm.network.*;
import com.powsybl.iidm.network.extensions.HvdcAngleDroopActivePowerControl;
import com.powsybl.iidm.network.extensions.HvdcOperatorActivePowerRange;
import com.powsybl.iidm.network.extensions.HvdcOperatorActivePowerRangeAdder;
import com.powsybl.iidm.network.extensions.LoadDetail;
import com.powsybl.metrix.mapping.log.*;
import com.powsybl.metrix.mapping.timeseries.EquipmentTimeSeriesMap;
import com.powsybl.metrix.mapping.timeseries.MappedEquipment;
import com.powsybl.timeseries.ReadOnlyTimeSeriesStore;
import com.powsybl.timeseries.TimeSeriesTable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author Paul Bui-Quang {@literal }
 */
public class TimeSeriesMapper {

    private static final Logger LOGGER = LoggerFactory.getLogger(TimeSeriesMapper.class);
    public static final double EPSILON_COMPARISON = 1e-5;
    private static final double EPSILON_ZERO_STD_DEV = 1e-6;
    public static final int CONSTANT_VARIANT_ID = -1;
    public static final int SWITCH_OPEN = 0; // 0 means switch is open
    public static final int DISCONNECTED_VALUE = 0; // 0 means equipment is disconnected
    public static final int CONNECTED_VALUE = 1;
    private static final String GENERATOR_NOT_FOUND_MESSAGE = "Generator '%s' not found";
    private static final String LOAD_NOT_FOUND_MESSAGE = "Load '%s' not found";
    private static final String LOAD_DETAIL_NOT_FOUND_MESSAGE = "LoadDetail '%s' not found";

    private final TimeSeriesMappingConfig config;
    private final TimeSeriesMapperParameters parameters;
    private final Network network;
    private final TimeSeriesMappingLogger timeSeriesMappingLogger;

    private TimeSeriesTable table;
    private TimeSeriesMapperChecker checker;

    private static class MapperContext {
        private final EquipmentTimeSeriesMap timeSeriesToLoadsMapping = new EquipmentTimeSeriesMap();
        private final EquipmentTimeSeriesMap timeSeriesToGeneratorsMapping = new EquipmentTimeSeriesMap();
        private final EquipmentTimeSeriesMap timeSeriesToDanglingLinesMapping = new EquipmentTimeSeriesMap();
        private final EquipmentTimeSeriesMap timeSeriesToHvdcLinesMapping = new EquipmentTimeSeriesMap();
        private final EquipmentTimeSeriesMap timeSeriesToPhaseTapChangersMapping = new EquipmentTimeSeriesMap();
        private final EquipmentTimeSeriesMap timeSeriesToBreakersMapping = new EquipmentTimeSeriesMap();
        private final EquipmentTimeSeriesMap timeSeriesToTransformersMapping = new EquipmentTimeSeriesMap();
        private final EquipmentTimeSeriesMap timeSeriesToRatioTapChangersMapping = new EquipmentTimeSeriesMap();
        private final EquipmentTimeSeriesMap timeSeriesToLccConverterStationsMapping = new EquipmentTimeSeriesMap();
        private final EquipmentTimeSeriesMap timeSeriesToVscConverterStationsMapping = new EquipmentTimeSeriesMap();
        private final EquipmentTimeSeriesMap timeSeriesToLinesMapping = new EquipmentTimeSeriesMap();
        private Map> equipmentTimeSeries;
    }

    public TimeSeriesMapper(TimeSeriesMappingConfig config, TimeSeriesMapperParameters parameters, Network network, TimeSeriesMappingLogger timeSeriesMappingLogger) {
        this.config = Objects.requireNonNull(config);
        this.parameters = Objects.requireNonNull(parameters);
        this.network = Objects.requireNonNull(network);
        this.timeSeriesMappingLogger = Objects.requireNonNull(timeSeriesMappingLogger);
    }

    public static void setHvdcMax(HvdcLine hvdcLine, double max) {
        HvdcOperatorActivePowerRange activePowerRange = hvdcLine.getExtension(HvdcOperatorActivePowerRange.class);
        if (activePowerRange != null) {
            activePowerRange.setOprFromCS1toCS2((float) Math.abs(max));
        }
        hvdcLine.setMaxP(Math.max(hvdcLine.getMaxP(), Math.abs(max)));
    }

    public static void setHvdcMin(HvdcLine hvdcLine, double min) {
        HvdcOperatorActivePowerRange activePowerRange = hvdcLine.getExtension(HvdcOperatorActivePowerRange.class);
        if (activePowerRange != null) {
            hvdcLine.getExtension(HvdcOperatorActivePowerRange.class).setOprFromCS2toCS1((float) Math.abs(min));
        }
        hvdcLine.setMaxP(Math.max(hvdcLine.getMaxP(), Math.abs(min)));
    }

    public static float getMax(Identifiable identifiable) {
        if (identifiable instanceof Generator generator) {
            return (float) generator.getMaxP();
        } else if (identifiable instanceof HvdcLine hvdcLine) {
            HvdcOperatorActivePowerRange activePowerRange = hvdcLine.getExtension(HvdcOperatorActivePowerRange.class);
            if (activePowerRange != null) {
                return activePowerRange.getOprFromCS1toCS2();
            } else {
                return (float) hvdcLine.getMaxP();
            }
        } else {
            return Float.MAX_VALUE;
        }
    }

    public static float getMin(Identifiable identifiable) {
        if (identifiable instanceof Generator generator) {
            return (float) generator.getMinP();
        } else if (identifiable instanceof HvdcLine hvdcLine) {
            HvdcOperatorActivePowerRange activePowerRange = hvdcLine.getExtension(HvdcOperatorActivePowerRange.class);
            if (activePowerRange != null) {
                return -activePowerRange.getOprFromCS2toCS1();
            } else {
                return (float) -hvdcLine.getMaxP();
            }
        } else {
            return Float.MIN_VALUE;
        }
    }

    public static HvdcAngleDroopActivePowerControl getActivePowerControl(HvdcLine hvdcLine) {
        HvdcAngleDroopActivePowerControl activePowerControl = hvdcLine.getExtension(HvdcAngleDroopActivePowerControl.class);
        if (activePowerControl != null &&
                activePowerControl.isEnabled() &&
                activePowerControl.getDroop() > 0) {
            return activePowerControl;
        }
        return null;
    }

    public static void setHvdcLineSetPoint(HvdcLine hvdcLine, double setPoint) {
        HvdcAngleDroopActivePowerControl activePowerControl = getActivePowerControl(hvdcLine);
        if (activePowerControl != null) {
            activePowerControl.setP0((float) setPoint);
        } else {
            if (setPoint >= 0) {
                hvdcLine.setConvertersMode(HvdcLine.ConvertersMode.SIDE_1_RECTIFIER_SIDE_2_INVERTER);
                hvdcLine.setActivePowerSetpoint((float) setPoint);
            } else {
                hvdcLine.setConvertersMode(HvdcLine.ConvertersMode.SIDE_1_INVERTER_SIDE_2_RECTIFIER);
                hvdcLine.setActivePowerSetpoint((float) -setPoint);
            }
        }
    }

    public static float getHvdcLineSetPoint(HvdcLine hvdcLine) {
        HvdcAngleDroopActivePowerControl activePowerControl = getActivePowerControl(hvdcLine);
        if (activePowerControl != null) {
            return activePowerControl.getP0();
        } else {
            if (hvdcLine.getConvertersMode() == HvdcLine.ConvertersMode.SIDE_1_INVERTER_SIDE_2_RECTIFIER) {
                return (float) -hvdcLine.getActivePowerSetpoint();
            } else {
                return (float) hvdcLine.getActivePowerSetpoint();
            }
        }
    }

    public static float getP(Identifiable identifiable) {
        if (identifiable instanceof Generator generator) {
            return (float) generator.getTargetP();
        } else if (identifiable instanceof HvdcLine hvdcLine) {
            return getHvdcLineSetPoint(hvdcLine);
        } else {
            return Float.MIN_VALUE;
        }
    }

    public static HvdcOperatorActivePowerRange addActivePowerRangeExtension(HvdcLine hvdcLine) {
        HvdcOperatorActivePowerRange hvdcRange = hvdcLine.getExtension(HvdcOperatorActivePowerRange.class);
        if (hvdcRange == null) {
            hvdcLine
                    .newExtension(HvdcOperatorActivePowerRangeAdder.class)
                    .withOprFromCS1toCS2((float) Math.abs(hvdcLine.getMaxP()))
                    .withOprFromCS2toCS1((float) Math.abs(hvdcLine.getMaxP()))
                    .add();
            hvdcRange = hvdcLine.getExtension(HvdcOperatorActivePowerRange.class);
        }
        return hvdcRange;
    }

    public static boolean isPowerOrLimitVariable(MappingVariable variable) {
        return variable == EquipmentVariable.TARGET_P ||
                variable == EquipmentVariable.ACTIVE_POWER_SETPOINT ||
                variable == EquipmentVariable.MIN_P ||
                variable == EquipmentVariable.MAX_P;
    }

    public static boolean isPowerVariable(MappingVariable variable) {
        return variable == EquipmentVariable.TARGET_P ||
                variable == EquipmentVariable.ACTIVE_POWER_SETPOINT;
    }

    public static MappingVariable getPowerVariable(Identifiable identifiable) {
        if (identifiable instanceof Generator) {
            return EquipmentVariable.TARGET_P;
        } else if (identifiable instanceof HvdcLine) {
            return EquipmentVariable.ACTIVE_POWER_SETPOINT;
        } else {
            throw new AssertionError("Unsupported equipment type for id " + identifiable.getId());
        }
    }

    private void mapToNetwork(int version, int variantId, int point,
                              IndexedMappingKey mappingKey, List mappedEquipments) {
        String timeSeriesName = mappingKey.getKey().getId();
        MappingVariable variable = mappingKey.getKey().getMappingVariable();

        // compute distribution key associated to equipment list
        double[] distributionKeys = new double[mappedEquipments.size()];
        double distributionKeySum = calculateDistributionKeySum(version, point, mappedEquipments, distributionKeys);

        double[] equipmentValues = new double[mappedEquipments.size()];
        Arrays.fill(equipmentValues, 0);

        double timeSeriesValue = table.getDoubleValue(version, mappingKey.getNum(), point);
        if (Double.isNaN(timeSeriesValue) || Double.isInfinite(timeSeriesValue)) {
            throw new TimeSeriesMappingException("Impossible to scale down " + timeSeriesValue + " of ts " + timeSeriesName + " at time index '" + table.getTableIndex().getInstantAt(point) + "' and version " + version);
        }

        LogBuilder logBuilder = new LogBuilder().level(System.Logger.Level.WARNING).version(version).point(variantId).index(table.getTableIndex());
        if (Math.abs(timeSeriesValue) > 0) {
            // check equipment list is not empty
            if (mappedEquipments.isEmpty()) {
                logEmptyFilter(timeSeriesName, timeSeriesValue, point, logBuilder);
            } else {
                distributionKeySum = logDistributionKeySumNull(mappedEquipments, timeSeriesName, distributionKeys, distributionKeySum, timeSeriesValue, logBuilder);

                if (logHvdcLimitSign(mappedEquipments, timeSeriesName, variable, timeSeriesValue, logBuilder)) {
                    return;
                }

                // scaling downtime series value to mapped equipments
                for (int i = 0; i < mappedEquipments.size(); i++) {
                    assert distributionKeySum != 0;
                    double distributionFactor = distributionKeys[i] / distributionKeySum;
                    double equipmentValue = timeSeriesValue * distributionFactor;
                    equipmentValues[i] = equipmentValue;
                }
            }
        }

        if (checker != null) {
            List> ids = mappedEquipments.stream().map(MappedEquipment::getIdentifiable).collect(Collectors.toList());
            boolean ignoreLimitsForTimeSeries = parameters.isIgnoreLimits() || TimeSeriesMapper.isPowerVariable(variable) && config.getIgnoreLimitsTimeSeriesNames().contains(timeSeriesName);
            checker.timeSeriesMappedToEquipments(variantId, timeSeriesName, timeSeriesValue, ids, variable, equipmentValues, ignoreLimitsForTimeSeries);
        }
    }

    private void logEmptyFilter(String timeSeriesName, double timeSeriesValue, int point, LogBuilder logBuilder) {
        LogContent emptyFilter = new EmptyFilter().timeSeriesName(timeSeriesName).timeSeriesValue(timeSeriesValue).build();
        Log log = logBuilder.point(point).logDescription(emptyFilter).build();
        if (parameters.isIgnoreEmptyFilter()) {
            timeSeriesMappingLogger.addLog(log);
        } else {
            throw new TimeSeriesMappingException(log.getMessage());
        }
    }

    private boolean logHvdcLimitSign(List mappedEquipments, String timeSeriesName, MappingVariable variable, double timeSeriesValue, LogBuilder logBuilder) {
        if (mappedEquipments.get(0).getIdentifiable() instanceof HvdcLine) {
            LimitSignBuilder limitSignBuilder = new LimitSignBuilder()
                    .timeSeriesValue(timeSeriesValue)
                    .timeSeriesName(timeSeriesName)
                    .variable(EquipmentVariable.MAX_P.getVariableName());
            if (variable == EquipmentVariable.MAX_P && timeSeriesValue < 0) {
                limitSignBuilder.max();
                timeSeriesMappingLogger.addLog(logBuilder.logDescription(limitSignBuilder.build()).build());
                return true;
            } else if (variable == EquipmentVariable.MIN_P && timeSeriesValue > 0) {
                limitSignBuilder.min();
                timeSeriesMappingLogger.addLog(logBuilder.logDescription(limitSignBuilder.build()).build());
                return true;
            }
        }
        return false;
    }

    private double logDistributionKeySumNull(List mappedEquipments, String timeSeriesName, double[] distributionKeys, double distributionKeySum, double timeSeriesValue, LogBuilder logBuilder) {
        double resultDistributionKeySum = distributionKeySum;
        if (resultDistributionKeySum == 0) {
            double distributionKey = NumberDistributionKey.ONE.value();
            for (int i = 0; i < mappedEquipments.size(); i++) {
                distributionKeys[i] = distributionKey;
                resultDistributionKeySum += distributionKeys[i];
            }
            LogContent logContent = new ZeroDistributionKeyInfo(timeSeriesName, timeSeriesValue,
                    mappedEquipments.stream().map(MappedEquipment::getId).toList()).build();
            Log log = logBuilder.level(System.Logger.Level.INFO).logDescription(logContent).build();
            timeSeriesMappingLogger.addLog(log);
        }
        return resultDistributionKeySum;
    }

    private double calculateDistributionKeySum(int version, int point, List mappedEquipments, double[] distributionKeys) {
        double distributionKeySum = 0;
        for (int i = 0; i < mappedEquipments.size(); i++) {
            MappedEquipment mappedEquipment = mappedEquipments.get(i);
            DistributionKey distributionKey = mappedEquipment.getDistributionKey();
            if (distributionKey instanceof NumberDistributionKey numberDistributionKey) {
                distributionKeys[i] = numberDistributionKey.value();
            } else if (distributionKey instanceof TimeSeriesDistributionKey timeSeriesDistributionKey) {
                int timeSeriesNum = timeSeriesDistributionKey.getTimeSeriesNum();
                if (timeSeriesNum == -1) {
                    timeSeriesNum = table.getDoubleTimeSeriesIndex(timeSeriesDistributionKey.getTimeSeriesName());
                    timeSeriesDistributionKey.setTimeSeriesNum(timeSeriesNum);
                }
                distributionKeys[i] = Math.abs(table.getDoubleValue(version, timeSeriesNum, point));
            } else {
                throw new AssertionError();
            }

            distributionKeySum += distributionKeys[i];
        }
        return distributionKeySum;
    }

    private void mapToNetwork(int version, int variantId, int point,
                              EquipmentTimeSeriesMap timeSeriesToEquipmentsMapping) {
        timeSeriesToEquipmentsMapping.getEquipmentTimeSeries().forEach((mappingKey, mappedEquipments) ->
            mapToNetwork(version, variantId, point, mappingKey, mappedEquipments));
    }

    public static IndexedMappingKey indexMappingKey(TimeSeriesTable timeSeriesTable, MappingKey key) {
        return new IndexedMappingKey(key, timeSeriesTable.getDoubleTimeSeriesIndex(key.getId()));
    }

    private void identifyConstantEquipmentTimeSeries(int version,
                                            Map> sourceTimeSeries,
                                            Map> constantTimeSeries,
                                            Map> variableTimeSeries) {
        sourceTimeSeries.forEach((indexedName, mappingKeys) -> {
            int timeSeriesNum = indexedName.getNum();
            if (table.getStdDev(version, timeSeriesNum) < EPSILON_COMPARISON) { // std dev == 0 means time-series is constant
                LOGGER.debug("Equipment time-series '{}' is constant", indexedName.getName());
                constantTimeSeries.put(indexedName, mappingKeys);
            } else {
                variableTimeSeries.put(indexedName, mappingKeys);
            }
        });
    }

    private void identifyConstantTimeSeries(int version,
                                            EquipmentTimeSeriesMap sourceTimeSeries,
                                            EquipmentTimeSeriesMap constantTimeSeries,
                                            EquipmentTimeSeriesMap variableTimeSeries) {
        if (!parameters.isIdentifyConstantTimeSeries()) {
            variableTimeSeries.init(sourceTimeSeries);
            return;
        }
        sourceTimeSeries.getEquipmentTimeSeries().forEach((indexedMappingKey, mappedEquipments) -> {
            int timeSeriesNum = indexedMappingKey.getNum();
            MappingVariable variable = indexedMappingKey.getKey().getMappingVariable();
            if (variable == EquipmentVariable.TARGET_P ||
                    variable == EquipmentVariable.ACTIVE_POWER_SETPOINT) {
                // Active power mapping is not tested in order to allow later correction of values not included in [minP, maxP]
                variableTimeSeries.addMappedEquipmentTimeSeries(indexedMappingKey, mappedEquipments);
            } else {
                if (table.getStdDev(version, timeSeriesNum) < EPSILON_ZERO_STD_DEV) { // std dev == 0 means time-series is constant
                    LOGGER.debug("Mapping time-series '{}' is constant", indexedMappingKey.getKey().getId());
                    constantTimeSeries.addMappedEquipmentTimeSeries(indexedMappingKey, mappedEquipments);
                } else {
                    variableTimeSeries.addMappedEquipmentTimeSeries(indexedMappingKey, mappedEquipments);
                }
            }
        });
    }

    private void correctUnmappedGenerator(boolean isUnmappedMinP, boolean isUnmappedMaxP, Generator generator,
                                          int version) {

        final double minP = generator.getMinP();
        final double maxP = generator.getMaxP();
        final double targetP = generator.getTargetP();

        new GeneratorBoundLimitBuilder()
                .minP(minP)
                .maxP(maxP)
                .targetP(targetP)
                .isUnmappedMinP(isUnmappedMinP)
                .isUnmappedMaxP(isUnmappedMaxP)
                .version(version)
                .index(table.getTableIndex())
                .ignoreLimits(parameters.isIgnoreLimits())
                .setAll(generator, timeSeriesMappingLogger);

    }

    private void correctUnmappedHvdcLine(boolean isUnmappedMinP, boolean isUnmappedMaxP, HvdcLine hvdcLine, int version) {

        final boolean isActivePowerRange = hvdcLine.getExtension(HvdcOperatorActivePowerRange.class) != null;
        final double minP = TimeSeriesMapper.getMin(hvdcLine);
        final double maxP = TimeSeriesMapper.getMax(hvdcLine);
        final double hvdcLineMaxP = hvdcLine.getMaxP();
        final double setPoint = TimeSeriesMapper.getHvdcLineSetPoint(hvdcLine);

        new HvdcBoundLimitBuilder()
                .isActivePowerRange(isActivePowerRange)
                .minP(minP)
                .maxP(maxP)
                .hvdcLineMaxP(hvdcLineMaxP)
                .setPoint(setPoint)
                .isUnmappedMinP(isUnmappedMinP)
                .isUnmappedMaxP(isUnmappedMaxP)
                .version(version)
                .index(table.getTableIndex())
                .ignoreLimits(parameters.isIgnoreLimits())
                .setAll(hvdcLine, timeSeriesMappingLogger);
    }

    private MapperContext initMapperContext() {
        MapperContext context = new MapperContext();
        context.timeSeriesToLoadsMapping.convertToEquipmentTimeSeriesMap(config.getTimeSeriesToLoadsMapping(), table, network, config);
        context.timeSeriesToGeneratorsMapping.convertToEquipmentTimeSeriesMap(config.getTimeSeriesToGeneratorsMapping(), table, network, config);
        context.timeSeriesToDanglingLinesMapping.convertToEquipmentTimeSeriesMap(config.getTimeSeriesToDanglingLinesMapping(), table, network, config);
        context.timeSeriesToHvdcLinesMapping.convertToEquipmentTimeSeriesMap(config.getTimeSeriesToHvdcLinesMapping(), table, network, config);
        context.timeSeriesToPhaseTapChangersMapping.convertToEquipmentTimeSeriesMap(config.getTimeSeriesToPhaseTapChangersMapping(), table, network, config);
        context.timeSeriesToBreakersMapping.convertToEquipmentTimeSeriesMap(config.getTimeSeriesToBreakersMapping(), table, network, config);
        context.timeSeriesToTransformersMapping.convertToEquipmentTimeSeriesMap(config.getTimeSeriesToTransformersMapping(), table, network, config);
        context.timeSeriesToRatioTapChangersMapping.convertToEquipmentTimeSeriesMap(config.getTimeSeriesToRatioTapChangersMapping(), table, network, config);
        context.timeSeriesToLccConverterStationsMapping.convertToEquipmentTimeSeriesMap(config.getTimeSeriesToLccConverterStationsMapping(), table, network, config);
        context.timeSeriesToVscConverterStationsMapping.convertToEquipmentTimeSeriesMap(config.getTimeSeriesToVscConverterStationsMapping(), table, network, config);
        context.timeSeriesToLinesMapping.convertToEquipmentTimeSeriesMap(config.getTimeSeriesToLinesMapping(), table, network, config);
        context.equipmentTimeSeries = config.getTimeSeriesToEquipment().entrySet().stream()
                .collect(Collectors.toMap(e -> new IndexedName(e.getKey(), table.getDoubleTimeSeriesIndex(e.getKey())), Map.Entry::getValue, (x, y) -> y, LinkedHashMap::new));
        return context;
    }

    private void initConstantAndVariableMapperContext(int version, MapperContext context, MapperContext constantTimeSeriesContext, MapperContext variableTimeSeriesContext) {
        // Load mappings are always variable time series to ensure data consistency (p0/LoadDetail)
        variableTimeSeriesContext.timeSeriesToLoadsMapping.init(context.timeSeriesToLoadsMapping);

        // Check if some other mappings are constant
        identifyConstantTimeSeries(version, context.timeSeriesToGeneratorsMapping, constantTimeSeriesContext.timeSeriesToGeneratorsMapping, variableTimeSeriesContext.timeSeriesToGeneratorsMapping);
        identifyConstantTimeSeries(version, context.timeSeriesToDanglingLinesMapping, constantTimeSeriesContext.timeSeriesToDanglingLinesMapping, variableTimeSeriesContext.timeSeriesToDanglingLinesMapping);
        identifyConstantTimeSeries(version, context.timeSeriesToHvdcLinesMapping, constantTimeSeriesContext.timeSeriesToHvdcLinesMapping, variableTimeSeriesContext.timeSeriesToHvdcLinesMapping);
        identifyConstantTimeSeries(version, context.timeSeriesToPhaseTapChangersMapping, constantTimeSeriesContext.timeSeriesToPhaseTapChangersMapping, variableTimeSeriesContext.timeSeriesToPhaseTapChangersMapping);
        identifyConstantTimeSeries(version, context.timeSeriesToBreakersMapping, constantTimeSeriesContext.timeSeriesToBreakersMapping, variableTimeSeriesContext.timeSeriesToBreakersMapping);
        identifyConstantTimeSeries(version, context.timeSeriesToTransformersMapping, constantTimeSeriesContext.timeSeriesToTransformersMapping, variableTimeSeriesContext.timeSeriesToTransformersMapping);
        identifyConstantTimeSeries(version, context.timeSeriesToRatioTapChangersMapping, constantTimeSeriesContext.timeSeriesToRatioTapChangersMapping, variableTimeSeriesContext.timeSeriesToRatioTapChangersMapping);
        identifyConstantTimeSeries(version, context.timeSeriesToLccConverterStationsMapping, constantTimeSeriesContext.timeSeriesToLccConverterStationsMapping, variableTimeSeriesContext.timeSeriesToLccConverterStationsMapping);
        identifyConstantTimeSeries(version, context.timeSeriesToVscConverterStationsMapping, constantTimeSeriesContext.timeSeriesToVscConverterStationsMapping, variableTimeSeriesContext.timeSeriesToVscConverterStationsMapping);
        identifyConstantTimeSeries(version, context.timeSeriesToLinesMapping, constantTimeSeriesContext.timeSeriesToLinesMapping, variableTimeSeriesContext.timeSeriesToLinesMapping);

        // Check if some equipment mappings are constant
        constantTimeSeriesContext.equipmentTimeSeries = new LinkedHashMap<>();
        variableTimeSeriesContext.equipmentTimeSeries = new LinkedHashMap<>();
        identifyConstantEquipmentTimeSeries(version, context.equipmentTimeSeries, constantTimeSeriesContext.equipmentTimeSeries, variableTimeSeriesContext.equipmentTimeSeries);
    }

    private void correctBaseCaseValues(int version) {
        network.getGeneratorStream()
                .filter(g -> config.getUnmappedGenerators().contains(g.getId()))
                .forEach(g -> {
                    final boolean isMinPUnmapped = config.getUnmappedMinPGenerators().contains(g.getId());
                    final boolean isMaxPUnmapped = config.getUnmappedMaxPGenerators().contains(g.getId());
                    correctUnmappedGenerator(isMinPUnmapped, isMaxPUnmapped, g, version);
                });
        network.getHvdcLineStream()
                .filter(l -> config.getUnmappedHvdcLines().contains(l.getId()))
                .forEach(l -> {
                    final boolean isMinPUnmapped = config.getUnmappedMinPHvdcLines().contains(l.getId());
                    final boolean isMaxPUnmapped = config.getUnmappedMaxPHvdcLines().contains(l.getId());
                    correctUnmappedHvdcLine(isMinPUnmapped, isMaxPUnmapped, l, version);
                });
    }

    private void mapToNetwork(MapperContext context, int version) {

        int firstPoint = parameters.getPointRange() != null ? parameters.getPointRange().lowerEndpoint() : 0;
        int lastPoint = parameters.getPointRange() != null ? parameters.getPointRange().upperEndpoint() : (table.getTableIndex().getPointCount() - 1);

        MapperContext constantTimeSeriesContext = new MapperContext();
        MapperContext variableTimeSeriesContext = new MapperContext();
        initConstantAndVariableMapperContext(version, context, constantTimeSeriesContext, variableTimeSeriesContext);

        // Correct base case values
        correctBaseCaseValues(version);

        // process constant time series
        if (checker != null) {
            checker.timeSeriesMappingStart(CONSTANT_VARIANT_ID, table.getTableIndex());
        }

        mapSinglePoint(version, CONSTANT_VARIANT_ID, firstPoint, constantTimeSeriesContext);

        // for unmapped equipments, keep base case value which is constant
        double constantBalance = calculateConstantBalance();

        if (checker != null) {
            checker.timeSeriesMappingEnd(CONSTANT_VARIANT_ID, table.getTableIndex(), constantBalance);
        }

        // process each time point
        for (int point = firstPoint; point <= lastPoint; point++) {

            if (checker != null) {
                checker.timeSeriesMappingStart(point, table.getTableIndex());
            }

            mapSinglePoint(version, point, point, variableTimeSeriesContext);

            if (checker != null) {
                checker.timeSeriesMappingEnd(point, table.getTableIndex(), 0);
            }
        }
    }

    private void verifyIdentifiable(Identifiable identifiable, String message, String id) {
        if (identifiable == null) {
            throw new TimeSeriesMappingException(String.format(message, id));
        }
    }

    private double getGeneratorInjection(String id) {
        Generator generator = network.getGenerator(id);
        verifyIdentifiable(generator, GENERATOR_NOT_FOUND_MESSAGE, id);
        return generator.getTargetP();
    }

    private double getLoadInjection(String id) {
        Load load = network.getLoad(id);
        verifyIdentifiable(load, LOAD_NOT_FOUND_MESSAGE, id);
        return -load.getP0();
    }

    private double getFixedLoadInjection(String id) {
        Load load = network.getLoad(id);
        verifyIdentifiable(load, LOAD_NOT_FOUND_MESSAGE, id);
        LoadDetail loadDetail = load.getExtension(LoadDetail.class);
        if (loadDetail == null) {
            throw new TimeSeriesMappingException(String.format(LOAD_DETAIL_NOT_FOUND_MESSAGE, id));
        }
        return -loadDetail.getFixedActivePower();
    }

    private double getVariableLoadInjection(String id) {
        Load load = network.getLoad(id);
        verifyIdentifiable(load, LOAD_NOT_FOUND_MESSAGE, id);
        LoadDetail loadDetail = load.getExtension(LoadDetail.class);
        if (loadDetail == null) {
            throw new TimeSeriesMappingException(String.format(LOAD_DETAIL_NOT_FOUND_MESSAGE, id));
        }
        return -loadDetail.getVariableActivePower();
    }

    private double getDanglingLineInjection(String id) {
        DanglingLine danglingLine = network.getDanglingLine(id);
        if (danglingLine == null) {
            throw new TimeSeriesMappingException(String.format("Dangling line '%s' not found", id));
        }
        return -danglingLine.getP0();
    }

    private double calculateConstantBalance() {
        double constantBalance = 0;
        Set unmappedGenerators = new HashSet<>(config.getUnmappedGenerators());
        unmappedGenerators.retainAll(config.getUnmappedMinPGenerators());
        unmappedGenerators.retainAll(config.getUnmappedMaxPGenerators());
        for (String id : unmappedGenerators) {
            constantBalance += getGeneratorInjection(id);
        }
        Set unmappedLoads = config.getUnmappedLoads();
        for (String id : unmappedLoads) {
            constantBalance += getLoadInjection(id);
        }
        for (String id : config.getUnmappedFixedActivePowerLoads()) {
            if (!unmappedLoads.contains(id)) {
                constantBalance += getFixedLoadInjection(id);
            }
        }
        for (String id : config.getUnmappedVariableActivePowerLoads()) {
            if (!unmappedLoads.contains(id)) {
                constantBalance += getVariableLoadInjection(id);
            }
        }
        for (String id : config.getUnmappedDanglingLines()) {
            constantBalance += getDanglingLineInjection(id);
        }
        return constantBalance;
    }

    boolean mapSinglePointIsEmpty(MapperContext context) {
        return context.timeSeriesToLoadsMapping.isEmpty() &&
               context.timeSeriesToGeneratorsMapping.isEmpty() &&
               context.timeSeriesToDanglingLinesMapping.isEmpty() &&
               context.timeSeriesToHvdcLinesMapping.isEmpty() &&
               context.timeSeriesToPhaseTapChangersMapping.isEmpty() &&
               context.timeSeriesToBreakersMapping.isEmpty() &&
               context.timeSeriesToTransformersMapping.isEmpty() &&
               context.timeSeriesToRatioTapChangersMapping.isEmpty() &&
               context.timeSeriesToLccConverterStationsMapping.isEmpty() &&
               context.timeSeriesToVscConverterStationsMapping.isEmpty() &&
               context.timeSeriesToLinesMapping.isEmpty() &&
               context.equipmentTimeSeries.isEmpty();
    }

    private void mapSinglePoint(int version, int variantId, int point, MapperContext context) {
        // only the constant variant could potentially be empty
        if (variantId == CONSTANT_VARIANT_ID && mapSinglePointIsEmpty(context)) {
            return;
        }

        // process time series for mapping
        mapToNetwork(version, variantId, point, context.timeSeriesToLoadsMapping);
        mapToNetwork(version, variantId, point, context.timeSeriesToGeneratorsMapping);
        mapToNetwork(version, variantId, point, context.timeSeriesToDanglingLinesMapping);
        mapToNetwork(version, variantId, point, context.timeSeriesToHvdcLinesMapping);
        mapToNetwork(version, variantId, point, context.timeSeriesToPhaseTapChangersMapping);
        mapToNetwork(version, variantId, point, context.timeSeriesToBreakersMapping);
        mapToNetwork(version, variantId, point, context.timeSeriesToTransformersMapping);
        mapToNetwork(version, variantId, point, context.timeSeriesToRatioTapChangersMapping);
        mapToNetwork(version, variantId, point, context.timeSeriesToLccConverterStationsMapping);
        mapToNetwork(version, variantId, point, context.timeSeriesToVscConverterStationsMapping);
        mapToNetwork(version, variantId, point, context.timeSeriesToLinesMapping);

        if (checker != null) {
            // generic mapping
            if (CONSTANT_VARIANT_ID != variantId) {
                checker.map(version, point, table);
            }

            // process equipment time series
            mapEquipmentToNetwork(version, point, context.equipmentTimeSeries);
        }
    }

    private void mapEquipmentToNetwork(int version, int point,
                                       Map> equipmentTimeSeries) {
        for (Map.Entry> e : equipmentTimeSeries.entrySet()) {
            String timeSeriesName = e.getKey().getName();
            int timeSeriesNum = e.getKey().getNum();
            double timeSeriesValue = table.getDoubleValue(version, timeSeriesNum, point);
            for (MappingKey equipment : e.getValue()) {
                checker.timeSeriesMappedToEquipment(point, timeSeriesName, network.getIdentifiable(equipment.getId()), equipment.getMappingVariable(), timeSeriesValue);
            }
        }
    }

    public void mapToNetwork(ReadOnlyTimeSeriesStore store, List observers) {

        checker = new TimeSeriesMapperChecker(observers, timeSeriesMappingLogger, parameters);

        checker.start();

        // create context
        MapperContext context = null;
        for (int version : parameters.getVersions()) {

            checker.versionStart(version);

            try {
                // load time series involved in the config in a table
                table = new TimeSeriesMappingConfigTableLoader(config, store).load(version, parameters.getRequiredTimeseries(), parameters.getPointRange());

                if (context == null) {
                    context = initMapperContext();
                }

                mapToNetwork(context, version);
            } finally {
                checker.versionEnd(version);
            }
        }

        checker.end();

        timeSeriesMappingLogger.printLogSynthesis();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy