PstRangeActionCreator.java

/*
 * 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.openrao.data.crac.io.nc.craccreator.remedialaction;

import com.powsybl.openrao.data.crac.io.nc.craccreator.NcCracUtils;
import com.powsybl.openrao.data.crac.io.nc.craccreator.constants.PropertyReference;
import com.powsybl.openrao.data.crac.io.nc.craccreator.constants.RelativeDirectionKind;
import com.powsybl.openrao.data.crac.io.nc.craccreator.constants.ValueOffsetKind;
import com.powsybl.openrao.data.crac.io.nc.objects.StaticPropertyRange;
import com.powsybl.openrao.data.crac.io.nc.objects.TapPositionAction;
import com.powsybl.openrao.data.crac.api.Crac;
import com.powsybl.openrao.data.crac.api.range.RangeType;
import com.powsybl.openrao.data.crac.api.range.TapRangeAdder;
import com.powsybl.openrao.data.crac.api.rangeaction.PstRangeActionAdder;
import com.powsybl.openrao.data.crac.io.commons.api.ImportStatus;
import com.powsybl.openrao.data.crac.io.commons.OpenRaoImportException;
import com.powsybl.openrao.data.crac.io.commons.iidm.IidmPstHelper;
import com.powsybl.iidm.network.Network;

import java.util.*;

/**
 * @author Mohamed Ben-rejeb {@literal <mohamed.ben-rejeb at rte-france.com>}
 */
public class PstRangeActionCreator {
    private final Crac crac;
    private final Network network;
    private final Map<String, String> pstPerTapChanger;

    public PstRangeActionCreator(Crac crac, Network network, Map<String, String> pstPerTapChanger) {
        this.crac = crac;
        this.network = network;
        this.pstPerTapChanger = pstPerTapChanger;
    }

    public PstRangeActionAdder getPstRangeActionAdder(boolean isGroup, String elementaryActionsAggregatorId, TapPositionAction nativeTapPositionAction, Map<String, Set<StaticPropertyRange>> linkedStaticPropertyRanges, String remedialActionId) {
        PstRangeActionAdder pstRangeActionAdder = crac.newPstRangeAction().withId(remedialActionId);
        Set<StaticPropertyRange> linkedStaticPropertyRangesToTapPositionAction = new HashSet<>();
        if (linkedStaticPropertyRanges.containsKey(nativeTapPositionAction.mrid())) {
            linkedStaticPropertyRangesToTapPositionAction = linkedStaticPropertyRanges.get(nativeTapPositionAction.mrid());
        }
        addTapPositionElementaryAction(isGroup, elementaryActionsAggregatorId, linkedStaticPropertyRangesToTapPositionAction, remedialActionId, pstRangeActionAdder, nativeTapPositionAction);
        return pstRangeActionAdder;
    }

    private void addTapPositionElementaryAction(boolean isGroup, String elementaryActionsAggregatorId, Set<StaticPropertyRange> linkedStaticPropertyRangesToTapPositionAction, String remedialActionId, PstRangeActionAdder pstRangeActionAdder, TapPositionAction nativeTapPositionAction) {
        if (!nativeTapPositionAction.normalEnabled()) {
            throw new OpenRaoImportException(ImportStatus.NOT_FOR_RAO, String.format("Remedial action %s will not be imported because the field normalEnabled in TapPositionAction is set to false", remedialActionId));
        }
        NcCracUtils.checkPropertyReference(remedialActionId, "TapPositionAction", PropertyReference.TAP_CHANGER, nativeTapPositionAction.propertyReference());
        if (!pstPerTapChanger.containsKey(nativeTapPositionAction.tapChangerId())) {
            throw new OpenRaoImportException(ImportStatus.ELEMENT_NOT_FOUND_IN_NETWORK, String.format("Remedial action %s will not be imported because no PowerTransformer was found in the network for TapChanger %s", remedialActionId, nativeTapPositionAction.tapChangerId()));
        }
        String pstId = pstPerTapChanger.get(nativeTapPositionAction.tapChangerId());
        IidmPstHelper iidmPstHelper = new IidmPstHelper(pstId, network);
        if (!iidmPstHelper.isValid()) {
            throw new OpenRaoImportException(ImportStatus.ELEMENT_NOT_FOUND_IN_NETWORK, String.format("Remedial action %s will not be imported because %s", remedialActionId, iidmPstHelper.getInvalidReason()));
        }

        if (isGroup) {
            pstRangeActionAdder.withGroupId(elementaryActionsAggregatorId);
        }
        pstRangeActionAdder
            .withNetworkElement(pstId)
            .withInitialTap(iidmPstHelper.getInitialTap())
            .withTapToAngleConversionMap(iidmPstHelper.getTapToAngleConversionMap());

        if (!linkedStaticPropertyRangesToTapPositionAction.isEmpty()) {
            Optional<Integer> normalValueUp = Optional.empty();
            Optional<Integer> normalValueDown = Optional.empty();
            for (StaticPropertyRange nativeStaticPropertyRange : linkedStaticPropertyRangesToTapPositionAction) {
                NcCracUtils.checkPropertyReference(remedialActionId, "StaticPropertyRange", PropertyReference.TAP_CHANGER, nativeStaticPropertyRange.propertyReference());

                if (!ValueOffsetKind.ABSOLUTE.toString().equals(nativeStaticPropertyRange.valueKind())) {
                    throw new OpenRaoImportException(ImportStatus.INCONSISTENCY_IN_DATA, String.format("Remedial action %s will not be imported because StaticPropertyRange has wrong value of valueKind, the only allowed value is absolute", remedialActionId));
                } else {
                    int normalValue = (int) nativeStaticPropertyRange.normalValue();
                    if (RelativeDirectionKind.DOWN.toString().equals(nativeStaticPropertyRange.direction())) {
                        normalValueDown.ifPresent(value -> {
                            throw new OpenRaoImportException(ImportStatus.INCONSISTENCY_IN_DATA, String.format("Remedial action %s will not be imported because there is more than ONE StaticPropertyRange with direction RelativeDirectionKind.down", remedialActionId));
                        });
                        normalValueDown = Optional.of(normalValue);
                    } else if (RelativeDirectionKind.UP.toString().equals(nativeStaticPropertyRange.direction())) {
                        normalValueUp.ifPresent(value -> {
                            throw new OpenRaoImportException(ImportStatus.INCONSISTENCY_IN_DATA, String.format("Remedial action %s will not be imported because there is more than ONE StaticPropertyRange with direction RelativeDirectionKind.up", remedialActionId));
                        });
                        normalValueUp = Optional.of(normalValue);
                    } else {
                        throw new OpenRaoImportException(ImportStatus.INCONSISTENCY_IN_DATA, String.format("Remedial action %s will not be imported because StaticPropertyRange has wrong value of direction, the only allowed values are RelativeDirectionKind.up and RelativeDirectionKind.down", remedialActionId));
                    }
                }
            }
            TapRangeAdder tapRangeAdder = pstRangeActionAdder.newTapRange().withRangeType(RangeType.ABSOLUTE);
            normalValueDown.ifPresent(tapRangeAdder::withMinTap);
            normalValueUp.ifPresent(tapRangeAdder::withMaxTap);
            tapRangeAdder.add();
        }
    }

}