muse2/simulation/investment/appraisal/
constraints.rs1use super::DemandMap;
3use super::optimisation::Variable;
4use crate::asset::{AssetCapacity, AssetRef, AssetState};
5use crate::commodity::Commodity;
6use crate::time_slice::{TimeSliceID, TimeSliceInfo};
7use crate::units::Flow;
8use highs::RowProblem as Problem;
9use indexmap::IndexMap;
10
11pub fn add_capacity_constraint(
17 problem: &mut Problem,
18 asset: &AssetRef,
19 max_capacity: Option<AssetCapacity>,
20 capacity_var: Variable,
21) {
22 let capacity_limit = max_capacity.unwrap_or(asset.capacity());
23 let capacity_limit = match capacity_limit {
24 AssetCapacity::Continuous(cap) => cap.value(),
25 AssetCapacity::Discrete(units, _) => units as f64,
26 };
27
28 let bounds = match asset.state() {
29 AssetState::Commissioned { .. } => {
30 capacity_limit..=capacity_limit
32 }
33 AssetState::Candidate => {
34 0.0..=capacity_limit
36 }
37 _ => panic!(
38 "add_capacity_constraint should only be called with Commissioned or Candidate assets"
39 ),
40 };
41 problem.add_row(bounds, [(capacity_var, 1.0)]);
42}
43
44pub fn add_activity_constraints(
55 problem: &mut Problem,
56 asset: &AssetRef,
57 capacity_var: Variable,
58 activity_vars: &IndexMap<TimeSliceID, Variable>,
59 time_slice_info: &TimeSliceInfo,
60) {
61 match asset.state() {
62 AssetState::Commissioned { .. } => {
63 add_activity_constraints_for_existing(problem, asset, activity_vars, time_slice_info);
64 }
65 AssetState::Candidate => {
66 add_activity_constraints_for_candidate(
67 problem,
68 asset,
69 capacity_var,
70 activity_vars,
71 time_slice_info,
72 );
73 }
74 _ => panic!(
75 "add_activity_constraints should only be called with Commissioned or Candidate assets"
76 ),
77 }
78}
79
80fn add_activity_constraints_for_existing(
81 problem: &mut Problem,
82 asset: &AssetRef,
83 activity_vars: &IndexMap<TimeSliceID, Variable>,
84 time_slice_info: &TimeSliceInfo,
85) {
86 for (ts_selection, limits) in asset.iter_activity_limits() {
87 let limits = limits.start().value()..=limits.end().value();
88
89 let terms = ts_selection
91 .iter(time_slice_info)
92 .map(|(time_slice, _)| (*activity_vars.get(time_slice).unwrap(), 1.0))
93 .collect::<Vec<_>>();
94
95 problem.add_row(limits, &terms);
97 }
98}
99
100fn add_activity_constraints_for_candidate(
101 problem: &mut Problem,
102 asset: &AssetRef,
103 capacity_var: Variable,
104 activity_vars: &IndexMap<TimeSliceID, Variable>,
105 time_slice_info: &TimeSliceInfo,
106) {
107 for (ts_selection, limits) in asset.iter_activity_per_capacity_limits() {
108 let mut upper_limit = limits.end().value();
109 let mut lower_limit = limits.start().value();
110
111 if let AssetCapacity::Discrete(_, unit_size) = asset.capacity() {
114 upper_limit *= unit_size.value();
115 lower_limit *= unit_size.value();
116 }
117
118 let mut terms_upper = vec![(capacity_var, -upper_limit)];
121 let mut terms_lower = vec![(capacity_var, -lower_limit)];
122 for (time_slice, _) in ts_selection.iter(time_slice_info) {
123 let var = *activity_vars.get(time_slice).unwrap();
124 terms_upper.push((var, 1.0));
125 terms_lower.push((var, 1.0));
126 }
127
128 problem.add_row(..=0.0, &terms_upper);
130
131 problem.add_row(0.0.., &terms_lower);
133 }
134}
135
136pub fn add_demand_constraints(
143 problem: &mut Problem,
144 asset: &AssetRef,
145 commodity: &Commodity,
146 time_slice_info: &TimeSliceInfo,
147 demand: &DemandMap,
148 activity_vars: &IndexMap<TimeSliceID, Variable>,
149 unmet_demand_vars: &IndexMap<TimeSliceID, Variable>,
150) {
151 for ts_selection in time_slice_info.iter_selections_at_level(commodity.time_slice_level) {
152 let mut demand_for_ts_selection = Flow(0.0);
153 let mut terms = Vec::new();
154 for (time_slice, _) in ts_selection.iter(time_slice_info) {
155 demand_for_ts_selection += demand[time_slice];
156 let flow_coeff = asset.get_flow(&commodity.id).unwrap().coeff;
157 terms.push((activity_vars[time_slice], flow_coeff.value()));
158 terms.push((unmet_demand_vars[time_slice], 1.0));
159 }
160 problem.add_row(
161 demand_for_ts_selection.value()..=demand_for_ts_selection.value(),
162 terms,
163 );
164 }
165}