PowerSimulations.jl icon indicating copy to clipboard operation
PowerSimulations.jl copied to clipboard

Tracking issue for known performance challenges

Open jd-lara opened this issue 3 years ago • 5 comments

  • [x] The use of ParameterRef types is problematic when using StandardPTDF models in simulations: See https://github.com/JuliaStochOpt/ParameterJuMP.jl/issues/85 for references. Potential fix: Move to ParametricOptInterface.jl.
  • [ ] For systems with a large number of time-steps calling PM.replicate will be costly and have a lot of data repetition.
  • [x] _update_simulation_state! uses a DataFrames interface to move results between the Store cache and the simulation state. This causes every read from the store to allocate a DataFrame. Potential fix: Use Array to read from the store, difficulty: ensure columns match. (Test show that this is not an issue)
  • [ ] Add containers is currently type unstable as it can return dense or sparse objects. (Unclear if this a big problem)
  • [ ] Parallelize state updating

jd-lara avatar Dec 17 '21 17:12 jd-lara

Part of the performance problem is that a lot of the parameter data is used in the form of 0 <= generation[t=1:T] <= available[t], where available is an input vector.

Rather than adding upper bounds, these are instead added as constraints like:

@constraint(model, [t=1:T], generation[t] <= available[t])

There are also others like

@constraint(model, [t=1:T], generation[t] <= constant * available[t])

and

@constraint(model, [t=1:T], generation[t] <= sum(available[t, i] for i in plants))

Adding linear constraints simplifies the usage with ParameterJuMP, but it comes at the cost of an extra constraint for every variable. We could consider using JuMP.set_upper_bound(variable, bound) where possible. It would reduce the problem size, and solvers can more efficiently restart the solve with a changed bound. (Although they'll have presolves which should catch this and turn them into variable bounds).

odow avatar Dec 06 '22 22:12 odow

Thanks, I think we can handle these cases for some parameters and dispatch the implementation of the parameter according to parameter type and reduce the burden for the some of the most common case which is time series.

There is also the case where there are parameters that are used in a long expression like the injections per node.

jd-lara avatar Dec 06 '22 23:12 jd-lara

Injections per node with long expressions: keep that. But for simple upper bounds, you can probably be cleverer in how you go about it.

odow avatar Dec 06 '22 23:12 odow

#899 will address point 1 by removing the use of ParameterJuMP and POI all together.

jd-lara avatar Dec 21 '22 00:12 jd-lara

IT might possible to speed up the state update in this block of code using Multithreading

function _update_system_state!(sim::Simulation, model::EmulationModel)
    sim_state = get_simulation_state(sim)
    simulation_time = get_current_time(sim)
    system_state = get_system_states(sim_state)
    store = get_simulation_store(sim)
    em_model_name = get_name(model)
    for key in get_container_keys(get_optimization_container(model))
        !should_write_resulting_value(key) && continue
        update_system_state!(system_state, key, store, em_model_name, simulation_time)
    end
    IS.@record :execution StateUpdateEvent(simulation_time, em_model_name, "SystemState")
    return
end

function _update_simulation_state!(sim::Simulation, model::EmulationModel)
    # Order of these operations matters. Do not reverse.
    # This will update the state with the results of the store first and then fill
    # the remaning values with the decision state.
    _update_system_state!(sim, model)
    _update_system_state!(sim, get_name(model))
    return
end

function _update_simulation_state!(sim::Simulation, model::DecisionModel)
    model_name = get_name(model)
    store = get_simulation_store(sim)
    simulation_time = get_current_time(sim)
    state = get_simulation_state(sim)
    model_params = get_decision_model_params(store, model_name)
    for field in fieldnames(DatasetContainer)
        for key in list_decision_model_keys(store, model_name, field)
            !has_dataset(get_decision_states(state), key) && continue
            res = read_result(DenseAxisArray, store, model_name, key, simulation_time)
            update_decision_state!(state, key, res, simulation_time, model_params)
        end
    end
    IS.@record :execution StateUpdateEvent(simulation_time, model_name, "DecisionState")
    return
end

jd-lara avatar Mar 18 '24 04:03 jd-lara