Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 31 additions & 7 deletions src/InfrastructureOptimizationModels.jl
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,7 @@ import InfrastructureSystems.Optimization:
AbstractStorageFormulation,
AbstractLoadFormulation,
AbstractHVDCNetworkModel,
AbstractPowerModel,
AbstractPowerFlowEvaluationModel,
AbstractPowerFlowEvaluationData
AbstractPowerModel

import InfrastructureSystems:
@scoped_enum,
Expand Down Expand Up @@ -170,7 +168,7 @@ export InitialCondition
export NetworkModel
export get_PTDF_matrix, get_LODF_matrix, get_reduce_radial_branches
export get_duals, get_reference_buses, get_subnetworks, get_bus_area_map
export get_power_flow_evaluation, has_subnetworks, get_subsystem
export get_evaluations, has_subnetworks, get_subsystem
export set_subsystem!, add_dual!
export requires_all_branch_models, supports_branch_filtering, ignores_branch_filtering
export validate_network_model
Expand Down Expand Up @@ -223,6 +221,7 @@ export add_constant_to_jump_expression!
export add_proportional_to_jump_expression!
export add_linear_to_jump_expression!
# Cost term helpers (generic objective function building blocks)
export add_cost_term_to_expression!
export add_cost_term_invariant!
export add_cost_term_variant!
export add_pwl_variables_delta!
Expand Down Expand Up @@ -376,7 +375,7 @@ export get_incompatible_devices

# Bulk export: symbols POM needs that weren't previously exported
# Core types
export OptimizationContainer, OperationModel, AbstractPowerFlowEvaluationModel
export OptimizationContainer, OperationModel
export ArgumentConstructStage, ModelConstructStage
export EmulationModelStore, DeviceModelForBranches
export SOSStatusVariable
Expand Down Expand Up @@ -443,6 +442,13 @@ export EmergencyUp
export EmergencyDown
export RawACE
export ProductionCostExpression
export ConstituentCostExpression
export FuelCostExpression
export StartUpCostExpression
export ShutDownCostExpression
export FixedCostExpression
export VOMCostExpression
export CurtailmentCostExpression
export FuelConsumptionExpression
export ActivePowerRangeExpressionLB
export ActivePowerRangeExpressionUB
Expand All @@ -468,8 +474,25 @@ export SparseVariableType, InterpolationVariableType, BinaryInterpolationVariabl
export UpperBound, LowerBound, BoundDirection, get_bound_direction
export EventParameter

# Abstract types for extensions (from InfrastructureSystems.Optimization)
export AbstractPowerFlowEvaluationData
# External evaluation abstraction (replaces the PowerFlows-specific shims)
export AbstractEvaluator
export AbstractEvaluationData
export EvaluationContainer
export initialize_evaluation_data
export evaluate!
export reset!
export is_solved
export get_evaluation_data
export get_inner_data
export get_evaluators
export get_evaluator
export add_evaluator!
export add_evaluation_data!
export reset_evaluations!
export is_from_evaluator
export lookup_value
export get_entry_type
export get_component_names

# Status Enums (from InfrastructureSystems)
export ModelBuildStatus
Expand Down Expand Up @@ -549,6 +572,7 @@ include("core/operation_model_abstract_types.jl")
include("core/network_reductions.jl")
include("core/service_model.jl")
include("core/device_model.jl")
include("core/external_evaluation.jl")
include("core/network_model.jl")
include("core/initial_conditions.jl")
include("core/settings.jl")
Expand Down
13 changes: 4 additions & 9 deletions src/common_models/interfaces.jl
Original file line number Diff line number Diff line change
Expand Up @@ -203,23 +203,18 @@ function start_up_cost(
)
end

"""
Extension point: Solve the power flow model.
Default: error. Concrete implementations require PowerFlows integration.
"""
function solve_powerflow! end

"""
Extension point: Calculate auxiliary variable values.
Concrete implementations in PowerOperationsModels for specific aux variable types.
"""
function calculate_aux_variable_value! end

"""
Extension point: Check if an auxiliary variable type comes from power flow evaluation.
Default: false. Override in POM for PowerFlowAuxVariableType subtypes.
Extension point: Check if an auxiliary variable type is produced by an external
evaluator (see `AbstractEvaluator`). Default: false. Override in POM for
evaluator-bound aux variable subtypes.
"""
is_from_power_flow(::Type{<:AuxVariableType}) = false
is_from_evaluator(::Type{<:AuxVariableType}) = false

"""
Extension point: Get minimum and maximum limits for a given component, constraint type, and device formulation.
Expand Down
141 changes: 141 additions & 0 deletions src/core/external_evaluation.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
"""
Abstract type for evaluator configurations attached to a `NetworkModel`.

An `AbstractEvaluator` is stateless configuration that, when used to build an
`OptimizationContainer`, produces an `AbstractEvaluationData` (its runtime
counterpart) via [`initialize_evaluation_data`](@ref).

Concrete subtypes (e.g. `PowerFlowEvaluationModel`) are defined in downstream
packages such as PowerOperationsModels and its PowerFlows extension.
"""
abstract type AbstractEvaluator end

"""
Abstract type for runtime evaluator state stored inside the
`OptimizationContainer`'s [`EvaluationContainer`](@ref).

Concrete subtypes wrap whatever domain-specific data (power-flow solver state,
exporter handles, etc.) the evaluator needs across iterations of
`calculate_aux_variables!`.
"""
abstract type AbstractEvaluationData end

"""
Holds the evaluators registered on a `NetworkModel` and their associated
runtime data, keyed by concrete evaluator type.

`evaluators` is populated by the user when constructing a `NetworkModel`;
`evaluation_data` is populated during `OptimizationContainer` construction by
calling [`initialize_evaluation_data`](@ref) for each registered evaluator.

The split between the two dictionaries may evolve; for now we keep them as
parallel keyed dictionaries so that `evaluation_data[T]` is the runtime state
produced from `evaluators[T]`.
"""
mutable struct EvaluationContainer
evaluators::Dict{DataType, Any}
evaluation_data::Dict{DataType, AbstractEvaluationData}
end

function EvaluationContainer()
return EvaluationContainer(
Dict{DataType, Any}(),
Dict{DataType, AbstractEvaluationData}(),
)
end

get_evaluators(ec::EvaluationContainer) = ec.evaluators
get_evaluation_data(ec::EvaluationContainer) = ec.evaluation_data

get_evaluator(ec::EvaluationContainer, T::DataType) = ec.evaluators[T]
get_evaluation_data(ec::EvaluationContainer, T::DataType) = ec.evaluation_data[T]

add_evaluator!(ec::EvaluationContainer, T::DataType, ev) =
(ec.evaluators[T] = ev)
add_evaluation_data!(
ec::EvaluationContainer,
T::DataType,
d::AbstractEvaluationData,
) = (ec.evaluation_data[T] = d)

Base.isempty(ec::EvaluationContainer) = isempty(ec.evaluators)
Base.length(ec::EvaluationContainer) = length(ec.evaluators)
Base.haskey(ec::EvaluationContainer, T::DataType) = haskey(ec.evaluators, T)

#=
=========================================================================
AbstractEvaluator interface (config-side)
=========================================================================
=#

"""
Build the runtime state for `ev` and return an `AbstractEvaluationData`.
Called once during `OptimizationContainer` construction. Concrete methods are
registered in downstream packages.
"""
function initialize_evaluation_data(
ev::AbstractEvaluator,
container,
system,
)
error(
"initialize_evaluation_data not implemented for evaluator type $(typeof(ev)). " *
"Define `initialize_evaluation_data(::$(typeof(ev)), container, system) ::AbstractEvaluationData` " *
"in the package that owns the concrete type.",
)
end

#=
=========================================================================
AbstractEvaluationData interface (runtime-side)
=========================================================================
=#

"""
Run the evaluation: read state from `container`/`system`, write aux-variable
inputs back into `container`, and mark `data` as solved. Concrete methods are
registered in downstream packages.
"""
function evaluate!(data::AbstractEvaluationData, container, system)
error(
"evaluate! not implemented for evaluation data type $(typeof(data)). " *
"Define `evaluate!(::$(typeof(data)), container, system)` " *
"in the package that owns the concrete type.",
)
end

"""
Mark `data` as not-yet-solved for the current iteration. Called by
`calculate_aux_variables!` at the top of each pass.
"""
function reset!(data::AbstractEvaluationData)
error(
"reset! not implemented for evaluation data type $(typeof(data)). " *
"Define `reset!(::$(typeof(data)))` " *
"in the package that owns the concrete type.",
)
end

"""
Return `true` if `data` has been solved in the current iteration.
"""
function is_solved(data::AbstractEvaluationData)
error(
"is_solved not implemented for evaluation data type $(typeof(data)). " *
"Define `is_solved(::$(typeof(data))) ::Bool` " *
"in the package that owns the concrete type.",
)
end

"""
Return the underlying domain-specific data wrapped by `data` (e.g., a
`PowerFlowData` for a power-flow evaluator). Used by aux-variable calculators
that need direct read access to the evaluator's output.
"""
function get_inner_data(data::AbstractEvaluationData)
error(
"get_inner_data not implemented for evaluation data type $(typeof(data)). " *
"Define `get_inner_data(::$(typeof(data)))` " *
"in the package that owns the concrete type.",
)
end
25 changes: 10 additions & 15 deletions src/core/network_model.jl
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,6 @@ function _check_pm_formulation(::Type{T}) where {T <: AbstractPowerModel}
end
end

# Note: PowerFlowEvaluationModel support has been moved to PowerOperationsModels
# These stub functions maintain API compatibility
_maybe_flatten_pfem(pfem::Vector) = pfem
_maybe_flatten_pfem(pfem) = [pfem]

"""
Establishes the NetworkModel for a given AC network formulation type.

Expand All @@ -39,8 +34,9 @@ Establishes the NetworkModel for a given AC network formulation type.
subnetworks are inferred from PTDF/VirtualPTDF or discovered from the system.
- `duals::Vector{DataType}` = Vector{DataType}()
Constraint types for which duals should be recorded.
- `power_flow_evaluation::Union{AbstractPowerFlowEvaluationModel, Vector{<:AbstractPowerFlowEvaluationModel}}`
Power-flow evaluation model(s). A single model is flattened to a vector internally.
- `evaluations::EvaluationContainer`
External evaluators (e.g. power-flow) keyed by concrete evaluator type.
Default is an empty container — no evaluator runs.

# Notes
- `modeled_branch_types` and `reduced_branch_tracker` are internal fields managed by the model.
Expand All @@ -51,8 +47,10 @@ Establishes the NetworkModel for a given AC network formulation type.

# Examples (concrete types like PTDFPowerModel, CopperPlatePowerModel are defined in PowerSimulations)
# ptdf = PNM.VirtualPTDF(system)
# ec = EvaluationContainer()
# add_evaluator!(ec, PFS.PowerFlowEvaluationModel, PFS.PowerFlowEvaluationModel())
# nw = NetworkModel(PTDFPowerModel; PTDF_matrix = ptdf, reduce_radial_branches = true,
# power_flow_evaluation = PFS.PowerFlowEvaluationModel())
# evaluations = ec)
#
# nw2 = NetworkModel(CopperPlatePowerModel; subnetworks = Dict(1 => Set([1,2,3])))
"""
Expand All @@ -66,7 +64,7 @@ mutable struct NetworkModel{T <: AbstractPowerModel}
network_reduction::PNM.NetworkReductionData
reduce_radial_branches::Bool
reduce_degree_two_branches::Bool
power_flow_evaluation::Vector{<:AbstractPowerFlowEvaluationModel}
evaluations::EvaluationContainer
subsystem::Union{Nothing, String}
hvdc_network_model::Union{Nothing, AbstractHVDCNetworkModel}
modeled_branch_types::Vector{DataType}
Expand All @@ -81,10 +79,7 @@ mutable struct NetworkModel{T <: AbstractPowerModel}
reduce_degree_two_branches = false,
subnetworks = Dict{Int, Set{Int}}(),
duals = Vector{DataType}(),
power_flow_evaluation::Union{
AbstractPowerFlowEvaluationModel,
Vector{<:AbstractPowerFlowEvaluationModel},
} = AbstractPowerFlowEvaluationModel[],
evaluations = EvaluationContainer(),
hvdc_network_model = nothing,
) where {T <: AbstractPowerModel}
_check_pm_formulation(T)
Expand All @@ -98,7 +93,7 @@ mutable struct NetworkModel{T <: AbstractPowerModel}
PNM.NetworkReductionData(),
reduce_radial_branches,
reduce_degree_two_branches,
_maybe_flatten_pfem(power_flow_evaluation),
evaluations,
nothing,
hvdc_network_model,
Vector{DataType}(),
Expand All @@ -119,7 +114,7 @@ get_reference_buses(m::NetworkModel{T}) where {T <: AbstractPowerModel} =
collect(keys(m.subnetworks))
get_subnetworks(m::NetworkModel) = m.subnetworks
get_bus_area_map(m::NetworkModel) = m.bus_area_map
get_power_flow_evaluation(m::NetworkModel) = m.power_flow_evaluation
get_evaluations(m::NetworkModel) = m.evaluations
has_subnetworks(m::NetworkModel) = !isempty(m.bus_area_map)
get_subsystem(m::NetworkModel) = m.subsystem
get_hvdc_network_model(m::NetworkModel) = m.hvdc_network_model
Expand Down
Loading
Loading