Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tracking issue for known performance challenges #800

Open
2 of 5 tasks
jd-lara opened this issue Dec 17, 2021 · 5 comments
Open
2 of 5 tasks

Tracking issue for known performance challenges #800

jd-lara opened this issue Dec 17, 2021 · 5 comments
Assignees

Comments

@jd-lara
Copy link
Member

jd-lara commented Dec 17, 2021

  • The use of ParameterRef types is problematic when using StandardPTDF models in simulations: See Performance disparity in expression operations JuliaStochOpt/ParameterJuMP.jl#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.
  • _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
@odow
Copy link
Contributor

odow commented Dec 6, 2022

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).

@jd-lara
Copy link
Member Author

jd-lara commented Dec 6, 2022

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.

@odow
Copy link
Contributor

odow commented Dec 6, 2022

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

@jd-lara jd-lara self-assigned this Dec 21, 2022
@jd-lara
Copy link
Member Author

jd-lara commented Dec 21, 2022

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

@jd-lara
Copy link
Member Author

jd-lara commented Mar 18, 2024

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants