1 /* Copyright 2002-2022 CS GROUP
2 * Licensed to CS GROUP (CS) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * CS licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.orekit.utils;
18
19 import java.util.ArrayList;
20 import java.util.Collections;
21 import java.util.Iterator;
22 import java.util.List;
23 import java.util.Map;
24
25 import org.hipparchus.analysis.differentiation.Gradient;
26 import org.hipparchus.util.FastMath;
27 import org.hipparchus.util.Precision;
28 import org.orekit.errors.OrekitException;
29 import org.orekit.errors.OrekitMessages;
30 import org.orekit.time.AbsoluteDate;
31
32
33 /** Class allowing to drive the value of a parameter.
34 * <p>
35 * This class is typically used as a bridge between an estimation
36 * algorithm (typically orbit determination or optimizer) and an
37 * internal parameter in a physical model that needs to be tuned,
38 * or a bridge between a finite differences algorithm and an
39 * internal parameter in a physical model that needs to be slightly
40 * offset. The physical model will expose to the algorithm a
41 * set of instances of this class so the algorithm can call the
42 * {@link #setValue(double)} method to update the
43 * parameter value. Each time the value is set, the physical model
44 * will be notified as it will register a {@link ParameterObserver
45 * ParameterObserver} for this purpose.
46 * </p>
47 * <p>
48 * This design has two major goals. First, it allows an external
49 * algorithm to drive internal parameters almost anonymously, as it only
50 * needs to get a list of instances of this class, without knowing
51 * what they really drive. Second, it allows the physical model to
52 * not expose directly setters methods for its parameters. In order
53 * to be able to modify the parameter value, the algorithm
54 * <em>must</em> retrieve a parameter driver.
55 * </p>
56 * @see ParameterObserver
57 * @author Luc Maisonobe
58 * @since 8.0
59 */
60 public class ParameterDriver {
61
62 /** Name of the parameter. */
63 private String name;
64
65 /** Reference value. */
66 private double referenceValue;
67
68 /** Scaling factor. */
69 private double scale;
70
71 /** Minimum value. */
72 private double minValue;
73
74 /** Maximum value. */
75 private double maxValue;
76
77 /** Reference date.
78 * @since 9.0
79 */
80 private AbsoluteDate referenceDate;
81
82 /** Current value. */
83 private double value;
84
85 /** Selection status.
86 * <p>
87 * Selection is used for estimated parameters in orbit determination,
88 * or to compute the Jacobian matrix in partial derivatives computation.
89 * </p>
90 */
91 private boolean selected;
92
93 /** Observers observing this driver. */
94 private final List<ParameterObserver> observers;
95
96 /** Simple constructor.
97 * <p>
98 * At construction, the parameter is configured as <em>not</em> selected,
99 * the reference date is set to {@code null} and the value is set to the
100 * {@code referenceValue}.
101 * </p>
102 * @param name name of the parameter
103 * @param referenceValue reference value of the parameter
104 * @param scale scaling factor to convert the parameters value to
105 * non-dimensional (typically set to the expected standard deviation of the
106 * parameter), it must be non-zero
107 * @param minValue minimum value
108 * @param maxValue maximum value
109 */
110 public ParameterDriver(final String name, final double referenceValue,
111 final double scale, final double minValue,
112 final double maxValue) {
113 if (FastMath.abs(scale) <= Precision.SAFE_MIN) {
114 throw new OrekitException(OrekitMessages.TOO_SMALL_SCALE_FOR_PARAMETER,
115 name, scale);
116 }
117 this.name = name;
118 this.referenceValue = referenceValue;
119 this.scale = scale;
120 this.minValue = minValue;
121 this.maxValue = maxValue;
122 this.referenceDate = null;
123 this.value = referenceValue;
124 this.selected = false;
125 this.observers = new ArrayList<>();
126 }
127
128
129 /** Add an observer for this driver.
130 * <p>
131 * The observer {@link ParameterObserver#valueChanged(double, ParameterDriver)
132 * valueChanged} method is called once automatically when the
133 * observer is added, and then called at each value change.
134 * </p>
135 * @param observer observer to add
136 * while being updated
137 */
138 public void addObserver(final ParameterObserver observer) {
139 observers.add(observer);
140 observer.valueChanged(getValue(), this);
141 }
142
143 /** Remove an observer.
144 * @param observer observer to remove
145 * @since 9.1
146 */
147 public void removeObserver(final ParameterObserver observer) {
148 for (final Iterator<ParameterObserver> iterator = observers.iterator(); iterator.hasNext();) {
149 if (iterator.next() == observer) {
150 iterator.remove();
151 return;
152 }
153 }
154 }
155
156 /** Replace an observer.
157 * @param oldObserver observer to replace
158 * @param newObserver new observer to use
159 * @since 10.1
160 */
161 public void replaceObserver(final ParameterObserver oldObserver, final ParameterObserver newObserver) {
162 for (int i = 0; i < observers.size(); ++i) {
163 if (observers.get(i) == oldObserver) {
164 observers.set(i, newObserver);
165 }
166 }
167 }
168
169 /** Get the observers for this driver.
170 * @return an unmodifiable view of the observers for this driver
171 * @since 9.1
172 */
173 public List<ParameterObserver> getObservers() {
174 return Collections.unmodifiableList(observers);
175 }
176
177 /** Change the name of this parameter driver.
178 * @param name new name
179 */
180 public void setName(final String name) {
181 final String previousName = this.name;
182 this.name = name;
183 for (final ParameterObserver observer : observers) {
184 observer.nameChanged(previousName, this);
185 }
186 }
187
188 /** Get name.
189 * @return name
190 */
191 public String getName() {
192 return name;
193 }
194
195 /** Get reference parameter value.
196 * @return reference parameter value
197 */
198 public double getReferenceValue() {
199 return referenceValue;
200 }
201
202 /** Set reference parameter value.
203 * @since 9.3
204 * @param referenceValue the reference value to set.
205 */
206 public void setReferenceValue(final double referenceValue) {
207 final double previousReferenceValue = this.referenceValue;
208 this.referenceValue = referenceValue;
209 for (final ParameterObserver observer : observers) {
210 observer.referenceValueChanged(previousReferenceValue, this);
211 }
212 }
213
214 /** Get minimum parameter value.
215 * @return minimum parameter value
216 */
217 public double getMinValue() {
218 return minValue;
219 }
220
221 /** Set minimum parameter value.
222 * @since 9.3
223 * @param minValue the minimum value to set.
224 */
225 public void setMinValue(final double minValue) {
226 final double previousMinValue = this.minValue;
227 this.minValue = minValue;
228 for (final ParameterObserver observer : observers) {
229 observer.minValueChanged(previousMinValue, this);
230 }
231 // Check if current value is not out of min/max range
232 setValue(value);
233 }
234
235 /** Get maximum parameter value.
236 * @return maximum parameter value
237 */
238 public double getMaxValue() {
239 return maxValue;
240 }
241
242 /** Set maximum parameter value.
243 * @since 9.3
244 * @param maxValue the maximum value to set.
245 */
246 public void setMaxValue(final double maxValue) {
247 final double previousMaxValue = this.maxValue;
248 this.maxValue = maxValue;
249 for (final ParameterObserver observer : observers) {
250 observer.maxValueChanged(previousMaxValue, this);
251 }
252 // Check if current value is not out of min/max range
253 setValue(value);
254 }
255
256 /** Get scale.
257 * @return scale
258 */
259 public double getScale() {
260 return scale;
261 }
262
263 /** Set scale.
264 * @since 9.3
265 * @param scale the scale to set.
266 */
267 public void setScale(final double scale) {
268 final double previousScale = this.scale;
269 this.scale = scale;
270 for (final ParameterObserver observer : observers) {
271 observer.scaleChanged(previousScale, this);
272 }
273 }
274
275 /** Get normalized value.
276 * <p>
277 * The normalized value is a non-dimensional value
278 * suitable for use as part of a vector in an optimization
279 * process. It is computed as {@code (current - reference)/scale}.
280 * </p>
281 * @return normalized value
282 */
283 public double getNormalizedValue() {
284 return (value - referenceValue) / scale;
285 }
286
287 /** Set normalized value.
288 * <p>
289 * The normalized value is a non-dimensional value
290 * suitable for use as part of a vector in an optimization
291 * process. It is computed as {@code (current - reference)/scale}.
292 * </p>
293 * @param normalized value
294 */
295 public void setNormalizedValue(final double normalized) {
296 setValue(referenceValue + scale * normalized);
297 }
298
299 /** Get current reference date.
300 * @return current reference date (null if it was never set)
301 * @since 9.0
302 */
303 public AbsoluteDate getReferenceDate() {
304 return referenceDate;
305 }
306
307 /** Set reference date.
308 * @param newReferenceDate new reference date
309 * @since 9.0
310 */
311 public void setReferenceDate(final AbsoluteDate newReferenceDate) {
312 final AbsoluteDate previousReferenceDate = getReferenceDate();
313 referenceDate = newReferenceDate;
314 for (final ParameterObserver observer : observers) {
315 observer.referenceDateChanged(previousReferenceDate, this);
316 }
317 }
318
319 /** Get current parameter value.
320 * @return current parameter value
321 */
322 public double getValue() {
323 return value;
324 }
325
326 /** Get the value as a gradient.
327 * @param freeParameters total number of free parameters in the gradient
328 * @param indices indices of the differentiation parameters in derivatives computations
329 * @return value with derivatives
330 * @since 10.2
331 */
332 public Gradient getValue(final int freeParameters, final Map<String, Integer> indices) {
333 final Integer index = indices.get(name);
334 return (index == null) ? Gradient.constant(freeParameters, value) : Gradient.variable(freeParameters, index, value);
335 }
336
337 /** Set parameter value.
338 * <p>
339 * If {@code newValue} is below {@link #getMinValue()}, it will
340 * be silently set to {@link #getMinValue()}. If {@code newValue} is
341 * above {@link #getMaxValue()}, it will be silently set to {@link
342 * #getMaxValue()}.
343 * </p>
344 * @param newValue new value
345 */
346 public void setValue(final double newValue) {
347 final double previousValue = getValue();
348 value = FastMath.max(minValue, FastMath.min(maxValue, newValue));
349 for (final ParameterObserver observer : observers) {
350 observer.valueChanged(previousValue, this);
351 }
352 }
353
354 /** Configure a parameter selection status.
355 * <p>
356 * Selection is used for estimated parameters in orbit determination,
357 * or to compute the Jacobian matrix in partial derivatives computation.
358 * </p>
359 * @param selected if true the parameter is selected,
360 * otherwise it will be fixed
361 */
362 public void setSelected(final boolean selected) {
363 final boolean previousSelection = isSelected();
364 this.selected = selected;
365 for (final ParameterObserver observer : observers) {
366 observer.selectionChanged(previousSelection, this);
367 }
368 }
369
370 /** Check if parameter is selected.
371 * <p>
372 * Selection is used for estimated parameters in orbit determination,
373 * or to compute the Jacobian matrix in partial derivatives computation.
374 * </p>
375 * @return true if parameter is selected, false if it is not
376 */
377 public boolean isSelected() {
378 return selected;
379 }
380
381 /** Get a text representation of the parameter.
382 * @return text representation of the parameter, in the form name = value.
383 */
384 public String toString() {
385 return name + " = " + value;
386 }
387
388 }