Skip to content
Closed
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
18 changes: 18 additions & 0 deletions scripts/generate_lcov.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/usr/bin/env julia
#
# Generate lcov.info from existing .cov files for Coverage Gutters.
# Run this AFTER running tests with coverage.
#
# Usage:
# 1. Run tests with coverage: julia --project=. -e 'using TestEnv; TestEnv.activate(); include("test/load_tests.jl"); InfrastructureOptimizationModelsTests.run_tests()'
# 2. Generate lcov: julia --project=. -e 'using TestEnv; TestEnv.activate(); include("scripts/generate_lcov.jl")'

using CoverageTools
using Coverage

const PROJECT_ROOT = dirname(@__DIR__)

coverage = CoverageTools.process_folder(joinpath(PROJECT_ROOT, "src"))
LCOV.writefile(joinpath(PROJECT_ROOT, "lcov.info"), coverage)

@info "Wrote $(joinpath(PROJECT_ROOT, "lcov.info"))"
21 changes: 21 additions & 0 deletions scripts/test_with_coverage.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/bin/zsh
#
# Run tests with coverage and generate lcov.info for Coverage Gutters.
# Usage: ./scripts/test_with_coverage.sh

set -euo pipefail
cd "$(dirname "$0")/.."

echo "==> Running tests with coverage..."
julia --project=. --code-coverage -e '
using TestEnv; TestEnv.activate()
include("test/runtests.jl")
'

echo "==> Generating lcov.info..."
julia --project=. -e '
using TestEnv; TestEnv.activate()
include("scripts/generate_lcov.jl")
'

echo "==> Done. lcov.info written to $(pwd)/lcov.info"
2 changes: 1 addition & 1 deletion src/ac_transmission_models/branch_constructor.jl
Original file line number Diff line number Diff line change
Expand Up @@ -583,7 +583,7 @@ function construct_device!(
device_model,
network_model,
)
add_feedforward_arguments!(container, devicemodel, devices)
add_feedforward_arguments!(container, device_model, devices)
return
end

Expand Down
12 changes: 4 additions & 8 deletions src/energy_storage_models/storage_models.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1415,7 +1415,7 @@ function add_cycling_charge_without_reserves!(
powerin_var = get_variable(container, ActivePowerInVariable, V)
slack_var = get_variable(container, StorageChargeCyclingSlackVariable, V)

constraint = add_constraints_container!(container, StorageCyclingCharge, V, names)
constraint = add_constraints_container!(container, StorageCyclingCharge, V, names, time_steps[end:end])
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[JuliaFormatter] reported by reviewdog 🐶

Suggested change
constraint = add_constraints_container!(container, StorageCyclingCharge, V, names, time_steps[end:end])
constraint = add_constraints_container!(
container,
StorageCyclingCharge,
V,
names,
time_steps[end:end],
)


for d in devices
name = PSY.get_name(d)
Expand All @@ -1435,7 +1435,6 @@ function add_cycling_charge_without_reserves!(
return
end

# no test coverage
function add_cycling_charge_with_reserves!(
container::OptimizationContainer,
devices::IS.FlattenIteratorWrapper{V},
Expand All @@ -1461,7 +1460,7 @@ function add_cycling_charge_with_reserves!(
PSY.get_conversion_factor(d)
cycle_count = PSY.get_cycle_limits(d)
efficiency = PSY.get_efficiency(d)
constraint[name, time_steps[end]] = JuMP.@constraint(
constraint[name] = JuMP.@constraint(
get_jump_model(container),
sum((
(powerin_var[name, t] + r_dn_ch[name, t]) *
Expand All @@ -1473,7 +1472,6 @@ function add_cycling_charge_with_reserves!(
return
end

# no test coverage
function add_constraints!(
container::OptimizationContainer,
::Type{StorageCyclingCharge},
Expand Down Expand Up @@ -1514,7 +1512,7 @@ function add_cycling_discharge_without_reserves!(
PSY.get_conversion_factor(d)
cycle_count = PSY.get_cycle_limits(d)
efficiency = PSY.get_efficiency(d)
constraint[name, time_steps[end]] = JuMP.@constraint(
constraint[name] = JuMP.@constraint(
get_jump_model(container),
sum(
(powerout_var[name, t] / efficiency.out) * fraction_of_hour for
Expand All @@ -1525,7 +1523,6 @@ function add_cycling_discharge_without_reserves!(
return
end

# no test coverage
function add_cycling_discharge_with_reserves!(
container::OptimizationContainer,
devices::IS.FlattenIteratorWrapper{V},
Expand All @@ -1551,7 +1548,7 @@ function add_cycling_discharge_with_reserves!(
PSY.get_conversion_factor(d)
cycle_count = PSY.get_cycle_limits(d)
efficiency = PSY.get_efficiency(d)
constraint[name, time_steps[end]] = JuMP.@constraint(
constraint[name] = JuMP.@constraint(
get_jump_model(container),
sum(
((powerout_var[name, t] + r_up_ds[name, t]) / efficiency.out) *
Expand All @@ -1562,7 +1559,6 @@ function add_cycling_discharge_with_reserves!(
return
end

# no test coverage
function add_constraints!(
container::OptimizationContainer,
::Type{StorageCyclingDischarge},
Expand Down
16 changes: 8 additions & 8 deletions src/initial_conditions/update_initial_conditions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@
# IC type -> variable/aux-variable type mapping (dispatch-based)
#################################################################################

_ic_variable_type(::Type{DevicePower}) = ActivePowerVariable()
_ic_variable_type(::Type{DeviceStatus}) = OnVariable()
_ic_variable_type(::Type{DeviceAboveMinPower}) = PowerAboveMinimumVariable()
_ic_variable_type(::Type{InitialTimeDurationOn}) = TimeDurationOn()
_ic_variable_type(::Type{InitialTimeDurationOff}) = TimeDurationOff()
_ic_variable_type(::Type{InitialEnergyLevel}) = EnergyVariable()
_ic_variable_type(::Type{DevicePower}) = ActivePowerVariable
_ic_variable_type(::Type{DeviceStatus}) = OnVariable
_ic_variable_type(::Type{DeviceAboveMinPower}) = PowerAboveMinimumVariable
_ic_variable_type(::Type{InitialTimeDurationOn}) = TimeDurationOn
_ic_variable_type(::Type{InitialTimeDurationOff}) = TimeDurationOff
_ic_variable_type(::Type{InitialEnergyLevel}) = EnergyVariable

# Dispatch to the right container getter based on variable vs aux variable type
# FIXME we should add something like this to the API.
_get_from_container(source, var_type::VariableType, comp_type) =
_get_from_container(source, var_type::Type{<:VariableType}, comp_type) =
get_variable(source, var_type, comp_type)
_get_from_container(source, var_type::AuxVariableType, comp_type) =
_get_from_container(source, var_type::Type{<:AuxVariableType}, comp_type) =
get_aux_variable(source, var_type, comp_type)

#################################################################################
Expand Down
2 changes: 1 addition & 1 deletion src/twoterminal_hvdc_models/branch_constructor.jl
Original file line number Diff line number Diff line change
Expand Up @@ -565,7 +565,7 @@ function construct_device!(
device_model,
network_model,
)
add_feedforward_arguments!(container, devicemodel, devices)
add_feedforward_arguments!(container, device_model, devices)
return
end

Expand Down
2 changes: 2 additions & 0 deletions test/Project.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
[deps]
Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
Coverage = "a2441757-f6aa-5fb2-8edb-039e3f45d037"
CoverageTools = "c36e975a-824b-4404-a568-ef97ca766997"
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
DataFramesMeta = "1313f7d8-7da2-5740-9ea0-a2ca25f37964"
DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8"
Expand Down
105 changes: 105 additions & 0 deletions test/test_core_internals.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
###############################################################################
# Tests for core type definitions, interface fallbacks, and helper methods
###############################################################################

@testset "Network formulation capabilities" begin
# supports_branch_filtering
@test POM.supports_branch_filtering(PTDFPowerModel) == true
@test POM.supports_branch_filtering(POM.SecurityConstrainedPTDFPowerModel) == true

# ignores_branch_filtering
@test POM.ignores_branch_filtering(CopperPlatePowerModel) == true
@test POM.ignores_branch_filtering(AreaBalancePowerModel) == true

# requires_all_branch_models
@test POM.requires_all_branch_models(PTDFPowerModel) == false
@test POM.requires_all_branch_models(CopperPlatePowerModel) == false
@test POM.requires_all_branch_models(AreaBalancePowerModel) == false
end

@testset "Expression type methods" begin
# should_write_resulting_value
@test IOM.should_write_resulting_value(POM.InterfaceTotalFlow) == true
@test IOM.should_write_resulting_value(POM.PTDFBranchFlow) == true
@test IOM.should_write_resulting_value(POM.HydroServedReserveUpExpression) == true
@test IOM.should_write_resulting_value(POM.HydroServedReserveDownExpression) == true
@test IOM.should_write_resulting_value(POM.TotalHydroFlowRateReservoirOutgoing) == true
@test IOM.should_write_resulting_value(POM.TotalHydroFlowRateTurbineOutgoing) == true

# convert_output_to_natural_units
@test IOM.convert_output_to_natural_units(POM.InterfaceTotalFlow) == true
@test IOM.convert_output_to_natural_units(POM.PTDFBranchFlow) == true
end

@testset "Auxiliary variable type methods" begin
# convert_output_to_natural_units
@test IOM.convert_output_to_natural_units(POM.PowerOutput) == true
@test IOM.convert_output_to_natural_units(POM.PowerFlowBranchActivePowerFromTo) == true
@test IOM.convert_output_to_natural_units(POM.PowerFlowBranchActivePowerToFrom) == true
@test IOM.convert_output_to_natural_units(POM.PowerFlowBranchReactivePowerFromTo) ==
true
@test IOM.convert_output_to_natural_units(POM.PowerFlowBranchReactivePowerToFrom) ==
true
@test IOM.convert_output_to_natural_units(POM.PowerFlowBranchActivePowerLoss) == true

# is_from_power_flow
@test IOM.is_from_power_flow(POM.PowerFlowVoltageAngle) == true
@test IOM.is_from_power_flow(POM.PowerFlowVoltageMagnitude) == true
@test IOM.is_from_power_flow(POM.PowerFlowBranchActivePowerFromTo) == true
@test IOM.is_from_power_flow(POM.PowerFlowLossFactors) == true
@test IOM.is_from_power_flow(POM.PowerFlowVoltageStabilityFactors) == true
@test IOM.is_from_power_flow(POM.TimeDurationOn) == false
@test IOM.is_from_power_flow(POM.PowerOutput) == false
end

@testset "Parameter type methods" begin
# convert_output_to_natural_units for parameters
@test IOM.convert_output_to_natural_units(POM.ActivePowerTimeSeriesParameter) == true
@test IOM.convert_output_to_natural_units(POM.ReactivePowerTimeSeriesParameter) == true
@test IOM.convert_output_to_natural_units(POM.RequirementTimeSeriesParameter) == true
@test IOM.convert_output_to_natural_units(POM.DynamicBranchRatingTimeSeriesParameter) ==
true
@test IOM.convert_output_to_natural_units(
POM.PostContingencyDynamicBranchRatingTimeSeriesParameter,
) == true
@test IOM.convert_output_to_natural_units(POM.UpperBoundValueParameter) == true
@test IOM.convert_output_to_natural_units(POM.LowerBoundValueParameter) == true
@test IOM.convert_output_to_natural_units(POM.EnergyLimitParameter) == true
@test IOM.convert_output_to_natural_units(POM.EnergyTargetParameter) == true
@test IOM.convert_output_to_natural_units(POM.ReservoirLimitParameter) == true
@test IOM.convert_output_to_natural_units(POM.ReservoirTargetParameter) == true
@test IOM.convert_output_to_natural_units(POM.EnergyTargetTimeSeriesParameter) == true
@test IOM.convert_output_to_natural_units(POM.EnergyBudgetTimeSeriesParameter) == true
@test IOM.convert_output_to_natural_units(POM.InflowTimeSeriesParameter) == false
@test IOM.convert_output_to_natural_units(POM.OutflowTimeSeriesParameter) == false

# should_write_resulting_value for parameters
@test IOM.should_write_resulting_value(POM.AvailableStatusParameter) == true
@test IOM.should_write_resulting_value(POM.ActivePowerOffsetParameter) == true
@test IOM.should_write_resulting_value(POM.ReactivePowerOffsetParameter) == true
@test IOM.should_write_resulting_value(POM.AvailableStatusChangeCountdownParameter) ==
true
end

@testset "Default interface methods" begin
# get_multiplier_value defaults for OCC parameters
gen = first(
PSY.get_components(PSY.ThermalStandard, PSB.build_system(PSITestSystems, "c_sys5")),
)
@test POM.get_multiplier_value(
IOM.StartupCostParameter,
gen,
POM.ThermalStandardDispatch,
) == 1.0
@test POM.get_multiplier_value(
IOM.ShutdownCostParameter,
gen,
POM.ThermalStandardDispatch,
) == 1.0

# get_default_on_variable / get_default_on_parameter
@test POM.get_default_on_variable(gen) isa POM.OnVariable
@test POM.get_default_on_parameter(gen) isa IOM.OnStatusParameter
end


Comment on lines +104 to +105
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[JuliaFormatter] reported by reviewdog 🐶

Suggested change

82 changes: 82 additions & 0 deletions test/test_device_branch_constructors.jl
Original file line number Diff line number Diff line change
Expand Up @@ -897,3 +897,85 @@ end
IOM.ModelBuildStatus.BUILT
@test solve!(model_ac) == IOM.RunStatus.SUCCESSFULLY_FINALIZED
end

@testset "StaticBranchBounds with CopperPlatePowerModel" begin
system = PSB.build_system(PSITestSystems, "c_sys5")
template = OperationsProblemTemplate(CopperPlatePowerModel)
set_device_model!(template, ThermalStandard, ThermalBasicDispatch)
set_device_model!(template, PowerLoad, StaticPowerLoad)
set_device_model!(template, Line, StaticBranchBounds)
set_device_model!(template, Transformer2W, StaticBranchBounds)
set_device_model!(template, TapTransformer, StaticBranchBounds)
model = DecisionModel(template, system; optimizer = HiGHS_optimizer)
@test build!(model; output_dir = mktempdir(; cleanup = true)) ==
IOM.ModelBuildStatus.BUILT
@test solve!(model) == IOM.RunStatus.SUCCESSFULLY_FINALIZED
end

@testset "StaticBranchUnbounded with PTDFPowerModel" begin
system = PSB.build_system(PSITestSystems, "c_sys5")
template = get_thermal_dispatch_template_network(
NetworkModel(PTDFPowerModel; PTDF_matrix = PTDF(system)),
)
set_device_model!(template, Line, StaticBranchUnbounded)
model = DecisionModel(template, system; optimizer = HiGHS_optimizer)
@test build!(model; output_dir = mktempdir(; cleanup = true)) ==
IOM.ModelBuildStatus.BUILT
@test solve!(model) == IOM.RunStatus.SUCCESSFULLY_FINALIZED
end

@testset "StaticBranch with AreaBalancePowerModel" begin
c_sys = PSB.build_system(PSISystems, "two_area_pjm_DA")
transform_single_time_series!(c_sys, Hour(24), Hour(1))
template = get_thermal_dispatch_template_network(NetworkModel(AreaBalancePowerModel))
set_device_model!(template, AreaInterchange, StaticBranch)
model =
DecisionModel(template, c_sys; resolution = Hour(1), optimizer = HiGHS_optimizer)
@test build!(model; output_dir = mktempdir(; cleanup = true)) ==
IOM.ModelBuildStatus.BUILT
@test solve!(model) == IOM.RunStatus.SUCCESSFULLY_FINALIZED
end

###############################################
##### SLACK VARIABLE TESTS ####################
###############################################

@testset "StaticBranch with slacks on PTDF model" begin
system = PSB.build_system(PSITestSystems, "c_sys5")
template = get_thermal_dispatch_template_network(
NetworkModel(PTDFPowerModel; PTDF_matrix = PTDF(system)),
)
set_device_model!(template, DeviceModel(Line, StaticBranch; use_slacks = true))
model = DecisionModel(template, system; optimizer = HiGHS_optimizer)
@test build!(model; output_dir = mktempdir(; cleanup = true)) ==
IOM.ModelBuildStatus.BUILT

@test !check_variable_bounded(model, FlowActivePowerSlackUpperBound, Line)
@test !check_variable_bounded(model, FlowActivePowerSlackLowerBound, Line)

@test solve!(model) == IOM.RunStatus.SUCCESSFULLY_FINALIZED
end

@testset "StaticBranchBounds with CopperPlate no-op" begin
system = PSB.build_system(PSITestSystems, "c_sys5")
template = OperationsProblemTemplate(CopperPlatePowerModel)
set_device_model!(template, ThermalStandard, ThermalBasicDispatch)
set_device_model!(template, PowerLoad, StaticPowerLoad)
set_device_model!(template, Line, StaticBranchBounds)
model = DecisionModel(template, system; optimizer = HiGHS_optimizer)
@test build!(model; output_dir = mktempdir(; cleanup = true)) ==
IOM.ModelBuildStatus.BUILT
@test solve!(model) == IOM.RunStatus.SUCCESSFULLY_FINALIZED
end

@testset "StaticBranchUnbounded with CopperPlatePowerModel" begin
system = PSB.build_system(PSITestSystems, "c_sys5")
template = OperationsProblemTemplate(CopperPlatePowerModel)
set_device_model!(template, ThermalStandard, ThermalBasicDispatch)
set_device_model!(template, PowerLoad, StaticPowerLoad)
set_device_model!(template, Line, StaticBranchUnbounded)
model = DecisionModel(template, system; optimizer = HiGHS_optimizer)
@test build!(model; output_dir = mktempdir(; cleanup = true)) ==
IOM.ModelBuildStatus.BUILT
@test solve!(model) == IOM.RunStatus.SUCCESSFULLY_FINALIZED
end
23 changes: 23 additions & 0 deletions test/test_device_hvdc.jl
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,26 @@ end
IOM.ModelBuildStatus.BUILT
@test solve!(model) == IOM.RunStatus.SUCCESSFULLY_FINALIZED
end

@testset "HVDC TwoTerminal Dispatch with PTDFPowerModel" begin
sys = PSB.build_system(PSITestSystems, "c_sys14_dc")
template = get_thermal_dispatch_template_network(
NetworkModel(PTDFPowerModel; PTDF_matrix = PTDF(sys)),
)
set_device_model!(template, TwoTerminalGenericHVDCLine, HVDCTwoTerminalDispatch)
model = DecisionModel(template, sys; optimizer = HiGHS_optimizer)
@test build!(model; output_dir = mktempdir(; cleanup = true)) ==
IOM.ModelBuildStatus.BUILT
end

@testset "HVDC TwoTerminal Unbounded with CopperPlatePowerModel" begin
sys = PSB.build_system(PSITestSystems, "c_sys14_dc")
template = OperationsProblemTemplate(CopperPlatePowerModel)
set_device_model!(template, ThermalStandard, ThermalBasicDispatch)
set_device_model!(template, PowerLoad, StaticPowerLoad)
set_device_model!(template, TwoTerminalGenericHVDCLine, HVDCTwoTerminalUnbounded)
model = DecisionModel(template, sys; optimizer = HiGHS_optimizer)
@test build!(model; output_dir = mktempdir(; cleanup = true)) ==
IOM.ModelBuildStatus.BUILT
@test solve!(model) == IOM.RunStatus.SUCCESSFULLY_FINALIZED
end
Loading
Loading