2020from temoa .components import time
2121
2222if TYPE_CHECKING :
23+ from pyomo .core .base import Expression
24+
2325 from temoa .core .model import TemoaModel
2426 from temoa .types import ExprLike
2527 from temoa .types .core_types import Period , Region , Season , Technology , TimeOfDay , Vintage
@@ -192,7 +194,7 @@ def create_operational_vintage_sets(model: TemoaModel) -> None:
192194 model .is_seasonal_storage [t ] = t in model .tech_seasonal_storage
193195
194196
195- def _ramp_constraint (
197+ def _ramp_activity_increase (
196198 model : TemoaModel ,
197199 ramp_up : bool ,
198200 r : Region ,
@@ -203,25 +205,12 @@ def _ramp_constraint(
203205 d_next : TimeOfDay ,
204206 t : Technology ,
205207 v : Vintage ,
206- ) -> ExprLike :
207- """
208- Helper function to compute ramping constraints for both ramp-up and ramp-down.
209-
210- Args:
211- model (TemoaModel): The Temoa model instance.
212- ramp_up (bool): True for ramp-up constraints, False for ramp-down constraints.
213- r (Region): The region.
214- p (Period): The period.
215- s (Season): The season.
216- d (TimeOfDay): The time of day.
217- s_next (Season): The next season.
218- d_next (TimeOfDay): The next time of day.
219- t (Technology): The technology.
220- v (Vintage): The vintage.
221-
222- Returns:
223- Expression | Constraint.Skip: A tuple containing the activity increase
224- and rampable activity expressions or a constraint skip.
208+ ) -> tuple [Expression , float ] | None :
209+ """Compute the hourly activity increase between adjacent time slices and the
210+ ramp fraction for the given direction.
211+
212+ Returns ``(activity_increase, ramp_fraction)`` or ``None`` when the ramp
213+ rate is so large that the constraint would never bind (skip).
225214 """
226215 ramping_param = model .ramp_up_hourly if ramp_up else model .ramp_down_hourly
227216
@@ -246,19 +235,51 @@ def _ramp_constraint(
246235 f'Should be less than { 1 / hours_elapsed :.4f} . Constraint skipped.'
247236 )
248237 logger .warning (msg .format (ramping_param .name , r , t , p , s , d , p , s_next , d_next ))
249- return Constraint . Skip
238+ return None
250239
251240 activity_increase = hourly_activity_sd_next - hourly_activity_sd
252- rampable_activity = (
241+ return activity_increase , ramp_fraction
242+
243+
244+ def _rampable_activity (
245+ model : TemoaModel ,
246+ r : Region ,
247+ p : Period ,
248+ t : Technology ,
249+ v : Vintage ,
250+ ramp_fraction : float ,
251+ ) -> ExprLike :
252+ """Rampable activity for the core model: fraction of total installed capacity."""
253+ return (
253254 ramp_fraction
254255 * model .v_capacity [r , p , t , v ]
255256 * value (model .capacity_to_activity [r , t ])
256257 / (24 * value (model .days_per_period ))
257258 )
259+
260+
261+ def _ramp_constraint (
262+ model : TemoaModel ,
263+ ramp_up : bool ,
264+ r : Region ,
265+ p : Period ,
266+ s : Season ,
267+ d : TimeOfDay ,
268+ t : Technology ,
269+ v : Vintage ,
270+ ) -> ExprLike :
271+ """Helper that composes :func:`_ramp_activity_increase` and
272+ :func:`_rampable_activity` into the core-model ramp constraint."""
273+ s_next , d_next = model .time_next [s , d ]
274+ result = _ramp_activity_increase (model , ramp_up , r , p , s , d , s_next , d_next , t , v )
275+ if result is None :
276+ return Constraint .Skip
277+ activity_increase , ramp_fraction = result
278+ rampable = _rampable_activity (model , r , p , t , v , ramp_fraction )
258279 if ramp_up :
259- return activity_increase <= rampable_activity
280+ return activity_increase <= rampable
260281 else :
261- return - activity_increase <= rampable_activity
282+ return - activity_increase <= rampable
262283
263284
264285def ramp_up_constraint (
@@ -314,17 +335,13 @@ def ramp_up_constraint(
314335 i.e. :math:`(H_d + H_{d_{next}}) / 2`
315336 - :math:`CAP \cdot C2A / (24 \cdot DPP)` gives the maximum hourly capacity
316337 """
317-
318- s_next , d_next = model .time_next [s , d ]
319338 return _ramp_constraint (
320339 model = model ,
321340 ramp_up = True ,
322341 r = r ,
323342 p = p ,
324343 s = s ,
325344 d = d ,
326- s_next = s_next ,
327- d_next = d_next ,
328345 t = t ,
329346 v = v ,
330347 )
@@ -365,17 +382,13 @@ def ramp_down_constraint(
365382 \\
366383 \text{where: } \Delta H = \frac{H_d + H_{d_{next}}}{2}
367384 """
368-
369- s_next , d_next = model .time_next [s , d ]
370385 return _ramp_constraint (
371386 model = model ,
372387 ramp_up = False ,
373388 r = r ,
374389 p = p ,
375390 s = s ,
376391 d = d ,
377- s_next = s_next ,
378- d_next = d_next ,
379392 t = t ,
380393 v = v ,
381394 )
0 commit comments