LotSizingDataModel.Solution 2.0.1
Solution objects for production, setup, inventory and related decisions.
Loading...
Searching...
No Matches
WarehouseCapacityDecision.cs
Go to the documentation of this file.
1using System;
2using System.ComponentModel;
3using System.Linq;
4using System.Xml.Serialization;
5using LotSizingDataModel.Core.Common;
6using LotSizingDataModel.Core.PhysicalModel;
7
9
10/// <summary>
11/// Stores the global capacity decisions associated with one
12/// warehouse over the complete planning horizon.
13/// </summary>
14/// <remarks>
15/// Period numbers are one-based.
16///
17/// Item-specific inventory levels, setups and additional-capacity
18/// decisions are stored separately in inventory decisions.
19/// </remarks>
20[Serializable]
21[XmlType(TypeName = "warehouseCapacityDecision")]
22public sealed class WarehouseCapacityDecision :
23 ModelObject,
24 IPlanningHorizonAware
25{
26 private WarehouseReference _warehouse =
27 new();
28
29 private IntegerTimeSeries _activations =
30 new();
31
32 private DoubleTimeSeries _additionalCapacityUsed =
33 new();
34
35 /// <summary>
36 /// Initializes an empty warehouse-capacity decision.
37 /// </summary>
38 /// <remarks>
39 /// This constructor is required by
40 /// <see cref="XmlSerializer"/>.
41 /// </remarks>
43 {
44 SubscribeToObject(_warehouse);
45 SubscribeToObject(_activations);
46 SubscribeToObject(_additionalCapacityUsed);
47 }
48
49 /// <summary>
50 /// Initializes a capacity decision for a warehouse
51 /// and a planning horizon.
52 /// </summary>
53 /// <param name="warehouse">
54 /// Reference to the warehouse.
55 /// </param>
56 /// <param name="planningHorizon">
57 /// Strictly positive number of planning periods.
58 /// </param>
60 WarehouseReference warehouse,
61 int planningHorizon)
62 : this()
63 {
64 ArgumentNullException.ThrowIfNull(warehouse);
65
66 if (warehouse.ReferenceId <= 0)
67 {
68 throw new ArgumentException(
69 "The warehouse reference identifier must be " +
70 "strictly positive.",
71 nameof(warehouse));
72 }
73
74 if (planningHorizon <= 0)
75 {
76 throw new ArgumentOutOfRangeException(
77 nameof(planningHorizon),
78 planningHorizon,
79 "The planning horizon must be strictly positive.");
80 }
81
82 Warehouse = warehouse;
83
84 ResizeTimeSeries(planningHorizon);
85 }
86
87 /// <summary>
88 /// Gets or sets the warehouse associated with
89 /// the capacity decision.
90 /// </summary>
91 [XmlElement("warehouse")]
92 public WarehouseReference Warehouse
93 {
94 get => _warehouse;
95 set
96 {
97 ArgumentNullException.ThrowIfNull(value);
98
99 if (ReferenceEquals(
100 _warehouse,
101 value))
102 {
103 return;
104 }
105
106 UnsubscribeFromObject(_warehouse);
107
108 SetProperty(
109 ref _warehouse,
110 value);
111
112 SubscribeToObject(_warehouse);
113
114 NotifyDerivedProperties();
115 }
116 }
117
118 /// <summary>
119 /// Gets or sets the binary global warehouse
120 /// activation decisions.
121 /// </summary>
122 /// <remarks>
123 /// Each value must be zero or one.
124 ///
125 /// When the supply-chain model does not contain a fixed
126 /// warehouse-activation decision, this series may remain
127 /// filled with zeros.
128 /// </remarks>
129 [XmlElement("activations")]
130 public IntegerTimeSeries Activations
131 {
132 get => _activations;
133 set
134 {
135 ArgumentNullException.ThrowIfNull(value);
136
137 if (ReferenceEquals(
138 _activations,
139 value))
140 {
141 return;
142 }
143
144 UnsubscribeFromObject(_activations);
145
146 SetProperty(
147 ref _activations,
148 value);
149
150 SubscribeToObject(_activations);
151
152 NotifyDerivedProperties();
153 }
154 }
155
156 /// <summary>
157 /// Gets or sets the global additional capacity used
158 /// by the warehouse during each planning period.
159 /// </summary>
160 /// <remarks>
161 /// Values must be finite and non-negative.
162 ///
163 /// The maximum available additional capacity belongs
164 /// to the supply-chain instance and is not duplicated
165 /// in this solution object.
166 /// </remarks>
167 [XmlElement("additionalCapacityUsed")]
168 public DoubleTimeSeries AdditionalCapacityUsed
169 {
170 get => _additionalCapacityUsed;
171 set
172 {
173 ArgumentNullException.ThrowIfNull(value);
174
175 if (ReferenceEquals(
176 _additionalCapacityUsed,
177 value))
178 {
179 return;
180 }
181
182 UnsubscribeFromObject(
183 _additionalCapacityUsed);
184
185 SetProperty(
186 ref _additionalCapacityUsed,
187 value);
188
189 SubscribeToObject(
190 _additionalCapacityUsed);
191
192 NotifyDerivedProperties();
193 }
194 }
195
196 /// <summary>
197 /// Gets the number of planning periods represented
198 /// by the activation series.
199 /// </summary>
200 [XmlIgnore]
201 public int PlanningHorizon =>
202 Activations.PeriodCount;
203
204 /// <summary>
205 /// Gets a value indicating whether every decision series
206 /// uses the same planning horizon.
207 /// </summary>
208 [XmlIgnore]
210 AdditionalCapacityUsed.PeriodCount ==
212
213 /// <summary>
214 /// Gets a value indicating whether the warehouse
215 /// reference is initialized.
216 /// </summary>
217 [XmlIgnore]
219 Warehouse.ReferenceId > 0;
220
221 /// <summary>
222 /// Gets a value indicating whether every activation
223 /// value is equal to zero or one.
224 /// </summary>
225 [XmlIgnore]
227 Activations.All(
228 activation =>
229 activation is 0 or 1);
230
231 /// <summary>
232 /// Gets a value indicating whether every additional-capacity
233 /// value is finite and non-negative.
234 /// </summary>
235 [XmlIgnore]
238 capacity =>
239 double.IsFinite(capacity) &&
240 capacity >= 0.0);
241
242 /// <summary>
243 /// Gets a value indicating whether the warehouse-capacity
244 /// decision is internally consistent.
245 /// </summary>
246 /// <remarks>
247 /// This property does not verify that the warehouse exists
248 /// in a particular supply-chain instance or that the used
249 /// capacity remains below the available capacity.
250 /// </remarks>
251 [XmlIgnore]
252 public bool IsInternallyValid =>
253 PlanningHorizon > 0 &&
258
259 /// <summary>
260 /// Determines whether the warehouse is activated
261 /// during a planning period.
262 /// </summary>
263 /// <param name="period">
264 /// One-based planning period.
265 /// </param>
266 /// <returns>
267 /// True when the activation value is one;
268 /// otherwise, false.
269 /// </returns>
270 public bool IsActivated(int period)
271 {
272 return Activations[period] == 1;
273 }
274
275 /// <summary>
276 /// Sets the global warehouse activation decision
277 /// for a planning period.
278 /// </summary>
279 /// <param name="period">
280 /// One-based planning period.
281 /// </param>
282 /// <param name="isActivated">
283 /// True to activate the warehouse;
284 /// otherwise, false.
285 /// </param>
286 public void SetActivated(
287 int period,
288 bool isActivated)
289 {
290 Activations[period] =
291 isActivated ? 1 : 0;
292 }
293
294 /// <summary>
295 /// Gets the global additional warehouse capacity used
296 /// during a planning period.
297 /// </summary>
298 /// <param name="period">
299 /// One-based planning period.
300 /// </param>
301 /// <returns>
302 /// Non-negative additional-capacity quantity.
303 /// </returns>
305 int period)
306 {
307 return AdditionalCapacityUsed[period];
308 }
309
310 /// <summary>
311 /// Sets the global additional warehouse capacity used
312 /// during a planning period.
313 /// </summary>
314 /// <param name="period">
315 /// One-based planning period.
316 /// </param>
317 /// <param name="capacity">
318 /// Finite and non-negative additional-capacity quantity.
319 /// </param>
321 int period,
322 double capacity)
323 {
324 if (!double.IsFinite(capacity) ||
325 capacity < 0.0)
326 {
327 throw new ArgumentOutOfRangeException(
328 nameof(capacity),
329 capacity,
330 "The additional capacity must be finite " +
331 "and non-negative.");
332 }
333
334 AdditionalCapacityUsed[period] =
335 capacity;
336 }
337
338 /// <summary>
339 /// Determines whether this decision refers to
340 /// the specified warehouse.
341 /// </summary>
342 /// <param name="warehouse">
343 /// Warehouse reference to compare.
344 /// </param>
345 /// <returns>
346 /// True when the warehouse kind and reference identifier
347 /// match; otherwise, false.
348 /// </returns>
349 public bool Matches(
350 WarehouseReference warehouse)
351 {
352 ArgumentNullException.ThrowIfNull(warehouse);
353
354 return SameWarehouse(
355 Warehouse,
356 warehouse);
357 }
358
359 /// <summary>
360 /// Resizes every decision series to the specified
361 /// planning horizon.
362 /// </summary>
363 /// <param name="periodCount">
364 /// Non-negative number of planning periods.
365 /// </param>
366 /// <remarks>
367 /// Existing values are preserved whenever possible.
368 /// New periods are initialized with zero.
369 /// </remarks>
370 public void ResizeTimeSeries(int periodCount)
371 {
372 if (periodCount < 0)
373 {
374 throw new ArgumentOutOfRangeException(
375 nameof(periodCount),
376 periodCount,
377 "The period count cannot be negative.");
378 }
379
380 Activations.Resize(
381 periodCount,
382 defaultValue: 0);
383
385 periodCount,
386 defaultValue: 0.0);
387
388 NotifyDerivedProperties();
389 }
390
391 /// <summary>
392 /// Resets every warehouse-capacity decision value
393 /// to zero.
394 /// </summary>
395 public void Clear()
396 {
397 Activations.Fill(0);
398 AdditionalCapacityUsed.Fill(0.0);
399
400 NotifyDerivedProperties();
401 }
402
403 /// <inheritdoc/>
404 public override string ToString()
405 {
406 int activatedPeriodCount =
407 Activations.Count(
408 activation =>
409 activation == 1);
410
411 double totalAdditionalCapacity =
413
414 return
415 $"Warehouse {Warehouse.Kind}:" +
416 $"{Warehouse.ReferenceId}: " +
417 $"activated periods {activatedPeriodCount}; " +
418 $"additional capacity " +
419 $"{totalAdditionalCapacity}";
420 }
421
422 private void SubscribeToObject(
423 ModelObject modelObject)
424 {
425 modelObject.PropertyChanged +=
426 OnNestedPropertyChanged;
427 }
428
429 private void UnsubscribeFromObject(
430 ModelObject modelObject)
431 {
432 modelObject.PropertyChanged -=
433 OnNestedPropertyChanged;
434 }
435
436 private void OnNestedPropertyChanged(
437 object? sender,
438 PropertyChangedEventArgs eventArgs)
439 {
440 NotifyDerivedProperties();
441 }
442
443 private void NotifyDerivedProperties()
444 {
445 OnPropertyChanged(
446 nameof(PlanningHorizon));
447
448 OnPropertyChanged(
450
451 OnPropertyChanged(
453
454 OnPropertyChanged(
456
457 OnPropertyChanged(
459
460 OnPropertyChanged(
461 nameof(IsInternallyValid));
462 }
463
464 private static bool SameWarehouse(
465 WarehouseReference first,
466 WarehouseReference second)
467 {
468 return first.Kind ==
469 second.Kind &&
470 first.ReferenceId ==
471 second.ReferenceId;
472 }
473}
WarehouseCapacityDecision()
Initializes an empty warehouse-capacity decision.
IntegerTimeSeries Activations
Gets or sets the binary global warehouse activation decisions.
void Clear()
Resets every warehouse-capacity decision value to zero.
WarehouseCapacityDecision(WarehouseReference warehouse, int planningHorizon)
Initializes a capacity decision for a warehouse and a planning horizon.
WarehouseReference Warehouse
Gets or sets the warehouse associated with the capacity decision.
bool IsInternallyValid
Gets a value indicating whether the warehouse-capacity decision is internally consistent.
bool HasConsistentPlanningHorizon
Gets a value indicating whether every decision series uses the same planning horizon.
bool IsActivated(int period)
Determines whether the warehouse is activated during a planning period.
bool HasValidActivationValues
Gets a value indicating whether every activation value is equal to zero or one.
bool HasValidWarehouseReference
Gets a value indicating whether the warehouse reference is initialized.
bool HasValidAdditionalCapacityValues
Gets a value indicating whether every additional-capacity value is finite and non-negative.
DoubleTimeSeries AdditionalCapacityUsed
Gets or sets the global additional capacity used by the warehouse during each planning period.
int PlanningHorizon
Gets the number of planning periods represented by the activation series.
double GetAdditionalCapacityUsed(int period)
Gets the global additional warehouse capacity used during a planning period.
bool Matches(WarehouseReference warehouse)
Determines whether this decision refers to the specified warehouse.
void SetActivated(int period, bool isActivated)
Sets the global warehouse activation decision for a planning period.
void ResizeTimeSeries(int periodCount)
Resizes every decision series to the specified planning horizon.
void SetAdditionalCapacityUsed(int period, double capacity)
Sets the global additional warehouse capacity used during a planning period.