diff --git a/gridpath/common_functions.py b/gridpath/common_functions.py index a344ad52c..8cea05c16 100644 --- a/gridpath/common_functions.py +++ b/gridpath/common_functions.py @@ -14,6 +14,7 @@ import os.path import sys +import warnings from argparse import ArgumentParser @@ -398,3 +399,25 @@ def create_results_df(index_columns, results_columns, data): ).set_index(index_columns) return df + + +def duals_wrapper(m, component, verbose=False): + try: + return m.dual[component] + except KeyError: + if verbose: + warnings.warn( + f""" + KeyError caught when saving duals for {component}. Duals were + not exported. This is expected if solving a MIP with CPLEX (and + possibly other solvers), not otherwise. + """ + ) + return None + + +def none_dual_type_error_wrapper(component, coefficient): + try: + return component / coefficient + except TypeError: + return None diff --git a/gridpath/project/capacity/capacity_groups.py b/gridpath/project/capacity/capacity_groups.py index 964d28c0f..02e799d57 100644 --- a/gridpath/project/capacity/capacity_groups.py +++ b/gridpath/project/capacity/capacity_groups.py @@ -22,6 +22,7 @@ from pyomo.environ import Set, Param, Constraint, NonNegativeReals, Expression, value from gridpath.auxiliary.auxiliary import get_required_subtype_modules +from gridpath.common_functions import duals_wrapper, none_dual_type_error_wrapper from gridpath.project.capacity.common_functions import ( load_project_capacity_type_modules, ) @@ -370,9 +371,12 @@ def export_results(scenario_directory, subproblem, stage, m, d): m.capacity_group_new_capacity_max[grp, prd], m.capacity_group_total_capacity_min[grp, prd], m.capacity_group_total_capacity_max[grp, prd], - m.dual[ - getattr(m, "Max_Group_Build_in_Period_Constraint")[grp, prd] - ] + duals_wrapper( + m, + getattr(m, "Max_Group_Build_in_Period_Constraint")[ + grp, prd + ], + ) if (grp, prd) in [ idx @@ -381,9 +385,12 @@ def export_results(scenario_directory, subproblem, stage, m, d): ) ] else None, - m.dual[ - getattr(m, "Min_Group_Build_in_Period_Constraint")[grp, prd] - ] + duals_wrapper( + m, + getattr(m, "Min_Group_Build_in_Period_Constraint")[ + grp, prd + ], + ) if (grp, prd) in [ idx @@ -392,24 +399,26 @@ def export_results(scenario_directory, subproblem, stage, m, d): ) ] else None, - m.dual[ + duals_wrapper( + m, getattr(m, "Max_Group_Total_Cap_in_Period_Constraint")[ grp, prd ] - ] - if (grp, prd) - in [ - idx - for idx in getattr( - m, "Max_Group_Total_Cap_in_Period_Constraint" - ) - ] - else None, - m.dual[ - getattr(m, "Min_Group_Total_Cap_in_Period_Constraint")[ - grp, prd + if (grp, prd) + in [ + idx + for idx in getattr( + m, "Max_Group_Total_Cap_in_Period_Constraint" + ) ] - ] + else None, + duals_wrapper( + m, + getattr(m, "Min_Group_Total_Cap_in_Period_Constraint")[ + grp, prd + ], + ), + ) if (grp, prd) in [ idx @@ -418,10 +427,15 @@ def export_results(scenario_directory, subproblem, stage, m, d): ) ] else None, - m.dual[ - getattr(m, "Max_Group_Build_in_Period_Constraint")[grp, prd] - ] - / m.period_objective_coefficient[prd] + none_dual_type_error_wrapper( + duals_wrapper( + m, + getattr(m, "Max_Group_Build_in_Period_Constraint")[ + grp, prd + ], + ), + m.period_objective_coefficient[prd], + ) if (grp, prd) in [ idx @@ -430,10 +444,15 @@ def export_results(scenario_directory, subproblem, stage, m, d): ) ] else None, - m.dual[ - getattr(m, "Min_Group_Build_in_Period_Constraint")[grp, prd] - ] - / m.period_objective_coefficient[prd] + none_dual_type_error_wrapper( + duals_wrapper( + m, + getattr(m, "Min_Group_Build_in_Period_Constraint")[ + grp, prd + ], + ), + m.period_objective_coefficient[prd], + ) if (grp, prd) in [ idx @@ -442,12 +461,15 @@ def export_results(scenario_directory, subproblem, stage, m, d): ) ] else None, - m.dual[ - getattr(m, "Max_Group_Total_Cap_in_Period_Constraint")[ - grp, prd - ] - ] - / m.period_objective_coefficient[prd] + none_dual_type_error_wrapper( + duals_wrapper( + m, + getattr(m, "Max_Group_Total_Cap_in_Period_Constraint")[ + grp, prd + ], + ), + m.period_objective_coefficient[prd], + ) if (grp, prd) in [ idx @@ -456,12 +478,15 @@ def export_results(scenario_directory, subproblem, stage, m, d): ) ] else None, - m.dual[ - getattr(m, "Min_Group_Total_Cap_in_Period_Constraint")[ - grp, prd - ] - ] - / m.period_objective_coefficient[prd] + none_dual_type_error_wrapper( + duals_wrapper( + m, + getattr(m, "Min_Group_Total_Cap_in_Period_Constraint")[ + grp, prd + ], + ), + m.period_objective_coefficient[prd], + ) if (grp, prd) in [ idx diff --git a/gridpath/project/capacity/potential.py b/gridpath/project/capacity/potential.py index ff4c613ca..50a0c6086 100644 --- a/gridpath/project/capacity/potential.py +++ b/gridpath/project/capacity/potential.py @@ -28,7 +28,7 @@ validate_column_monotonicity, ) from gridpath.auxiliary.auxiliary import get_required_subtype_modules -from gridpath.common_functions import create_results_df +from gridpath.common_functions import create_results_df, duals_wrapper from gridpath.project import PROJECT_PERIOD_DF from gridpath.project.capacity.common_functions import ( load_project_capacity_type_modules, @@ -586,28 +586,28 @@ def export_results(scenario_directory, subproblem, stage, m, d): [ prj, prd, - m.dual[getattr(m, "Min_Build_Power_Constraint")[prj, prd]] + duals_wrapper(m, getattr(m, "Min_Build_Power_Constraint")[prj, prd]) if (prj, prd) in [idx for idx in getattr(m, "Min_Build_Power_Constraint")] else None, - m.dual[getattr(m, "Max_Build_Power_Constraint")[prj, prd]] + duals_wrapper(m, getattr(m, "Max_Build_Power_Constraint")[prj, prd]) if (prj, prd) in [idx for idx in getattr(m, "Max_Build_Power_Constraint")] else None, - m.dual[getattr(m, "Min_Power_Constraint")[prj, prd]] + duals_wrapper(m, getattr(m, "Min_Power_Constraint")[prj, prd]) if (prj, prd) in [idx for idx in getattr(m, "Min_Power_Constraint")] else None, - m.dual[getattr(m, "Max_Power_Constraint")[prj, prd]] + duals_wrapper(m, getattr(m, "Max_Power_Constraint")[prj, prd]) if (prj, prd) in [idx for idx in getattr(m, "Max_Power_Constraint")] else None, - m.dual[getattr(m, "Min_Build_Energy_Constraint")[prj, prd]] + duals_wrapper(m, getattr(m, "Min_Build_Energy_Constraint")[prj, prd]) if (prj, prd) in [idx for idx in getattr(m, "Min_Build_Energy_Constraint")] else None, - m.dual[getattr(m, "Max_Build_Energy_Constraint")[prj, prd]] + duals_wrapper(m, getattr(m, "Max_Build_Energy_Constraint")[prj, prd]) if (prj, prd) in [idx for idx in getattr(m, "Max_Build_Energy_Constraint")] else None, - m.dual[getattr(m, "Min_Energy_Constraint")[prj, prd]] + duals_wrapper(m, getattr(m, "Min_Energy_Constraint")[prj, prd]) if (prj, prd) in [idx for idx in getattr(m, "Min_Energy_Constraint")] else None, - m.dual[getattr(m, "Max_Energy_Constraint")[prj, prd]] + duals_wrapper(m, getattr(m, "Max_Energy_Constraint")[prj, prd]) if (prj, prd) in [idx for idx in getattr(m, "Max_Energy_Constraint")] else None, ] diff --git a/gridpath/project/operations/operational_types/gen_commit_unit_common.py b/gridpath/project/operations/operational_types/gen_commit_unit_common.py index df7596f79..6f827775c 100644 --- a/gridpath/project/operations/operational_types/gen_commit_unit_common.py +++ b/gridpath/project/operations/operational_types/gen_commit_unit_common.py @@ -67,6 +67,7 @@ subset_init_by_set_membership, ) from gridpath.auxiliary.dynamic_components import headroom_variables, footroom_variables +from gridpath.common_functions import duals_wrapper from gridpath.project.operations.operational_types.common_functions import ( determine_relevant_timepoints, load_optype_model_data, @@ -3803,7 +3804,7 @@ def add_duals_to_dispatch_results(mod, Bin_or_Lin, BIN_OR_LIN): for c in sorted(constraint_column_dict.keys()): constraint_object = getattr(mod, c) if (prj, tmp) in constraint_object: - duals.append(mod.dual[constraint_object[prj, tmp]]) + duals.append(duals_wrapper(mod, constraint_object[prj, tmp])) else: duals.append(None) diff --git a/gridpath/run_scenario.py b/gridpath/run_scenario.py index e0d4601c5..f5c09c3cd 100644 --- a/gridpath/run_scenario.py +++ b/gridpath/run_scenario.py @@ -1020,23 +1020,6 @@ def save_duals( ) n += 1 - # for index in constraint_object: - # try: - # duals_writer.writerow( - # list(index) + [instance.dual[constraint_object[index]]] - # ) - # # We get an error when trying to export duals with CPLEX - # # when solving MIPs, so catch it here and ignore to avoid - # # breaking the script, but throw a warning - # except KeyError: - # warnings.warn( - # """ - # KeyError caught when saving duals. Duals were not exported. - # This is expected if solving a MIP with CPLEX, - # not otherwise. - # """ - # ) - def summarize_results(scenario_directory, subproblem, stage, parsed_arguments): """ diff --git a/gridpath/system/load_balance/load_balance.py b/gridpath/system/load_balance/load_balance.py index 98811e533..ae9a74c94 100644 --- a/gridpath/system/load_balance/load_balance.py +++ b/gridpath/system/load_balance/load_balance.py @@ -47,7 +47,11 @@ load_balance_consumption_components, load_balance_production_components, ) -from gridpath.common_functions import create_results_df +from gridpath.common_functions import ( + create_results_df, + duals_wrapper, + none_dual_type_error_wrapper, +) from gridpath.system.load_balance import LOAD_ZONE_TMP_DF @@ -198,9 +202,11 @@ def export_results(scenario_directory, subproblem, stage, m, d): tmp, value(m.Overgeneration_MW_Expression[lz, tmp]), value(m.Unserved_Energy_MW_Expression[lz, tmp]), - m.dual[getattr(m, "Meet_Load_Constraint")[lz, tmp]], - m.dual[getattr(m, "Meet_Load_Constraint")[lz, tmp]] - / m.tmp_objective_coefficient[tmp], + duals_wrapper(m, getattr(m, "Meet_Load_Constraint")[lz, tmp]), + none_dual_type_error_wrapper( + duals_wrapper(m, getattr(m, "Meet_Load_Constraint")[lz, tmp]), + m.tmp_objective_coefficient[tmp], + ), ] for lz in getattr(m, "LOAD_ZONES") for tmp in getattr(m, "TMPS") diff --git a/gridpath/system/policy/carbon_cap/carbon_balance.py b/gridpath/system/policy/carbon_cap/carbon_balance.py index 34211ea1b..9baf5d2f5 100644 --- a/gridpath/system/policy/carbon_cap/carbon_balance.py +++ b/gridpath/system/policy/carbon_cap/carbon_balance.py @@ -22,7 +22,11 @@ carbon_cap_balance_emission_components, carbon_cap_balance_credit_components, ) -from gridpath.common_functions import create_results_df +from gridpath.common_functions import ( + create_results_df, + duals_wrapper, + none_dual_type_error_wrapper, +) from gridpath.system.policy.carbon_cap import CARBON_CAP_ZONE_PRD_DF @@ -107,12 +111,14 @@ def export_results(scenario_directory, subproblem, stage, m, d): p, value(m.Total_Carbon_Emissions_from_All_Sources_Expression[z, p]), value(m.Total_Carbon_Credits_from_All_Sources_Expression[z, p]), - m.dual[getattr(m, "Carbon_Cap_Constraint")[z, p]] + duals_wrapper(m, getattr(m, "Carbon_Cap_Constraint")[z, p]) if (z, p) in [idx for idx in getattr(m, "Carbon_Cap_Constraint")] else None, ( - m.dual[getattr(m, "Carbon_Cap_Constraint")[z, p]] - / m.period_objective_coefficient[p] + none_dual_type_error_wrapper( + duals_wrapper(m, getattr(m, "Carbon_Cap_Constraint")[z, p]), + m.period_objective_coefficient[p], + ) if (z, p) in [idx for idx in getattr(m, "Carbon_Cap_Constraint")] else None ), diff --git a/gridpath/system/policy/energy_targets/horizon_energy_target_balance.py b/gridpath/system/policy/energy_targets/horizon_energy_target_balance.py index 3f246ed6c..89bcbbe57 100644 --- a/gridpath/system/policy/energy_targets/horizon_energy_target_balance.py +++ b/gridpath/system/policy/energy_targets/horizon_energy_target_balance.py @@ -23,7 +23,11 @@ from pyomo.environ import Var, Constraint, NonNegativeReals, Expression, value -from gridpath.common_functions import create_results_df +from gridpath.common_functions import ( + create_results_df, + duals_wrapper, + none_dual_type_error_wrapper, +) from gridpath.system.policy.energy_targets import ENERGY_TARGET_ZONE_HRZ_DF @@ -114,13 +118,17 @@ def export_results(scenario_directory, subproblem, stage, m, d): + value(m.Total_Curtailed_Horizon_Energy_Target_Energy_MWh[z, bt, h]) ), value(m.Horizon_Energy_Target_Shortage_MWh_Expression[z, bt, h]), - m.dual[getattr(m, "Horizon_Energy_Target_Constraint")[z, bt, h]] + duals_wrapper(m, getattr(m, "Horizon_Energy_Target_Constraint")[z, bt, h]) if (z, bt, h) in [idx for idx in getattr(m, "Horizon_Energy_Target_Constraint")] else None, ( - m.dual[getattr(m, "Horizon_Energy_Target_Constraint")[z, bt, h]] - / m.hrz_objective_coefficient[bt, h] + none_dual_type_error_wrapper( + duals_wrapper( + m, getattr(m, "Horizon_Energy_Target_Constraint")[z, bt, h] + ), + m.hrz_objective_coefficient[bt, h], + ) if (z, bt, h) in [idx for idx in getattr(m, "Horizon_Energy_Target_Constraint")] else None diff --git a/gridpath/system/policy/energy_targets/period_energy_target_balance.py b/gridpath/system/policy/energy_targets/period_energy_target_balance.py index 8e912540c..d1349bc73 100644 --- a/gridpath/system/policy/energy_targets/period_energy_target_balance.py +++ b/gridpath/system/policy/energy_targets/period_energy_target_balance.py @@ -23,7 +23,11 @@ from pyomo.environ import Var, Constraint, NonNegativeReals, Expression, value -from gridpath.common_functions import create_results_df +from gridpath.common_functions import ( + create_results_df, + duals_wrapper, + none_dual_type_error_wrapper, +) from gridpath.system.policy.energy_targets import ENERGY_TARGET_ZONE_PRD_DF @@ -111,12 +115,16 @@ def export_results(scenario_directory, subproblem, stage, m, d): + value(m.Total_Curtailed_Period_Energy_Target_Energy_MWh[z, p]) ), value(m.Period_Energy_Target_Shortage_MWh_Expression[z, p]), - m.dual[getattr(m, "Period_Energy_Target_Constraint")[z, p]] + duals_wrapper(m, getattr(m, "Period_Energy_Target_Constraint")[z, p]) if (z, p) in [idx for idx in getattr(m, "Period_Energy_Target_Constraint")] else None, ( - m.dual[getattr(m, "Period_Energy_Target_Constraint")[z, p]] - / m.period_objective_coefficient[p] + none_dual_type_error_wrapper( + duals_wrapper( + m, getattr(m, "Period_Energy_Target_Constraint")[z, p] + ), + m.period_objective_coefficient[p], + ) if (z, p) in [idx for idx in getattr(m, "Period_Energy_Target_Constraint")] else None diff --git a/gridpath/system/policy/fuel_burn_limits/fuel_burn_limit_balance.py b/gridpath/system/policy/fuel_burn_limits/fuel_burn_limit_balance.py index 344a43742..e34fb2163 100644 --- a/gridpath/system/policy/fuel_burn_limits/fuel_burn_limit_balance.py +++ b/gridpath/system/policy/fuel_burn_limits/fuel_burn_limit_balance.py @@ -25,7 +25,11 @@ from db.common_functions import spin_on_database_lock from gridpath.auxiliary.db_interface import setup_results_import from gridpath.auxiliary.dynamic_components import fuel_burn_balance_components -from gridpath.common_functions import create_results_df +from gridpath.common_functions import ( + create_results_df, + duals_wrapper, + none_dual_type_error_wrapper, +) from gridpath.system.policy.fuel_burn_limits import FUEL_BURN_LIMITS_DF @@ -231,35 +235,53 @@ def export_results(scenario_directory, subproblem, stage, m, d): if (f, z, bt, h) in m.FUEL_FUEL_BA_BLN_TYPE_HRZS_WITH_FUEL_BURN_MAX_REL_LIMIT else None, - m.dual[getattr(m, "Meet_Fuel_Burn_Min_Abs_Constraint")[f, z, bt, h]] + duals_wrapper( + m, getattr(m, "Meet_Fuel_Burn_Min_Abs_Constraint")[f, z, bt, h] + ) if (f, z, bt, h) in [idx for idx in getattr(m, "Meet_Fuel_Burn_Min_Abs_Constraint")] else None, ( - m.dual[getattr(m, "Meet_Fuel_Burn_Min_Abs_Constraint")[f, z, bt, h]] - / m.hrz_objective_coefficient[bt, h] + none_dual_type_error_wrapper( + duals_wrapper( + m, getattr(m, "Meet_Fuel_Burn_Min_Abs_Constraint")[f, z, bt, h] + ), + m.hrz_objective_coefficient[bt, h], + ) if (f, z, bt, h) in [idx for idx in getattr(m, "Meet_Fuel_Burn_Min_Abs_Constraint")] else None ), - m.dual[getattr(m, "Meet_Fuel_Burn_Max_Abs_Constraint")[f, z, bt, h]] + duals_wrapper( + m, getattr(m, "Meet_Fuel_Burn_Max_Abs_Constraint")[f, z, bt, h] + ) if (f, z, bt, h) in [idx for idx in getattr(m, "Meet_Fuel_Burn_Max_Abs_Constraint")] else None, ( - m.dual[getattr(m, "Meet_Fuel_Burn_Max_Abs_Constraint")[f, z, bt, h]] - / m.hrz_objective_coefficient[bt, h] + none_dual_type_error_wrapper( + duals_wrapper( + m, getattr(m, "Meet_Fuel_Burn_Max_Abs_Constraint")[f, z, bt, h] + ), + m.hrz_objective_coefficient[bt, h], + ) if (f, z, bt, h) in [idx for idx in getattr(m, "Meet_Fuel_Burn_Max_Abs_Constraint")] else None ), - m.dual[getattr(m, "Meet_Fuel_Burn_Max_Rel_Constraint")[f, z, bt, h]] + duals_wrapper( + m, getattr(m, "Meet_Fuel_Burn_Max_Rel_Constraint")[f, z, bt, h] + ) if (f, z, bt, h) in [idx for idx in getattr(m, "Meet_Fuel_Burn_Max_Rel_Constraint")] else None, ( - m.dual[getattr(m, "Meet_Fuel_Burn_Max_Rel_Constraint")[f, z, bt, h]] - / m.hrz_objective_coefficient[bt, h] + none_dual_type_error_wrapper( + duals_wrapper( + m, getattr(m, "Meet_Fuel_Burn_Max_Rel_Constraint")[f, z, bt, h] + ), + m.hrz_objective_coefficient[bt, h], + ) if (f, z, bt, h) in [idx for idx in getattr(m, "Meet_Fuel_Burn_Max_Rel_Constraint")] else None diff --git a/gridpath/system/reliability/local_capacity/local_capacity_balance.py b/gridpath/system/reliability/local_capacity/local_capacity_balance.py index 0c6cc3b67..eeb6971bf 100644 --- a/gridpath/system/reliability/local_capacity/local_capacity_balance.py +++ b/gridpath/system/reliability/local_capacity/local_capacity_balance.py @@ -27,7 +27,11 @@ from gridpath.auxiliary.dynamic_components import ( local_capacity_balance_provision_components, ) -from gridpath.common_functions import create_results_df +from gridpath.common_functions import ( + create_results_df, + duals_wrapper, + none_dual_type_error_wrapper, +) from gridpath.system.reliability.local_capacity import LOCAL_CAPACITY_ZONE_PRD_DF @@ -105,12 +109,14 @@ def export_results(scenario_directory, subproblem, stage, m, d): p, value(m.Total_Local_Capacity_from_All_Sources_Expression_MW[z, p]), value(m.Local_Capacity_Shortage_MW_Expression[z, p]), - m.dual[getattr(m, "Local_Capacity_Constraint")[z, p]] + duals_wrapper(m, getattr(m, "Local_Capacity_Constraint")[z, p]) if (z, p) in [idx for idx in getattr(m, "Local_Capacity_Constraint")] else None, ( - m.dual[getattr(m, "Local_Capacity_Constraint")[z, p]] - / m.period_objective_coefficient[p] + none_dual_type_error_wrapper( + duals_wrapper(m, getattr(m, "Local_Capacity_Constraint")[z, p]), + m.period_objective_coefficient[p], + ) if (z, p) in [idx for idx in getattr(m, "Local_Capacity_Constraint")] else None ), diff --git a/gridpath/system/reliability/prm/prm_balance.py b/gridpath/system/reliability/prm/prm_balance.py index dab73d4ed..8dc9ae3fc 100644 --- a/gridpath/system/reliability/prm/prm_balance.py +++ b/gridpath/system/reliability/prm/prm_balance.py @@ -24,7 +24,11 @@ from db.common_functions import spin_on_database_lock from gridpath.auxiliary.dynamic_components import prm_balance_provision_components -from gridpath.common_functions import create_results_df +from gridpath.common_functions import ( + create_results_df, + duals_wrapper, + none_dual_type_error_wrapper, +) from gridpath.system.reliability.prm import PRM_ZONE_PRD_DF @@ -100,12 +104,14 @@ def export_results(scenario_directory, subproblem, stage, m, d): p, value(m.Total_PRM_from_All_Sources_Expression[z, p]), value(m.PRM_Shortage_MW_Expression[z, p]), - m.dual[getattr(m, "PRM_Constraint")[z, p]] + duals_wrapper(m, getattr(m, "PRM_Constraint")[z, p]) if (z, p) in [idx for idx in getattr(m, "PRM_Constraint")] else None, ( - m.dual[getattr(m, "PRM_Constraint")[z, p]] - / m.period_objective_coefficient[p] + none_dual_type_error_wrapper( + duals_wrapper(m, getattr(m, "PRM_Constraint")[z, p]), + m.period_objective_coefficient[p], + ) if (z, p) in [idx for idx in getattr(m, "PRM_Constraint")] else None ), diff --git a/gridpath/system/reserves/balance/reserve_balance.py b/gridpath/system/reserves/balance/reserve_balance.py index 85707f4d8..728979ef9 100644 --- a/gridpath/system/reserves/balance/reserve_balance.py +++ b/gridpath/system/reserves/balance/reserve_balance.py @@ -18,6 +18,7 @@ from pyomo.environ import Var, Constraint, NonNegativeReals, Expression, value from gridpath.auxiliary.db_interface import import_csv +from gridpath.common_functions import duals_wrapper, none_dual_type_error_wrapper def generic_add_model_components( @@ -159,12 +160,16 @@ def generic_export_results( m.tmp_weight[tmp], m.hrs_in_tmp[tmp], value(getattr(m, reserve_violation_expression)[ba, tmp]), - m.dual[getattr(m, duals_map[reserve_type])[ba, tmp]] + duals_wrapper(m, getattr(m, duals_map[reserve_type])[ba, tmp]) if (ba, tmp) in [idx for idx in getattr(m, duals_map[reserve_type])] else None, ( - m.dual[getattr(m, duals_map[reserve_type])[ba, tmp]] - / m.tmp_objective_coefficient[tmp] + none_dual_type_error_wrapper( + duals_wrapper( + m, getattr(m, duals_map[reserve_type])[ba, tmp] + ), + m.tmp_objective_coefficient[tmp], + ) if (ba, tmp) in [idx for idx in getattr(m, duals_map[reserve_type])] else None