AbstractTerminal.java

/**
 * Copyright (c) 2016, All partners of the iTesla project (http://www.itesla-project.eu/consortium)
 * 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.PowsyblException;
import com.powsybl.commons.ref.Ref;
import com.powsybl.iidm.network.*;
import com.powsybl.iidm.network.util.SwitchPredicates;
import gnu.trove.list.array.TDoubleArrayList;

import java.util.List;
import java.util.function.Predicate;

/**
 * @author Geoffroy Jamgotchian {@literal <geoffroy.jamgotchian at rte-france.com>}
 */
abstract class AbstractTerminal implements TerminalExt {

    protected static final String UNMODIFIABLE_REMOVED_EQUIPMENT = "Cannot modify removed equipment ";
    protected static final String CANNOT_ACCESS_BUS_REMOVED_EQUIPMENT = "Cannot access bus of removed equipment ";

    private Ref<? extends VariantManagerHolder> network;

    protected final ThreeSides side;

    protected AbstractConnectable connectable;

    protected VoltageLevelExt voltageLevel;

    protected final ReferrerManager<Terminal> referrerManager = new ReferrerManager<>(this);

    // attributes depending on the variant

    protected final TDoubleArrayList p;

    protected final TDoubleArrayList q;

    protected boolean removed = false;

    AbstractTerminal(Ref<? extends VariantManagerHolder> network, ThreeSides side) {
        this.side = side;
        this.network = network;
        int variantArraySize = network.get().getVariantManager().getVariantArraySize();
        p = new TDoubleArrayList(variantArraySize);
        q = new TDoubleArrayList(variantArraySize);
        for (int i = 0; i < variantArraySize; i++) {
            p.add(Double.NaN);
            q.add(Double.NaN);
        }
    }

    @Override
    public ThreeSides getSide() {
        return side;
    }

    protected String getAttributeSideSuffix() {
        return "" + (side != null ? side.getNum() : "");
    }

    protected VariantManagerHolder getVariantManagerHolder() {
        return network.get();
    }

    @Override
    public AbstractConnectable getConnectable() {
        return connectable;
    }

    @Override
    public void setConnectable(AbstractConnectable connectable) {
        this.connectable = connectable;
    }

    @Override
    public VoltageLevelExt getVoltageLevel() {
        if (removed) {
            throw new PowsyblException("Cannot access voltage level of removed equipment " + connectable.id);
        }
        return voltageLevel;
    }

    @Override
    public void setVoltageLevel(VoltageLevelExt voltageLevel) {
        this.voltageLevel = voltageLevel;
        if (voltageLevel != null) {
            network = voltageLevel.getNetworkRef();
        }
    }

    @Override
    public double getP() {
        if (removed) {
            throw new PowsyblException("Cannot access p of removed equipment " + connectable.id);
        }
        return p.get(network.get().getVariantIndex());
    }

    @Override
    public Terminal setP(double p) {
        if (removed) {
            throw new PowsyblException(UNMODIFIABLE_REMOVED_EQUIPMENT + connectable.id);
        }
        if (connectable.getType() == IdentifiableType.BUSBAR_SECTION) {
            throw new ValidationException(connectable, "cannot set active power on a busbar section");
        }
        int variantIndex = network.get().getVariantIndex();
        double oldValue = this.p.set(variantIndex, p);
        String variantId = network.get().getVariantManager().getVariantId(variantIndex);
        getConnectable().notifyUpdate(() -> "p" + getAttributeSideSuffix(), variantId, oldValue, p);
        return this;
    }

    @Override
    public double getQ() {
        if (removed) {
            throw new PowsyblException("Cannot access q of removed equipment " + connectable.id);
        }
        return q.get(network.get().getVariantIndex());
    }

    @Override
    public Terminal setQ(double q) {
        if (removed) {
            throw new PowsyblException(UNMODIFIABLE_REMOVED_EQUIPMENT + connectable.id);
        }
        if (connectable.getType() == IdentifiableType.BUSBAR_SECTION) {
            throw new ValidationException(connectable, "cannot set reactive power on a busbar section");
        }
        int variantIndex = network.get().getVariantIndex();
        double oldValue = this.q.set(variantIndex, q);
        String variantId = network.get().getVariantManager().getVariantId(variantIndex);
        getConnectable().notifyUpdate(() -> "q" + getAttributeSideSuffix(), variantId, oldValue, q);
        return this;
    }

    protected abstract double getV();

    @Override
    public double getI() {
        if (removed) {
            throw new PowsyblException("Cannot access i of removed equipment " + connectable.id);
        }
        if (connectable.getType() == IdentifiableType.BUSBAR_SECTION) {
            return 0;
        }
        int variantIndex = network.get().getVariantIndex();
        return Math.hypot(p.get(variantIndex), q.get(variantIndex))
                / (Math.sqrt(3.) * getV() / 1000);
    }

    @Override
    public boolean connect() {
        return connect(SwitchPredicates.IS_NONFICTIONAL_BREAKER);
    }

    /**
     * Try to connect the terminal.<br/>
     * Depends on the working variant.
     * @param isTypeSwitchToOperate Predicate telling if a switch is considered operable. Examples of predicates are available in the class {@link SwitchPredicates}
     * @return true if terminal has been connected, false otherwise
     * @see VariantManager
     */
    @Override
    public boolean connect(Predicate<Switch> isTypeSwitchToOperate) {
        if (removed) {
            throw new PowsyblException(UNMODIFIABLE_REMOVED_EQUIPMENT + connectable.id);
        }
        int variantIndex = getVariantManagerHolder().getVariantIndex();
        String variantId = getVariantManagerHolder().getVariantManager().getVariantId(variantIndex);
        boolean connectedBefore = isConnected();
        connectable.notifyUpdate("beginConnect", variantId, connectedBefore, null);
        boolean connected = voltageLevel.getTopologyModel().connect(this, isTypeSwitchToOperate);
        boolean connectedAfter = isConnected();
        connectable.notifyUpdate("endConnect", variantId, null, connectedAfter);
        return connected;
    }

    @Override
    public boolean disconnect() {
        return disconnect(SwitchPredicates.IS_CLOSED_BREAKER);
    }

    /**
     * Disconnect the terminal.<br/>
     * Depends on the working variant.
     * @param isSwitchOpenable Predicate telling if a switch is considered openable. Examples of predicates are available in the class {@link SwitchPredicates}
     * @return true if terminal has been disconnected, false otherwise
     * @see VariantManager
     */
    @Override
    public boolean disconnect(Predicate<Switch> isSwitchOpenable) {
        if (removed) {
            throw new PowsyblException(UNMODIFIABLE_REMOVED_EQUIPMENT + connectable.id);
        }
        int variantIndex = getVariantManagerHolder().getVariantIndex();
        String variantId = getVariantManagerHolder().getVariantManager().getVariantId(variantIndex);
        boolean disconnectedBefore = !isConnected();
        connectable.notifyUpdate("beginDisconnect", variantId, disconnectedBefore, null);
        boolean disconnected = voltageLevel.getTopologyModel().disconnect(this, isSwitchOpenable);
        boolean disconnectedAfter = !isConnected();
        connectable.notifyUpdate("endDisconnect", variantId, null, disconnectedAfter);
        return disconnected;
    }

    @Override
    public void extendVariantArraySize(int initVariantArraySize, int number, int sourceIndex) {
        p.ensureCapacity(p.size() + number);
        q.ensureCapacity(q.size() + number);
        for (int i = 0; i < number; i++) {
            p.add(p.get(sourceIndex));
            q.add(q.get(sourceIndex));
        }
    }

    @Override
    public void reduceVariantArraySize(int number) {
        p.remove(p.size() - number, number);
        q.remove(q.size() - number, number);
    }

    @Override
    public void deleteVariantArrayElement(int index) {
        // nothing to do
    }

    @Override
    public void allocateVariantArrayElement(int[] indexes, int sourceIndex) {
        for (int index : indexes) {
            p.set(index, p.get(sourceIndex));
            q.set(index, q.get(sourceIndex));
        }
    }

    @Override
    public void remove() {
        removed = true;
    }

    @Override
    public ReferrerManager<Terminal> getReferrerManager() {
        return referrerManager;
    }

    @Override
    public List<Object> getReferrers() {
        return referrerManager.getReferrers().stream().map(r -> (Object) r).toList();
    }
}