Skip to content

Redistribute operation models#104

Merged
jd-lara merged 5 commits into
mainfrom
ac/move_op
May 28, 2026
Merged

Redistribute operation models#104
jd-lara merged 5 commits into
mainfrom
ac/move_op

Conversation

@acostarelli
Copy link
Copy Markdown
Member

No description provided.

Anthony Costarelli and others added 5 commits May 20, 2026 11:28
IOM is meant to be a domain-neutral optimization-modeling library. This
removes the power-flavoured problem-type chain (DecisionProblem,
EmulationProblem, DefaultDecisionProblem, DefaultEmulationProblem,
GenericOpProblem, GenericEmulationProblem) and the five Simulation*
placeholder types that were stubs for PowerSimulations.jl.

In their place IOM gains a single abstract type:

  abstract type AbstractOptimizationProblem end

DecisionModel and EmulationModel are re-parameterized over it, retaining
their phantom {M} parameter so POM-side dispatch on concrete problem tags
keeps working. IOM-side methods (get_problem_type, get_current_time,
init_model_store_params!, OptimizationProblemOutputs constructors,
error-stub validate_template) are re-rooted on the new abstract.

validate_time_series! becomes an extension-point stub (function declaration
with no methods); POM provides concrete methods on its own problem-type
chain. Outer constructors that call finalize_template! and the
EmulationModel update_parameters!/update_model!/update_parameter_values!
chain move to POM, where they belong.

Base.show methods for the removed Simulation* placeholders are deleted
from print_pt_v3.jl; the ProblemOutputsTypes union is collapsed to
OptimizationProblemOutputs.

Verified: 1144/1144 unit tests pass, plus 10/10 Aqua quality checks.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The EmulationModel struct previously carried an explicit inner constructor
that called finalize_template! — a POM-side function. That left
power-flavoured construction logic in IOM, inconsistent with how
DecisionModel handled it (no inner constructor; all construction logic in
outer methods that moved to POM in the prior commit).

Drop the inner constructor, leaving the auto-generated default
EmulationModel{M}(name, template, sys, internal, simulation_info, store,
ext) constructor. POM's Settings-taking outer constructor will do the
finalize_template!/OptimizationContainer/validate_time_series! work and
call the auto-default to construct the model.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
OperationModel was IOM's abstract supertype for DecisionModel and
EmulationModel. The "Operation" prefix carried unnecessary power-domain
flavor for what is a fully generic wrapper-level abstraction in IOM —
it describes "any optimization-model wrapper" with no domain commitment.

Rename it to AbstractOptimizationModel:
  - Follows the Julia Abstract* convention (consistent with the
    AbstractOptimizationProblem introduced in the prior commit).
  - Removes the residual "Operation" naming from IOM.
  - Reads correctly: DecisionModel/EmulationModel are concrete
    optimization models, and their supertype names that role generically.

All 14 affected IOM files updated (struct supertypes, method dispatches
on the abstract type, module export). 1144/1144 unit tests pass plus
10/10 Aqua checks.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The Settings-taking, kwargs, and type-first constructors for DecisionModel
and EmulationModel are not power-specific — their bodies use only IOM
types and IOM-declared extension points (finalize_template! and
validate_time_series!). The only thing tying them to POM in the prior
commit was the `where {M <: AbstractPowerDecisionProblem}` constraint.

Move all six generic constructors (3 for DecisionModel, 3 for
EmulationModel) back to IOM, parameterized on
`<: AbstractOptimizationProblem`. The construction path is now:

  1. User calls DecisionModel{MyProblem}(template, sys; horizon, ...)
  2. IOM kwargs constructor builds Settings, delegates to Settings-taking
  3. IOM Settings-taking constructor finalizes the template, builds the
     OptimizationContainer, calls validate_time_series!
  4. finalize_template! and validate_time_series! dispatch to whichever
     domain library (POM, hypothetical GasOperationsModels, etc.) has
     supplied methods for the concrete template/model types.

POM keeps only:
  - The default-tag convenience constructor that picks
    GenericPowerDecisionProblem/GenericPowerEmulationProblem
  - The bare-system error stub for DefaultPowerDecisionProblem
  - All the build/solve/run/reset dispatches on AbstractPower* types
  - validate_template and validate_time_series! method implementations

Net effect: future domain libraries get the construction path for free
by defining their template type, implementing finalize_template! and
validate_time_series! for it, and optionally a default-tag convenience.

IOM unit tests: 1144/1144 pass, plus 10/10 Aqua checks.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@acostarelli acostarelli requested a review from jd-lara May 20, 2026 17:50
solver/model settings, builds a `Settings`, and delegates to the
settings-taking constructor.
"""
function DecisionModel{M}(
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jd-lara What do you think about keeping the constructors here?

@github-actions
Copy link
Copy Markdown
Contributor

Performance Results
Main

This branch

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refactors the operation-model type hierarchy to better separate domain-specific optimization problems from the core InfrastructureOptimizationModels (IOM) infrastructure, and updates related interfaces/printing/output writing to dispatch on the new abstractions.

Changes:

  • Replace OperationModel/DecisionProblem/EmulationProblem usage with AbstractOptimizationModel/AbstractOptimizationProblem across the operation layer.
  • Remove simulation-related placeholder types/printing from IOM and narrow printing support to optimization outputs/models.
  • Adjust stores/output-writing and debugging/numerical-analysis utilities to dispatch on the new abstract model type.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
test/test_utils/run_simulation.jl Removes a test utility that built/executed a 2-step simulation.
src/utils/print_pt_v3.jl Updates show dispatch to new model abstractions; removes simulation-related printing.
src/operation/store_common.jl Updates output-writing dispatch from OperationModel to AbstractOptimizationModel.
src/operation/optimization_model_interface.jl Renames the shared model interface file and updates dispatch signatures accordingly.
src/operation/optimization_debugging.jl Updates debugging helpers to accept AbstractOptimizationModel.
src/operation/model_numerical_analysis_utils.jl Updates numerical-bounds utilities to accept AbstractOptimizationModel.
src/operation/initial_conditions_update_in_memory_store.jl Updates IC-update dispatch to accept AbstractOptimizationModel.
src/operation/emulation_model.jl Refactors EmulationModel to parameterize on AbstractOptimizationProblem and adjusts constructors/docs.
src/operation/decision_model.jl Refactors DecisionModel to parameterize on AbstractOptimizationProblem and adjusts constructors/docs.
src/InfrastructureOptimizationModels.jl Updates exports/includes to reflect the new optimization-model interface naming and types.
src/core/optimization_problem_outputs.jl Updates docs to reference the renamed model abstraction; minor doc cleanup.
src/core/operation_model_abstract_types.jl Replaces legacy abstract types with AbstractOptimizationProblem / AbstractOptimizationModel.
.claude/claude.md Updates internal file listing to reflect the new interface filename.
Comments suppressed due to low confidence (1)

src/operation/optimization_model_interface.jl:193

  • get_simulation_info(model, val) = model.simulation_info = val is a setter but is named like a getter (and lacks a !). This is confusing and easy to misuse, and it also overloads get_simulation_info with different arity for unrelated semantics. Rename to set_simulation_info!(model, val) (and optionally keep a deprecated alias if needed).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 71 to 74
Dict{String, Any}(),
)
validate_time_series!(model)
return model
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's make it a no-op and maybe write a @warn

Comment on lines 142 to 156
"""
Build the optimization problem of type M with the specific system and template

# Arguments
DecisionModel(::Type{M}, template, sys, jump_model=nothing; kwargs...)
where {M <: AbstractOptimizationProblem}

- `::Type{M} where M<:DecisionProblem`: The abstract operation model type
- `template::AbstractProblemTemplate`: The model reference made up of transmission, devices, branches, and services.
- `sys::IS.InfrastructureSystemsContainer`: the system created using Power Systems
- `jump_model::Union{Nothing, JuMP.Model}` = nothing: Enables passing a custom JuMP model. Use with care.

# Example

```julia
template = ProblemTemplate(CopperPlatePowerModel, devices, branches, services)
problem = DecisionModel(MyOpProblemType, template, system, optimizer)
```
Type-first dispatch variant.
"""
function DecisionModel(
::Type{M},
template::AbstractProblemTemplate,
sys::IS.InfrastructureSystemsContainer,
jump_model::Union{Nothing, JuMP.Model} = nothing;
kwargs...,
) where {M <: DecisionProblem}
) where {M <: AbstractOptimizationProblem}
return DecisionModel{M}(template, sys, jump_model; kwargs...)
end
EmulationModelStore(),
Dict{String, Any}(),
)
validate_time_series!(model)
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, no-op.

Comment on lines 112 to 126
"""
Build the optimization problem of type M with the specific system and template

# Arguments
EmulationModel(::Type{M}, template, sys, jump_model=nothing; kwargs...)
where {M <: AbstractOptimizationProblem}

- `::Type{M} where M<:EmulationProblem`: The abstract Emulation model type
- `template::AbstractProblemTemplate`: The model reference made up of transmission, devices,
branches, and services.
- `sys::IS.InfrastructureSystemsContainer`: the system created using Power Systems
- `jump_model::Union{Nothing, JuMP.Model}`: Enables passing a custom JuMP model. Use with care

# Example

```julia
template = ProblemTemplate(CopperPlatePowerModel, devices, branches, services)
problem = EmulationModel(MyEmProblemType, template, system, optimizer)
```
Type-first dispatch variant.
"""
function EmulationModel(
::Type{M},
template::AbstractProblemTemplate,
sys::IS.InfrastructureSystemsContainer,
jump_model::Union{Nothing, JuMP.Model} = nothing;
kwargs...,
) where {M <: EmulationProblem}
return EmulationModel{M}(template, sys, jump_model; kwargs...)
end

function EmulationModel(
template::AbstractProblemTemplate,
sys::IS.InfrastructureSystemsContainer,
jump_model::Union{Nothing, JuMP.Model} = nothing;
kwargs...,
)
return EmulationModel{GenericEmulationProblem}(template, sys, jump_model; kwargs...)
end

"""
Builds an empty emulation model. This constructor is used for the implementation of custom
emulation models that do not require a template.

# Arguments

- `::Type{M} where M<:EmulationProblem`: The abstract operation model type
- `sys::IS.InfrastructureSystemsContainer`: the system created using Power Systems
- `jump_model::Union{Nothing, JuMP.Model}` = nothing: Enables passing a custom JuMP model. Use with care.

# Example

```julia
problem = EmulationModel(system, optimizer)
```
"""
function EmulationModel{M}(
sys::IS.InfrastructureSystemsContainer,
jump_model::Union{Nothing, JuMP.Model} = nothing;
kwargs...,
) where {M <: EmulationProblem}
) where {M <: AbstractOptimizationProblem}
return EmulationModel{M}(template, sys, jump_model; kwargs...)
end
Comment on lines 358 to 361
It is recommended that `directory` be the directory that contains a serialized
OperationModel. That will allow automatic deserialization of the PowerSystems.System.
AbstractOptimizationModel. That will allow automatic deserialization of the PowerSystems.System.
The `OptimizationProblemOutputs` instance can be deserialized with `OptimizationProblemOutputs(directory)`.
"""
Comment on lines +1053 to 1055
- `res::OptimizationProblemOutputs`: Outputs
- `directory::AbstractString` : target directory
- `format = "CSV"` : can be "csv" or "json
Comment on lines 1 to +19
"""
Abstract type for Decision Model and Emulation Model. OperationModel structs are parameterized with DecisionProblem or Emulation Problem structs
"""
abstract type OperationModel end

#TODO: Document the required interfaces for custom types
"""
Abstract type for Decision Problems
Abstract type for any optimization problem. Concrete subtypes are provided by
downstream domain libraries (e.g. PowerOperationsModels). `DecisionModel{M}` and
`EmulationModel{M}` are parameterized over this type so any optimization problem
can be wrapped without IOM knowing its domain.

# Example

import InfrastructureOptimizationModels
const POM = InfrastructureOptimizationModels
struct MyCustomProblem <: POM.DecisionProblem
const IOM = InfrastructureOptimizationModels
struct MyCustomProblem <: IOM.AbstractOptimizationProblem end
"""
abstract type DecisionProblem end
abstract type AbstractOptimizationProblem end

"""
Abstract type for Emulation Problems

# Example

import InfrastructureOptimizationModels
const POM = InfrastructureOptimizationModels
struct MyCustomEmulator <: POM.EmulationProblem
Abstract supertype for `DecisionModel` and `EmulationModel`. Concrete subtypes
are parameterized with an `AbstractOptimizationProblem` subtype.
"""
abstract type EmulationProblem end

#################################################################################
# Simulation Models Container
# Holds references to models in a simulation
# Used for display/printing purposes
struct SimulationModels
decision_models::Vector{<:OperationModel}
emulation_model::Union{Nothing, OperationModel}
end

function SimulationModels(;
decision_models,
emulation_model::Union{Nothing, OperationModel} = nothing,
)
return SimulationModels(decision_models, emulation_model)
end

#################################################################################
# Simulation Sequence
# Holds the execution sequence information for a simulation
# This is a placeholder struct - concrete implementation in PowerSimulations
struct SimulationSequence
executions_by_model::Dict
horizons::Dict
intervals::Dict
SimulationSequence() = new(Dict(), Dict(), Dict())
end

# Placeholder accessor function for simulation sequence
get_step_resolution(::SimulationSequence) = Dates.Hour(1)

#################################################################################
# Simulation Type
# Abstract type for simulation objects
# Concrete implementation should be in PowerSimulations
abstract type Simulation end

#################################################################################
# Simulation Outputs Type
# Abstract type for simulation outputs
# Concrete implementation should be in PowerSimulations
abstract type SimulationOutputs end

#################################################################################
# Simulation Problem Outputs Type
# Abstract type for individual problem outputs within a simulation
# Concrete implementation should be in PowerSimulations
abstract type SimulationProblemOutputs end
abstract type AbstractOptimizationModel end
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. OperationModels are a POM thing.

Comment on lines 380 to 508
@@ -500,10 +504,7 @@ export RunStatus
export SimulationBuildStatus

# Problem Types
export DecisionProblem
export EmulationProblem
export DefaultDecisionProblem
export DefaultEmulationProblem
export AbstractOptimizationProblem

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See above.

@jd-lara jd-lara merged commit 1e30905 into main May 28, 2026
6 of 7 checks passed
@jd-lara jd-lara deleted the ac/move_op branch May 28, 2026 20:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants