LfActionUtils.java

/**
 * Copyright (c) 2025, Coreso SA (https://www.coreso.eu/) and TSCNET Services GmbH (https://www.tscnet.eu/)
 * Copyright (c) 2022-2025, 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.openloadflow.network.action;

import com.powsybl.action.*;
import com.powsybl.iidm.network.Network;
import com.powsybl.openloadflow.graph.GraphConnectivity;
import com.powsybl.openloadflow.network.*;
import com.powsybl.openloadflow.util.Reports;

import java.util.List;
import java.util.Objects;

import static com.powsybl.openloadflow.network.action.AbstractLfBranchAction.updateBusesAndBranchStatus;

/**
 * @author Bertrand Rix {@literal <bertrand.rix at artelys.com>}
 * @author Anne Tilloy {@literal <anne.tilloy at rte-france.com>}
 * @author Jean-Luc Bouchot {@literal <jlbouchot at gmail.com>}
 */
public final class LfActionUtils {

    private LfActionUtils() {
    }

    public static LfAction createLfAction(Action action, Network network, boolean breakers, LfNetwork lfNetwork) {
        Objects.requireNonNull(action);
        Objects.requireNonNull(network);
        return switch (action.getType()) {
            case SwitchAction.NAME -> new LfSwitchAction(action.getId(), (SwitchAction) action, lfNetwork);
            case TerminalsConnectionAction.NAME ->
                new LfTerminalsConnectionAction(action.getId(), (TerminalsConnectionAction) action, lfNetwork);
            case PhaseTapChangerTapPositionAction.NAME ->
                new LfPhaseTapChangerAction(action.getId(), (PhaseTapChangerTapPositionAction) action, lfNetwork);
            case RatioTapChangerTapPositionAction.NAME ->
                new LfRatioTapChangerAction(action.getId(), (RatioTapChangerTapPositionAction) action, lfNetwork);
            case LoadAction.NAME ->
                new LfLoadAction(action.getId(), (LoadAction) action, network, breakers);
            case GeneratorAction.NAME -> new LfGeneratorAction(action.getId(), (GeneratorAction) action, lfNetwork);
            case HvdcAction.NAME -> new LfHvdcAction(action.getId(), (HvdcAction) action);
            case ShuntCompensatorPositionAction.NAME ->
                new LfShuntCompensatorPositionAction(action.getId(), (ShuntCompensatorPositionAction) action);
            case AreaInterchangeTargetAction.NAME ->
                new LfAreaInterchangeTargetAction(action.getId(), (AreaInterchangeTargetAction) action);
            default -> throw new UnsupportedOperationException("Unsupported action type: " + action.getType());
        };
    }

    public static void applyListOfActions(List<LfAction> actions, LfNetwork network, LfContingency contingency, LfNetworkParameters networkParameters) {
        Objects.requireNonNull(actions);
        Objects.requireNonNull(network);

        // first apply action modifying connectivity
        List<LfAction> branchActions = actions.stream()
            .filter(action -> action instanceof AbstractLfBranchAction<?>)
            .toList();
        updateConnectivity(branchActions, network, contingency);

        // then process remaining changes of actions
        actions.stream()
            .filter(action -> !(action instanceof AbstractLfBranchAction<?>))
            .forEach(action -> {
                if (!action.apply(network, contingency, networkParameters)) {
                    Reports.reportActionApplicationFailure(action.getId(), contingency.getId(), network.getReportNode());
                }
            });
    }

    private static void updateConnectivity(List<LfAction> branchActions, LfNetwork network, LfContingency contingency) {
        GraphConnectivity<LfBus, LfBranch> connectivity = network.getConnectivity();

        // re-update connectivity according to post contingency state (revert after LfContingency apply)
        connectivity.startTemporaryChanges();
        contingency.getDisabledNetwork().getBranches().forEach(connectivity::removeEdge);

        // update connectivity according to post action state
        connectivity.startTemporaryChanges();

        branchActions.forEach(action -> {
            if (!((AbstractLfBranchAction<?>) action).applyOnConnectivity(connectivity)) {
                Reports.reportActionApplicationFailure(action.getId(), contingency.getId(), network.getReportNode());
            }
        });

        updateBusesAndBranchStatus(connectivity);

        // reset connectivity to discard post contingency connectivity and post action connectivity
        connectivity.undoTemporaryChanges();
        connectivity.undoTemporaryChanges();
    }

}