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