LotSizingDataModel.Core 2.0.1
Core domain model, shared abstractions and XML-serializable entities.
Loading...
Searching...
No Matches
DoubleTimeSeriesParameter.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/// Base class for decision-model parameters represented by one
11/// double value for each planning period.
12///
13/// This technical base class centralizes:
14/// - XML serialization;
15/// - planning-horizon resizing;
16/// - change notifications;
17/// - value validation.
18/// </summary>
19[Serializable]
20[XmlType(TypeName = "doubleTimeSeriesParameter")]
21public abstract class DoubleTimeSeriesParameter :
24{
25 private DoubleTimeSeries _values = new();
26
27 /// <summary>
28 /// Initializes an empty period-dependent parameter.
29 ///
30 /// Derived concrete classes must expose a public
31 /// parameterless constructor for XmlSerializer.
32 /// </summary>
34 {
35 // Subscribe to changes in the values time series
36 SubscribeToValues(_values);
37 }
38
39 /// <summary>
40 /// Initializes a period-dependent parameter with the specified
41 /// planning horizon.
42 /// </summary>
43 /// <param name="planningHorizon">
44 /// Number of periods in the planning horizon.
45 /// </param>
46 /// <param name="defaultValue">
47 /// Initial value assigned to every period.
48 /// </param>
50 int planningHorizon,
51 double defaultValue = 0.0)
52 : this() // Call default constructor to subscribe to values
53 {
54 // Validate the default value before applying it to all periods
55 ValidateValue(defaultValue, nameof(defaultValue));
56
57 // Resize the time series to the specified planning horizon
58 Values.Resize(
59 planningHorizon,
60 defaultValue);
61 }
62
63 /// <summary>
64 /// Gets or sets the values of the parameter for all
65 /// planning periods.
66 ///
67 /// The value at position t corresponds to the parameter
68 /// value for planning period t.
69 /// </summary>
70 [XmlElement("values")]
72 {
73 get => _values;
74 set
75 {
76 // Ensure the time series is never null
77 DoubleTimeSeries newValue =
78 value ?? new DoubleTimeSeries();
79
80 // Validate all values in the new time series
81 ValidateSeries(newValue);
82
83 // Avoid unnecessary updates if the reference is the same
84 if (ReferenceEquals(_values, newValue))
85 {
86 return;
87 }
88
89 // Unsubscribe from the old time series
90 UnsubscribeFromValues(_values);
91
92 _values = newValue;
93
94 // Subscribe to the new time series
95 SubscribeToValues(_values);
96
97 // Notify dependent properties
100 }
101 }
102
103 /// <summary>
104 /// Gets the number of planning periods currently represented
105 /// by this parameter.
106 ///
107 /// This value is calculated from the time series and is not
108 /// serialized separately.
109 /// </summary>
110 [XmlIgnore]
111 public int PlanningHorizon =>
112 Values.PeriodCount;
113
114 /// <summary>
115 /// Gets or sets the parameter value for a planning period.
116 ///
117 /// Period numbering starts at 1.
118 /// </summary>
119 /// <param name="period">
120 /// Planning period between 1 and <see cref="PlanningHorizon"/>.
121 /// </param>
122 [XmlIgnore]
123 public double this[int period]
124 {
125 get => Values[period];
126 set
127 {
128 // Validate the value before setting
129 ValidateValue(value, nameof(value));
130 Values[period] = value;
131 }
132 }
133
134 /// <summary>
135 /// Gets the parameter value for a planning period.
136 /// </summary>
137 public double GetValue(int period)
138 {
139 return this[period];
140 }
141
142 /// <summary>
143 /// Sets the parameter value for a planning period.
144 /// </summary>
145 public void SetValue(
146 int period,
147 double value)
148 {
149 this[period] = value;
150 }
151
152 /// <summary>
153 /// Assigns the same value to every planning period.
154 /// </summary>
155 public void Fill(double value)
156 {
157 // Validate the value before filling all periods
158 ValidateValue(value, nameof(value));
159 Values.Fill(value);
160 }
161
162 /// <summary>
163 /// Resizes the time series when the planning horizon changes.
164 ///
165 /// Existing values are preserved and newly created periods
166 /// receive <see cref="DefaultValueForNewPeriods"/>.
167 /// </summary>
168 public void ResizeTimeSeries(int periodCount)
169 {
170 // Validate that the period count is non-negative
171 if (periodCount < 0)
172 {
173 throw new ArgumentOutOfRangeException(
174 nameof(periodCount),
175 periodCount,
176 "The planning horizon cannot be negative.");
177 }
178
179 // Resize values with the default value for new periods
180 Values.Resize(
181 periodCount,
183 }
184
185 /// <summary>
186 /// Gets the value assigned to periods created when
187 /// the planning horizon grows.
188 ///
189 /// Derived classes can override this property when another
190 /// default value is more appropriate.
191 /// </summary>
192 [XmlIgnore]
193 protected virtual double DefaultValueForNewPeriods => 0.0;
194
195 /// <summary>
196 /// Validates one value before it is assigned to the parameter.
197 ///
198 /// The default implementation accepts every finite value.
199 /// Derived classes can impose additional constraints,
200 /// such as non-negativity.
201 /// </summary>
202 /// <param name="value">Value to validate.</param>
203 /// <param name="parameterName">
204 /// Name used when an exception is raised.
205 /// </param>
206 protected virtual void ValidateValue(
207 double value,
208 string parameterName)
209 {
210 // Check if the value is finite (not NaN or Infinity)
211 if (double.IsNaN(value) ||
212 double.IsInfinity(value))
213 {
214 throw new ArgumentOutOfRangeException(
215 parameterName,
216 value,
217 "A period-dependent value must be finite.");
218 }
219 }
220
221 /// <summary>
222 /// Validates all values contained in a time series.
223 /// </summary>
224 protected void ValidateSeries(
225 DoubleTimeSeries values)
226 {
227 // Ensure the time series is not null
228 ArgumentNullException.ThrowIfNull(values);
229
230 // Validate each value in the time series
231 foreach (double value in values)
232 {
233 ValidateValue(value, nameof(values));
234 }
235 }
236
237 /// <summary>
238 /// Subscribes to property change notifications from the values time series.
239 /// </summary>
240 private void SubscribeToValues(
241 DoubleTimeSeries values)
242 {
243 // Listen to property changes in the time series
244 values.PropertyChanged +=
245 OnValuesPropertyChanged;
246 }
247
248 /// <summary>
249 /// Unsubscribes from property change notifications from the values time series.
250 /// </summary>
251 private void UnsubscribeFromValues(
252 DoubleTimeSeries values)
253 {
254 // Stop listening to property changes in the time series
255 values.PropertyChanged -=
256 OnValuesPropertyChanged;
257 }
258
259 /// <summary>
260 /// Handles property change notifications from the values time series
261 /// and propagates relevant changes to dependent properties.
262 /// </summary>
263 private void OnValuesPropertyChanged(
264 object? sender,
265 PropertyChangedEventArgs e)
266 {
267 // Always notify that Values changed
268 OnPropertyChanged(nameof(Values));
269
270 // Notify dependent properties when period count or values change
271 if (e.PropertyName ==
273 e.PropertyName ==
274 nameof(DoubleTimeSeries.Values))
275 {
277 }
278 }
279}
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.
void ResizeTimeSeries(int periodCount)
Resizes the time series when the planning horizon changes.
void Fill(double value)
Assigns the same value to every planning period.
DoubleTimeSeriesParameter()
Initializes an empty period-dependent parameter.
double GetValue(int period)
Gets the parameter value for a planning period.
DoubleTimeSeries Values
Gets or sets the values of the parameter for all planning periods.
void SetValue(int period, double value)
Sets the parameter value for a planning period.
int PlanningHorizon
Gets the number of planning periods currently represented by this parameter.
virtual void ValidateValue(double value, string parameterName)
Validates one value before it is assigned to the parameter.
void ValidateSeries(DoubleTimeSeries values)
Validates all values contained in a time series.
virtual double DefaultValueForNewPeriods
Gets the value assigned to periods created when the planning horizon grows.
DoubleTimeSeriesParameter(int planningHorizon, double defaultValue=0.0)
Initializes a period-dependent parameter with the specified planning horizon.
Defines a model object containing data indexed by planning period.