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