LotSizingDataModel.Core 2.0.1
Core domain model, shared abstractions and XML-serializable entities.
Loading...
Searching...
No Matches
Demand.cs
Go to the documentation of this file.
1using System;
2using System.ComponentModel;
3using System.Linq;
4using System.Xml.Serialization;
6
8
9/// <summary>
10/// Represents the demand for an item issued by a distribution center.
11///
12/// Corresponds to the UML association class "Demande"
13/// between Article and Centre de distribution.
14///
15/// Demand quantities correspond to D[c,i,t] in the source model.
16/// </summary>
17[Serializable]
18[XmlType(TypeName = "demand")]
19public sealed partial class Demand :
22{
23 private int _itemId;
24 private int _distributionCenterId;
25 private DoubleTimeSeries _quantities = new();
26
27 /// <summary>
28 /// Initializes an empty demand.
29 ///
30 /// This public parameterless constructor is required
31 /// by <see cref="XmlSerializer"/>.
32 /// </summary>
33 public Demand()
34 {
35 // Subscribe to changes in the quantities time series
36 SubscribeToQuantities(_quantities);
37 }
38
39 /// <summary>
40 /// Initializes a demand for an item and a distribution center.
41 /// </summary>
42 /// <param name="itemId">
43 /// Identifier of the requested item.
44 /// </param>
45 /// <param name="distributionCenterId">
46 /// Identifier of the distribution center issuing the demand.
47 /// </param>
48 /// <param name="planningHorizon">
49 /// Number of periods in the planning horizon.
50 /// </param>
51 public Demand(
52 int itemId,
53 int distributionCenterId,
54 int planningHorizon)
55 : this() // Call default constructor to subscribe to quantities
56 {
57 // Initialize demand properties (validation occurs in setters)
58 ItemId = itemId;
59 DistributionCenterId = distributionCenterId;
60 ResizeTimeSeries(planningHorizon);
61 }
62
63 /// <summary>
64 /// Gets or sets the identifier of the requested item.
65 ///
66 /// Corresponds to index i in D[c,i,t].
67 /// </summary>
68 [XmlAttribute("itemId")]
69 public int ItemId
70 {
71 get => _itemId;
72 set
73 {
74 // Validate that the item identifier is non-negative
75 if (value < 0)
76 {
77 throw new ArgumentOutOfRangeException(
78 nameof(value),
79 value,
80 "The item identifier cannot be negative.");
81 }
82
83 // Update the backing field and notify property change if value differs
84 SetProperty(ref _itemId, value);
85 }
86 }
87
88 /// <summary>
89 /// Gets or sets the identifier of the distribution center
90 /// issuing the demand.
91 ///
92 /// Corresponds to index c in D[c,i,t].
93 /// </summary>
94 [XmlAttribute("distributionCenterId")]
96 {
97 get => _distributionCenterId;
98 set
99 {
100 // Validate that the distribution center identifier is non-negative
101 if (value < 0)
102 {
103 throw new ArgumentOutOfRangeException(
104 nameof(value),
105 value,
106 "The distribution-center identifier cannot be negative.");
107 }
108
109 // Update the backing field and notify property change if value differs
110 SetProperty(
111 ref _distributionCenterId,
112 value);
113 }
114 }
115
116 /// <summary>
117 /// Gets or sets the quantities requested for each planning period.
118 ///
119 /// The value for period t corresponds to D[c,i,t]
120 /// in the source model.
121 /// </summary>
122 [XmlElement("quantities")]
124 {
125 get => _quantities;
126 set
127 {
128 // Ensure the time series is never null
129 DoubleTimeSeries newValue =
130 value ?? new DoubleTimeSeries();
131
132 // Avoid unnecessary updates if the reference is the same
133 if (ReferenceEquals(_quantities, newValue))
134 {
135 return;
136 }
137
138 // Unsubscribe from the old time series
139 UnsubscribeFromQuantities(_quantities);
140
141 _quantities = newValue;
142
143 // Subscribe to the new time series
144 SubscribeToQuantities(_quantities);
145
146 // Notify dependent properties
149 }
150 }
151
152 /// <summary>
153 /// Gets the number of periods represented by the demand.
154 ///
155 /// This calculated property is not serialized because the information
156 /// is already contained in <see cref="Quantities"/>.
157 /// </summary>
158 [XmlIgnore]
159 public int PlanningHorizon =>
160 Quantities.PeriodCount;
161
162 /// <summary>
163 /// Gets the demand quantity for a planning period.
164 /// </summary>
165 /// <param name="period">
166 /// One-based planning-period number.
167 /// </param>
168 public double GetQuantity(int period)
169 {
170 return Quantities[period];
171 }
172
173 /// <summary>
174 /// Sets the demand quantity for a planning period.
175 /// </summary>
176 /// <param name="period">
177 /// One-based planning-period number.
178 /// </param>
179 /// <param name="quantity">
180 /// Non-negative finite demand quantity.
181 /// </param>
182 public void SetQuantity(
183 int period,
184 double quantity)
185 {
186 // Validate the quantity before setting
187 ValidateQuantity(quantity, nameof(quantity));
188
189 Quantities[period] = quantity;
190 }
191
192 /// <summary>
193 /// Assigns the same demand quantity to every period.
194 /// </summary>
195 public void Fill(double quantity)
196 {
197 // Validate the quantity before filling
198 ValidateQuantity(quantity, nameof(quantity));
199
200 Quantities.Fill(quantity);
201 }
202
203 /// <summary>
204 /// Resizes the period-dependent demand data.
205 ///
206 /// Existing values are preserved. Newly created periods receive
207 /// a demand quantity of zero.
208 /// </summary>
209 public void ResizeTimeSeries(int periodCount)
210 {
211 // Validate that the period count is non-negative
212 if (periodCount < 0)
213 {
214 throw new ArgumentOutOfRangeException(
215 nameof(periodCount),
216 periodCount,
217 "The planning horizon cannot be negative.");
218 }
219
220 // Resize quantities with zero as default value for new periods
221 Quantities.Resize(
222 periodCount,
223 defaultValue: 0.0);
224 }
225
226 /// <summary>
227 /// Determines whether this object represents the same
228 /// item/distribution-center association as another demand.
229 /// </summary>
230 public bool RefersToSameDemand(Demand? other)
231 {
232 // Compare both item ID and distribution center ID for equality
233 return other is not null
234 && ItemId == other.ItemId
237 }
238
239 /// <summary>
240 /// Determines whether this demand concerns the specified item.
241 /// </summary>
242 public bool ConcernsItem(int itemId)
243 {
244 return ItemId == itemId;
245 }
246
247 /// <summary>
248 /// Determines whether this demand concerns
249 /// the specified distribution center.
250 /// </summary>
252 int distributionCenterId)
253 {
254 // Check if the distribution center ID matches
255 return DistributionCenterId ==
256 distributionCenterId;
257 }
258
259 /// <summary>
260 /// Checks whether all currently stored demand quantities
261 /// are finite and non-negative.
262 /// </summary>
263 [XmlIgnore]
264 public bool HasValidQuantities =>
265 Quantities.All(
266 quantity =>
267 !double.IsNaN(quantity)
268 && !double.IsInfinity(quantity)
269 && quantity >= 0.0);
270
271 /// <summary>
272 /// Subscribes to property change notifications from the quantities time series.
273 /// </summary>
274 private void SubscribeToQuantities(
275 DoubleTimeSeries quantities)
276 {
277 // Listen to property changes in the time series
278 quantities.PropertyChanged +=
279 OnQuantitiesPropertyChanged;
280 }
281
282 /// <summary>
283 /// Unsubscribes from property change notifications from the quantities time series.
284 /// </summary>
285 private void UnsubscribeFromQuantities(
286 DoubleTimeSeries quantities)
287 {
288 // Stop listening to property changes in the time series
289 quantities.PropertyChanged -=
290 OnQuantitiesPropertyChanged;
291 }
292
293 /// <summary>
294 /// Handles property change notifications from the quantities time series
295 /// and propagates relevant changes to dependent properties.
296 /// </summary>
297 private void OnQuantitiesPropertyChanged(
298 object? sender,
299 PropertyChangedEventArgs e)
300 {
301 // Always notify that Quantities changed
303
304 // Notify dependent properties when period count or values change
305 if (e.PropertyName ==
307 e.PropertyName ==
308 nameof(DoubleTimeSeries.Values))
309 {
312 }
313 }
314
315 /// <summary>
316 /// Validates that a demand quantity is finite and non-negative.
317 /// </summary>
318 /// <param name="quantity">The quantity to validate.</param>
319 /// <param name="parameterName">The parameter name for error messages.</param>
320 /// <exception cref="ArgumentOutOfRangeException">
321 /// If the quantity is NaN, infinite, or negative.
322 /// </exception>
323 private static void ValidateQuantity(
324 double quantity,
325 string parameterName)
326 {
327 // Check if the quantity is finite (not NaN or Infinity)
328 if (double.IsNaN(quantity) ||
329 double.IsInfinity(quantity))
330 {
331 throw new ArgumentOutOfRangeException(
332 parameterName,
333 quantity,
334 "A demand quantity must be a finite number.");
335 }
336
337 // Check if the quantity is non-negative
338 if (quantity < 0.0)
339 {
340 throw new ArgumentOutOfRangeException(
341 parameterName,
342 quantity,
343 "A demand quantity cannot be negative.");
344 }
345 }
346}
Represents a sequence of finite double values indexed by planning period.
double[] Values
Gets or replaces all values of the time series.
int PeriodCount
Gets the number of periods currently represented.
Base class for model objects that notify listeners when one of their properties changes.
virtual void OnPropertyChanged([CallerMemberName] string? propertyName=null)
Raises the PropertyChanged event.
Demand()
Initializes an empty demand.
Definition Demand.cs:33
bool ConcernsItem(int itemId)
Determines whether this demand concerns the specified item.
Definition Demand.cs:242
bool ConcernsDistributionCenter(int distributionCenterId)
Determines whether this demand concerns the specified distribution center.
Definition Demand.cs:251
int ItemId
Gets or sets the identifier of the requested item.
Definition Demand.cs:70
void SetQuantity(int period, double quantity)
Sets the demand quantity for a planning period.
Definition Demand.cs:182
int PlanningHorizon
Gets the number of periods represented by the demand.
Definition Demand.cs:159
Demand(int itemId, int distributionCenterId, int planningHorizon)
Initializes a demand for an item and a distribution center.
Definition Demand.cs:51
int DistributionCenterId
Gets or sets the identifier of the distribution center issuing the demand.
Definition Demand.cs:96
bool HasValidQuantities
Checks whether all currently stored demand quantities are finite and non-negative.
Definition Demand.cs:264
DoubleTimeSeries Quantities
Gets or sets the quantities requested for each planning period.
Definition Demand.cs:124
void ResizeTimeSeries(int periodCount)
Resizes the period-dependent demand data.
Definition Demand.cs:209
bool RefersToSameDemand(Demand? other)
Determines whether this object represents the same item/distribution-center association as another de...
Definition Demand.cs:230
void Fill(double quantity)
Assigns the same demand quantity to every period.
Definition Demand.cs:195
double GetQuantity(int period)
Gets the demand quantity for a planning period.
Definition Demand.cs:168
Defines a model object containing data indexed by planning period.