AbstractLoadingLimitsAdder.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/.
 * SPDX-License-Identifier: MPL-2.0
 */
package com.powsybl.iidm.network.impl;

import com.powsybl.commons.report.ReportNode;
import com.powsybl.iidm.network.*;
import com.powsybl.iidm.network.util.LoadingLimitsUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

import static java.lang.Integer.MAX_VALUE;

/**
 * @author Miora Ralambotiana {@literal <miora.ralambotiana at rte-france.com>}
 */
abstract class AbstractLoadingLimitsAdder<L extends LoadingLimits, A extends LoadingLimitsAdder<L, A>> implements LoadingLimitsAdder<L, A> {
    private static final Logger LOGGER = LoggerFactory.getLogger(AbstractLoadingLimitsAdder.class);

    protected final Validable validable;
    private final String ownerId;

    protected double permanentLimit = Double.NaN;

    protected final TreeMap<Integer, LoadingLimits.TemporaryLimit> temporaryLimits = new TreeMap<>(LoadingLimitsUtil.ACCEPTABLE_DURATION_COMPARATOR);

    public class TemporaryLimitAdderImpl<B extends LoadingLimitsAdder<L, B>> implements TemporaryLimitAdder<B> {

        private String name;

        private double value = Double.NaN;

        private Integer acceptableDuration;

        private boolean fictitious = false;

        private boolean ensureNameUnicity = false;

        @Override
        public TemporaryLimitAdder<B> setName(String name) {
            this.name = name;
            return this;
        }

        @Override
        public TemporaryLimitAdder<B> setValue(double value) {
            this.value = value;
            return this;
        }

        @Override
        public TemporaryLimitAdder<B> setAcceptableDuration(int acceptableDuration) {
            this.acceptableDuration = acceptableDuration;
            return this;
        }

        @Override
        public TemporaryLimitAdder<B> setFictitious(boolean fictitious) {
            this.fictitious = fictitious;
            return this;
        }

        @Override
        public TemporaryLimitAdder<B> ensureNameUnicity() {
            this.ensureNameUnicity = true;
            return this;
        }

        @Override
        public B endTemporaryLimit() {
            if (Double.isNaN(value)) {
                throw new ValidationException(validable, "temporary limit value is not set");
            }
            if (value < 0) {
                throw new ValidationException(validable, "temporary limit value must be >= 0");
            }
            if (value == 0) {
                LOGGER.info("{}temporary limit value is set to 0", validable.getMessageHeader());
            }
            if (acceptableDuration == null) {
                throw new ValidationException(validable, "acceptable duration is not set");
            }
            if (acceptableDuration < 0) {
                throw new ValidationException(validable, "acceptable duration must be >= 0");
            }
            checkAndGetUniqueName();
            temporaryLimits.put(acceptableDuration, new AbstractLoadingLimits.TemporaryLimitImpl(name, value, acceptableDuration, fictitious));
            return (B) AbstractLoadingLimitsAdder.this;
        }

        private void checkAndGetUniqueName() {
            if (name == null) {
                throw new ValidationException(validable, "name is not set");
            }
            if (ensureNameUnicity) {
                int i = 0;
                String uniqueName = name;
                while (i < Integer.MAX_VALUE && nameExists(uniqueName)) {
                    uniqueName = name + "#" + i;
                    i++;
                }
                name = uniqueName;
            }
        }

        private boolean nameExists(String name) {
            return temporaryLimits.values().stream().anyMatch(t -> t.getName().equals(name));
        }
    }

    AbstractLoadingLimitsAdder(Validable validable, String ownerId) {
        this.validable = Objects.requireNonNull(validable);
        this.ownerId = ownerId;
    }

    @Override
    public A setPermanentLimit(double permanentLimit) {
        this.permanentLimit = permanentLimit;
        return (A) this;
    }

    @Override
    public TemporaryLimitAdder<A> beginTemporaryLimit() {
        return new TemporaryLimitAdderImpl<>();
    }

    @Override
    public double getPermanentLimit() {
        return permanentLimit;
    }

    @Override
    public double getTemporaryLimitValue(int acceptableDuration) {
        return Optional.ofNullable(temporaryLimits.get(acceptableDuration)).map(LoadingLimits.TemporaryLimit::getValue).orElse(Double.NaN);
    }

    @Override
    public boolean hasTemporaryLimits() {
        return !temporaryLimits.isEmpty();
    }

    protected void checkAndUpdateValidationLevel(NetworkImpl network) {
        network.setValidationLevelIfGreaterThan(checkLoadingLimits(network.getMinValidationLevel(), network.getReportNodeContext().getReportNode()));
    }

    protected ValidationLevel checkLoadingLimits(ValidationLevel validationLevel, ReportNode reportNode) {
        return ValidationUtil.checkLoadingLimits(validable, permanentLimit, temporaryLimits.values(), validationLevel, reportNode);
    }

    private Optional<LoadingLimits.TemporaryLimit> getTemporaryLimitByName(String name) {
        return temporaryLimits.values().stream().filter(l -> l.getName().equals(name))
                .findFirst();
    }

    @Override
    public double getTemporaryLimitValue(String name) {
        return getTemporaryLimitByName(name).map(LoadingLimits.TemporaryLimit::getValue).orElse(Double.NaN);
    }

    @Override
    public int getTemporaryLimitAcceptableDuration(String name) {
        return getTemporaryLimitByName(name).map(LoadingLimits.TemporaryLimit::getAcceptableDuration).orElse(MAX_VALUE);
    }

    @Override
    public double getLowestTemporaryLimitValue() {
        return temporaryLimits.values().stream().map(LoadingLimits.TemporaryLimit::getValue).min(Double::compareTo).orElse(Double.NaN);
    }

    @Override
    public Collection<String> getTemporaryLimitNames() {
        return temporaryLimits.values().stream().map(LoadingLimits.TemporaryLimit::getName).toList();
    }

    @Override
    public void removeTemporaryLimit(String name) {
        temporaryLimits.values().removeIf(l -> l.getName().equals(name));
    }

    @Override
    public String getOwnerId() {
        return ownerId;
    }

}