eu.itesla_project.online.StateAnalyzer.java Source code

Java tutorial

Introduction

Here is the source code for eu.itesla_project.online.StateAnalyzer.java

Source

/**
 * Copyright (c) 2016, All partners of the iTesla project (http://www.itesla-project.eu/consortium)
 * Copyright (c) 2016, 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 eu.itesla_project.online;

import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Function;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;

import eu.itesla_project.iidm.network.Network;
import eu.itesla_project.iidm.network.StateManager;
import eu.itesla_project.loadflow.api.LoadFlow;
import eu.itesla_project.loadflow.api.LoadFlowResult;
import eu.itesla_project.modules.constraints.ConstraintsModifier;
import eu.itesla_project.modules.contingencies.ActionParameters;
import eu.itesla_project.contingency.Contingency;
import eu.itesla_project.modules.mcla.MontecarloSampler;
import eu.itesla_project.modules.online.OnlineDb;
import eu.itesla_project.modules.online.OnlineRulesFacade;
import eu.itesla_project.modules.online.OnlineStep;
import eu.itesla_project.modules.online.OnlineWorkflowParameters;
import eu.itesla_project.modules.online.RulesFacadeResults;
import eu.itesla_project.modules.online.StateStatus;
import eu.itesla_project.modules.optimizer.CCOFinalStatus;
import eu.itesla_project.modules.optimizer.CorrectiveControlOptimizer;
import eu.itesla_project.modules.optimizer.CorrectiveControlOptimizerResult;
import eu.itesla_project.modules.optimizer.PostContingencyState;
import eu.itesla_project.security.LimitViolation;
import eu.itesla_project.security.Security;
import eu.itesla_project.security.Security.CurrentLimitType;
import eu.itesla_project.simulation.securityindexes.SecurityIndex;
import eu.itesla_project.simulation.ImpactAnalysis;
import eu.itesla_project.simulation.ImpactAnalysisResult;
import eu.itesla_project.simulation.Stabilization;
import eu.itesla_project.simulation.StabilizationResult;
import eu.itesla_project.simulation.StabilizationStatus;
import eu.itesla_project.online.OnlineWorkflowImpl.StateAnalizerListener;

/**
 *
 * @author Quinary <itesla@quinary.com>
 */
public class StateAnalyzer implements Callable<Void> {

    Logger logger = LoggerFactory.getLogger(StateAnalyzer.class);

    private OnlineWorkflowContext context;
    private MontecarloSampler sampler;
    private LoadFlow loadFlow;
    private OnlineRulesFacade rulesFacade;
    private CorrectiveControlOptimizer optimizer;
    private Stabilization stabilization;
    private ImpactAnalysis impactAnalysis;
    private OnlineDb onlineDb;
    private Integer stateId;
    private OnlineWorkflowParameters parameters;
    private StateAnalizerListener stateListener;
    private EnumMap<OnlineTaskType, OnlineTaskStatus> status = new EnumMap<OnlineTaskType, OnlineTaskStatus>(
            OnlineTaskType.class);
    Map<String, Boolean> loadflowResults = new HashMap<String, Boolean>();
    private ConstraintsModifier constraintsModifier;

    public StateAnalyzer(OnlineWorkflowContext context, MontecarloSampler sampler, LoadFlow loadFlow,
            OnlineRulesFacade rulesFacade, CorrectiveControlOptimizer optimizer, Stabilization stabilization,
            ImpactAnalysis impactAnalysis, OnlineDb onlineDb, StateAnalizerListener stateListener,
            ConstraintsModifier constraintsModifier, OnlineWorkflowParameters parameters) {
        this.context = context;
        this.sampler = sampler;
        this.loadFlow = loadFlow;
        this.rulesFacade = rulesFacade;
        this.optimizer = optimizer;
        this.stabilization = stabilization;
        this.impactAnalysis = impactAnalysis;
        this.onlineDb = onlineDb;
        this.stateListener = stateListener;
        this.constraintsModifier = constraintsModifier;
        this.parameters = parameters;
        //stateId = "STATE-" + context.incrementStateCounter();
        stateId = context.incrementStateCounter();
        initStatus();
        stateListener.onUpdate(stateId, status, context.timeHorizon);
    }

    private void initStatus() {
        status.put(OnlineTaskType.SAMPLING, OnlineTaskStatus.IDLE);
        status.put(OnlineTaskType.LOAD_FLOW, OnlineTaskStatus.IDLE);
        status.put(OnlineTaskType.SECURITY_RULES, OnlineTaskStatus.IDLE);
        status.put(OnlineTaskType.OPTIMIZER, OnlineTaskStatus.IDLE);
        status.put(OnlineTaskType.TIME_DOMAIN_SIM, OnlineTaskStatus.IDLE);
    }

    @Override
    public Void call() throws Exception {
        OnlineTaskType currentStatus = OnlineTaskType.SAMPLING;

        try {
            // create new state
            logger.info("Analyzing state {}", stateId);
            String stateIdStr = String.valueOf(stateId);
            context.getNetwork().getStateManager().cloneState(StateManager.INITIAL_STATE_ID, stateIdStr);
            context.getNetwork().getStateManager().setWorkingState(stateIdStr);
            // sample
            logger.info("{}: sampling started", stateId);
            status.put(currentStatus, OnlineTaskStatus.RUNNING);
            stateListener.onUpdate(stateId, status, context.timeHorizon);
            if (!parameters.analyseBasecase() || stateId > 0)
                sampler.sample();
            else
                logger.info("{}: state = basecase", stateId);
            status.put(currentStatus, OnlineTaskStatus.SUCCESS);
            stateListener.onUpdate(stateId, status, context.timeHorizon);
            logger.info("{}: sampling terminated", stateId);

            // complete state with loadflow
            currentStatus = OnlineTaskType.LOAD_FLOW;
            status.put(currentStatus, OnlineTaskStatus.RUNNING);
            stateListener.onUpdate(stateId, status, context.timeHorizon);
            logger.info("{}: loadflow started", stateId);
            LoadFlowResult result = loadFlow.run();
            status.put(currentStatus, result.isOk() ? OnlineTaskStatus.SUCCESS : OnlineTaskStatus.FAILED);
            stateListener.onUpdate(stateId, status, context.timeHorizon);
            logger.info("{}: loadflow terminated", stateId);
            if (result.getMetrics() != null) {
                logger.info("{}: loadflow metrics: {}", stateId, result.getMetrics());
                if (!result.getMetrics().isEmpty())
                    onlineDb.storeMetrics(context.getWorkflowId(), stateId, OnlineStep.LOAD_FLOW,
                            result.getMetrics());
            }
            status.put(currentStatus, result.isOk() ? OnlineTaskStatus.SUCCESS : OnlineTaskStatus.FAILED);

            if (parameters.storeStates()) {
                logger.info("{}: storing state in online db", stateId);
                onlineDb.storeState(context.getWorkflowId(), stateId, context.getNetwork());
            }

            if (result.isOk()) {
                // stores violations only if loadflow converges
                logger.info("{}: storing violations after {} in online db", stateId, OnlineStep.LOAD_FLOW);
                List<LimitViolation> violations = Security.checkLimits(context.getNetwork(), CurrentLimitType.PATL,
                        parameters.getLimitReduction());
                if (violations != null && !violations.isEmpty()) {
                    onlineDb.storeViolations(context.getWorkflowId(), stateId, OnlineStep.LOAD_FLOW, violations);
                    if (parameters.isHandleViolationsInN()) {
                        if (parameters.analyseBasecase() && stateId == 0) {
                            constraintsModifier.looseConstraints(stateIdStr, violations,
                                    parameters.getConstraintMargin(), true); // loose constraints on state 0 (=basecase)
                        } else {
                            constraintsModifier.looseConstraints(stateIdStr, violations); // loose constraints on sampled state
                        }
                    }
                } else
                    logger.info("{}: no violations after {}", stateId, OnlineStep.LOAD_FLOW);

                stateListener.onUpdate(stateId, status, context.timeHorizon);
                // check state against contingencies
                boolean isStateSafe = true;
                List<Contingency> contingenciesForOptimizer = new ArrayList<Contingency>();
                List<Contingency> contingenciesForSimulator = new ArrayList<Contingency>();
                currentStatus = OnlineTaskType.SECURITY_RULES;
                status.put(currentStatus, OnlineTaskStatus.RUNNING);
                stateListener.onUpdate(stateId, status, context.timeHorizon);

                for (Contingency contingency : context.getContingenciesToAnalyze()) {
                    logger.info("{}: check security rules against contingency {}", stateId, contingency.getId());
                    RulesFacadeResults rulesResults = rulesFacade.evaluate(contingency, context.getNetwork());
                    if (rulesResults.areRulesAvailable()) {
                        if (rulesResults.getStateStatus() == StateStatus.SAFE) { // check if this contingency is ok
                            logger.info("{}: is safe for contingency {}", stateId, contingency.getId());
                            if (parameters.validation()) { // if validation
                                // send all [contingency,state] pairs to simulation
                                contingenciesForSimulator.add(contingency);
                                // send safe [contingency,state] pairs to optimizer
                                contingenciesForOptimizer.add(contingency);
                            }
                        } else if (rulesResults.getStateStatus() == StateStatus.SAFE_WITH_CORRECTIVE_ACTIONS) { // check if this contingency could be ok with corrective actions
                            logger.info("{}: requires corrective actions for contingency {}", stateId,
                                    contingency.getId());
                            isStateSafe = false;
                            contingenciesForOptimizer.add(contingency);
                            if (parameters.validation()) { // if validation
                                // send all [contingency,state] pairs to simulation
                                contingenciesForSimulator.add(contingency);
                            }
                        } else { // we need to perform a time-domain simulation on this state for this contingency
                            logger.info("{}: requires time-domain simulation for contingency {}", stateId,
                                    contingency.getId());
                            isStateSafe = false;
                            contingenciesForSimulator.add(contingency);
                        }
                    } else {
                        logger.warn("{}: no valid rules for contingency {}", stateId, contingency.getId());
                        contingenciesForSimulator.add(contingency);
                    }

                    synchronized (context.getSecurityRulesResults()) {
                        context.getSecurityRulesResults().addStateWithSecurityRulesResults(contingency.getId(),
                                stateId, rulesResults.getStateStatus(), rulesResults.getIndexesResults(),
                                rulesResults.areRulesAvailable(), rulesResults.getInvalidRules());
                        stateListener.onSecurityRulesApplicationResults(contingency.getId(), stateId, context);
                    }

                    if (parameters.validation()) {
                        RulesFacadeResults wcaRulesResults = rulesFacade.wcaEvaluate(contingency,
                                context.getNetwork());
                        synchronized (context.getWcaSecurityRulesResults()) {
                            context.getWcaSecurityRulesResults().addStateWithSecurityRulesResults(
                                    contingency.getId(), stateId, wcaRulesResults.getStateStatus(),
                                    wcaRulesResults.getIndexesResults(), rulesResults.areRulesAvailable(),
                                    rulesResults.getInvalidRules());
                        }
                    }
                }
                status.put(currentStatus, OnlineTaskStatus.SUCCESS);
                stateListener.onUpdate(stateId, status, context.timeHorizon);
                computeAndStorePostContingencyViolations(context.getNetwork(), context.getContingenciesToAnalyze());
                if (isStateSafe && !parameters.validation()) {
                    // state is safe: stop analysis and destroy the state
                    logger.info("{}: is safe for every contingency: stopping analysis", stateId);
                    //context.getNetwork().getStateManager().removeState(stateIdStr); // the state is still needed
                    return null;
                } else {
                    if (contingenciesForOptimizer.size() > 0) {
                        // perform corrective control optimization
                        currentStatus = OnlineTaskType.OPTIMIZER;
                        status.put(currentStatus, OnlineTaskStatus.RUNNING);
                        stateListener.onUpdate(stateId, status, context.timeHorizon);
                        logger.info("{}: corrective control optimization started - working on {} contingencies",
                                stateId, contingenciesForOptimizer.size());
                        runOptimizer(context.getNetwork(), contingenciesForOptimizer, contingenciesForSimulator,
                                context.getResults());
                        // the optimizer could possibly have changed the network working state: set the original one
                        context.getNetwork().getStateManager().setWorkingState(stateIdStr);
                        stateListener.onOptimizerResults(stateId, context);
                        logger.info("{}: corrective control optimization terminated", stateId);
                        status.put(OnlineTaskType.OPTIMIZER, OnlineTaskStatus.SUCCESS);
                        stateListener.onUpdate(stateId, status, context.timeHorizon);
                    }
                    if (contingenciesForSimulator.size() > 0) {
                        // perform time-domain simulation
                        currentStatus = OnlineTaskType.TIME_DOMAIN_SIM;
                        status.put(currentStatus, OnlineTaskStatus.RUNNING);
                        stateListener.onUpdate(stateId, status, context.timeHorizon);
                        logger.info("{}: time-domain simulation started - working on {} contingencies", stateId,
                                contingenciesForSimulator.size());
                        logger.info("{}: stabilization started", stateId);
                        StabilizationResult stabilizationResult = stabilization.run();
                        logger.info("{}: stabilization terminated", stateId);
                        if (stabilizationResult.getMetrics() != null) {
                            logger.info("{}: stabilization metrics: {}", stateId, stabilizationResult.getMetrics());
                            if (!stabilizationResult.getMetrics().isEmpty())
                                onlineDb.storeMetrics(context.getWorkflowId(), stateId, OnlineStep.STABILIZATION,
                                        stabilizationResult.getMetrics());
                        }
                        if (stabilizationResult.getStatus() == StabilizationStatus.COMPLETED) {
                            ImpactAnalysisResult impactAnalysisResult = impactAnalysis.run(
                                    stabilizationResult.getState(),
                                    OnlineUtils.getContingencyIds(contingenciesForSimulator));
                            logger.info("{}: impact analysis terminated", stateId);
                            if (impactAnalysisResult.getMetrics() != null) {
                                logger.info("{}: impact analysis metrics: {}", stateId,
                                        impactAnalysisResult.getMetrics());
                                if (!impactAnalysisResult.getMetrics().isEmpty())
                                    onlineDb.storeMetrics(context.getWorkflowId(), stateId,
                                            OnlineStep.IMPACT_ANALYSIS, impactAnalysisResult.getMetrics());
                            }
                            putResultsIntoContext(stateId, impactAnalysisResult, context.getResults());
                            stateListener.onImpactAnalysisResults(stateId, context);
                            logger.info("{}: time-domain simulation terminated", stateId);
                            status.put(OnlineTaskType.TIME_DOMAIN_SIM, OnlineTaskStatus.SUCCESS);
                            stateListener.onUpdate(stateId, status, context.timeHorizon);
                        } else {
                            logger.info("{}: time-domain simulation failed (stabilization)", stateId);
                            status.put(OnlineTaskType.TIME_DOMAIN_SIM, OnlineTaskStatus.FAILED);
                            stateListener.onUpdate(stateId, status, context.timeHorizon,
                                    "time-domain simulation failed (stabilization): metrics = "
                                            + stabilizationResult.getMetrics());
                        }
                    }
                }
                stateListener.onUpdate(stateId, status, context.timeHorizon);
            } else {
                logger.error("{}: stop analisys of state: loadflow does not converge: metrics = {}", stateIdStr,
                        result.getMetrics());
                stateListener.onUpdate(stateId, status, context.timeHorizon,
                        "LoadFLow does not converge: metrics = " + result.getMetrics());
            }
        } catch (Throwable t) {
            status.put(currentStatus, OnlineTaskStatus.FAILED);
            //TODO  manage string ifo detail 
            stateListener.onUpdate(stateId, status, context.timeHorizon, currentStatus + " failed ... ");
            logger.error("{}: Error working on state: {}", stateId, t.toString(), t);
        }
        return null;
    }

    private void runOptimizer(Network network, List<Contingency> contingencies,
            List<Contingency> contingenciesForSimulator, ForecastAnalysisResults results) {
        String stateId = network.getStateManager().getWorkingStateId();
        logger.info("{}: running optimizer", stateId);
        List<Callable<Void>> postContingencyStateComputations = new ArrayList<>(contingencies.size());
        for (Contingency contingency : contingencies) {
            postContingencyStateComputations.add(new Callable<Void>() {

                @Override
                public Void call() throws Exception {
                    String postContingencyStateId = stateId + "-post-" + contingency.getId();
                    boolean loadflowConverge = computePostContingencyState(network, stateId, contingency,
                            postContingencyStateId);
                    if (loadflowConverge) {
                        logger.info("{}: adding state {} to post contingency states for optimizer", stateId,
                                postContingencyStateId);
                        PostContingencyState postContingencyState = new PostContingencyState(network,
                                postContingencyStateId, contingency);
                        logger.info("{}: running optimizer on post contingency state {} of contingency {}", stateId,
                                postContingencyStateId, contingency.getId());
                        CorrectiveControlOptimizerResult optimizerResult = null;
                        try {
                            optimizerResult = optimizer.run(postContingencyState);
                        } catch (Throwable t) {
                            logger.error("{}: Error running optimizer on contingency {}: {}", stateId,
                                    contingency.getId(), t.getMessage(), t);
                            optimizerResult = new CorrectiveControlOptimizerResult(contingency.getId(), false);
                            optimizerResult.setFinalStatus(CCOFinalStatus.OPTIMIZER_EXECUTION_ERROR);
                            optimizerResult.setCause(t.getMessage());
                        }
                        logger.info(
                                "{}: optimizer results for contingency {}: action found = {}, status = {}, cause = {}",
                                stateId, contingency.getId(), optimizerResult.areActionsFound(),
                                optimizerResult.getFinalStatus(), optimizerResult.getCause());
                        Map<String, Map<String, ActionParameters>> actions = null;
                        if (optimizerResult.areActionsFound()) {
                            logger.info("{}: optimizer results: action plan {}, actions {} for contingency {}",
                                    stateId, optimizerResult.getActionPlan(), optimizerResult.getActionsIds(),
                                    contingency.getId());
                            actions = new HashMap<String, Map<String, ActionParameters>>();
                            for (String actionId : optimizerResult.getActionsIds())
                                actions.put(actionId, optimizerResult.getEquipmentsWithParameters(actionId));
                        } else {
                            logger.error("{}: Error: optimizer didn't find actions for post contingency state {}",
                                    stateId, postContingencyStateId);
                            if (!parameters.validation()) { // if validation -> all the [contingency,state] pairs have already been added to the list for simulation -> no need to do it here
                                // add to contingencies for simulator
                                synchronized (contingenciesForSimulator) {
                                    contingenciesForSimulator.add(contingency);
                                }
                            }
                        }
                        synchronized (results) {
                            results.addStateWithActions(contingency.getId(), Integer.valueOf(stateId),
                                    optimizerResult.areActionsFound(), optimizerResult.getFinalStatus(),
                                    optimizerResult.getCause(), optimizerResult.getActionPlan(), actions);
                        }
                    } else {
                        logger.info(
                                "{}: loadflow does not converge on post contigency state {}, the contingency {} will be analyzed by T-D simulation",
                                stateId, postContingencyStateId, contingency.getId());
                        if (!parameters.validation()) { // if validation -> all the [contingency,state] pairs have already been added to the list for simulation -> no need to do it here
                            // add to contingencies for simulator
                            synchronized (contingenciesForSimulator) {
                                contingenciesForSimulator.add(contingency);
                            }
                        }
                    }

                    return null;
                }

            });
        }
        ExecutorService taskExecutor = Executors.newFixedThreadPool(contingencies.size());
        try {
            taskExecutor.invokeAll(postContingencyStateComputations);
        } catch (InterruptedException e) {
            logger.error("{}: Error running optimizer: {}", stateId, e.getMessage());
        }
        taskExecutor.shutdown();
        network.getStateManager().setWorkingState(stateId);
    }

    private void computeAndStorePostContingencyViolations(Network network, List<Contingency> contingencies) {
        String stateId = network.getStateManager().getWorkingStateId();
        logger.info("{}: computing post contingency violations", stateId);
        List<Callable<Void>> postContingencyViolationsComputations = new ArrayList<>(contingencies.size());
        for (Contingency contingency : contingencies) {
            postContingencyViolationsComputations.add(new Callable<Void>() {

                @Override
                public Void call() throws Exception {
                    List<LimitViolation> violations = new ArrayList<LimitViolation>();
                    // compute post contingency state
                    String postContingencyStateId = stateId + "-post-" + contingency.getId();
                    boolean loadflowConverge = computePostContingencyState(network, stateId, contingency,
                            postContingencyStateId);
                    if (loadflowConverge) {
                        logger.info("{}: computing post contingency violations for contingency {}", stateId,
                                contingency.getId());
                        violations = Security.checkLimits(network, CurrentLimitType.PATL,
                                parameters.getLimitReduction());
                        if (violations == null || violations.isEmpty()) {
                            logger.info("{}: no post contingency violations for contingency {}", stateId,
                                    contingency.getId());
                            violations = new ArrayList<LimitViolation>();
                        }
                    } else {
                        logger.info(
                                "{}: post contingency loadflow does not converge for contingency {}, skipping computing post contingency violations",
                                stateId, contingency.getId());
                    }
                    logger.info(
                            "{}: storing post contingency violations/loadflow results for contingency {} in online db",
                            stateId, contingency.getId());
                    onlineDb.storePostContingencyViolations(context.getWorkflowId(), Integer.valueOf(stateId),
                            contingency.getId(), loadflowConverge, violations);
                    network.getStateManager().setWorkingState(stateId);
                    //                     network.getStateManager().removeState(postContingencyStateId);
                    return null;
                }
            });
        }
        ExecutorService taskExecutor = Executors.newFixedThreadPool(contingencies.size());
        try {
            taskExecutor.invokeAll(postContingencyViolationsComputations);
        } catch (InterruptedException e) {
            logger.error("{}: Error computing post contingency vioations: {}", stateId, e.getMessage());
        }
        taskExecutor.shutdown();
    }

    private boolean computePostContingencyState(Network network, String stateId, Contingency contingency,
            String postContingencyStateId) {
        boolean loadflowConverge = false;
        logger.info("{}: computing post contingency state for contingency {}", stateId, contingency.getId());
        //String postContingencyStateId = stateId + "-post-" + contingency.getId();
        boolean alreadyProcessed = false;
        synchronized (loadflowResults) {
            if (loadflowResults.containsKey(postContingencyStateId)) {
                alreadyProcessed = true;
                loadflowConverge = loadflowResults.get(postContingencyStateId);
            }
        }
        if (alreadyProcessed && network.getStateManager().getStateIds().contains(postContingencyStateId)) {
            // post contingency state already computed, avoid to run the load flow again
            logger.info("{}: post contingency state {} already computed", stateId, postContingencyStateId);
            network.getStateManager().setWorkingState(postContingencyStateId);
        } else {
            // create post contingency state
            logger.info("{}: creating post contingency state {}", stateId, postContingencyStateId);
            network.getStateManager().cloneState(stateId, postContingencyStateId);
            network.getStateManager().setWorkingState(postContingencyStateId);
            // apply contingency to post contingency state
            logger.info("{}: applying contingency {} to post contingency state {}", stateId, contingency.getId(),
                    postContingencyStateId);
            contingency.toTask().modify(network);
            try {
                // run load flow on post contingency state
                logger.info("{}: running load flow on post contingency state {}", stateId, postContingencyStateId);
                LoadFlowResult result = loadFlow.run();
                if (result.isOk()) {
                    logger.info("{}: load flow on post contingency state {} converge", stateId,
                            postContingencyStateId);
                    loadflowConverge = true;
                } else {
                    logger.info("{}: load flow on post contingency state {} does not converge", stateId,
                            postContingencyStateId);
                    loadflowConverge = false;
                }
                synchronized (loadflowResults) {
                    loadflowResults.put(postContingencyStateId, loadflowConverge);
                }
            } catch (Exception e) {
                logger.info("{}: error running load flow on post contingency state {}: {}", stateId,
                        postContingencyStateId, e.getMessage());
                loadflowConverge = false;
            }
        }
        //network.getStateManager().setWorkingState(stateId);
        return loadflowConverge;
    }

    private void putResultsIntoContext(Integer stateId, ImpactAnalysisResult simulationResult,
            ForecastAnalysisResults results) {
        Objects.requireNonNull(stateId, "state id is null");
        Objects.requireNonNull(simulationResult, "simulation result is null");
        Objects.requireNonNull(results, "forecast analysis result is null");
        List<SecurityIndex> securityIndexesList = new ArrayList<SecurityIndex>();
        if (parameters.getSecurityIndexes() == null)
            securityIndexesList = simulationResult.getSecurityIndexes();
        else {
            securityIndexesList = simulationResult.getSecurityIndexes().stream()
                    .filter(x -> parameters.getSecurityIndexes().contains(x.getId().getSecurityIndexType()))
                    .collect(Collectors.toList());
            if (securityIndexesList.isEmpty()) {
                logger.info("Empty filter security indexes -> using all the indexes");
                securityIndexesList = simulationResult.getSecurityIndexes();
            }
        }
        //Multimap<String, SecurityIndex> securityIndexes = Multimaps.index(simulationResult.getSecurityIndexes(), new Function<SecurityIndex, String>() {
        Multimap<String, SecurityIndex> securityIndexes = Multimaps.index(securityIndexesList,
                new Function<SecurityIndex, String>() {
                    @Override
                    public String apply(SecurityIndex index) {
                        return index.getId().getContingencyId();
                    }
                });
        synchronized (results) {
            for (Map.Entry<String, Collection<SecurityIndex>> entry : securityIndexes.asMap().entrySet()) {
                boolean isSafe = OnlineUtils.isSafe(entry.getValue());
                if (!isSafe) {
                    logger.info("{}: unsafe for contingency {} afer time domain simulation", stateId,
                            entry.getKey());
                    results.addUnsafeStateWithIndexes(entry.getKey(), stateId, new ArrayList<>(entry.getValue()));
                } else {
                    logger.info("{}: safe for contingency {} afer time domain simulation", stateId, entry.getKey());
                    if (parameters.validation()) // if validation add anyway to results
                        results.addUnsafeStateWithIndexes(entry.getKey(), stateId,
                                new ArrayList<>(entry.getValue()));
                }
            }
        }
    }

}