1 /* Copyright 2002-2025 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.propagation;
18
19 import org.hipparchus.analysis.interpolation.HermiteInterpolator;
20 import org.hipparchus.linear.BlockRealMatrix;
21 import org.hipparchus.linear.MatrixUtils;
22 import org.hipparchus.linear.RealMatrix;
23 import org.orekit.errors.OrekitInternalError;
24 import org.orekit.frames.Frame;
25 import org.orekit.frames.LOFType;
26 import org.orekit.orbits.Orbit;
27 import org.orekit.orbits.OrbitType;
28 import org.orekit.orbits.PositionAngleType;
29 import org.orekit.time.AbsoluteDate;
30 import org.orekit.time.TimeInterpolator;
31 import org.orekit.time.TimeStampedPair;
32 import org.orekit.utils.CartesianDerivativesFilter;
33
34 import java.util.ArrayList;
35 import java.util.List;
36
37 /**
38 * State covariance Keplerian quintic interpolator.
39 * <p>
40 * Its purpose is to interpolate state covariance between tabulated state covariances using polynomial interpolation. To do
41 * so, it uses a {@link HermiteInterpolator} and compute the first and second order derivatives at tabulated states assuming
42 * a standard Keplerian motion depending on given derivatives filter.
43 * <p>
44 * It gives very accurate results as explained <a
45 * href="https://orekit.org/doc/technical-notes/Implementation_of_covariance_interpolation_in_Orekit.pdf">here</a>. In the
46 * very poorly tracked test case evolving in a highly dynamical environment mentioned in the linked thread, the user can
47 * expect at worst errors of less than 0.2% in position sigmas and less than 0.35% in velocity sigmas with steps of 40mn
48 * between tabulated values.
49 * <p>
50 * However, note that this method does not guarantee the positive definiteness of the computed state covariance as opposed to
51 * {@link StateCovarianceBlender}.
52 *
53 * @author Vincent Cucchietti
54 * @see HermiteInterpolator
55 * @see StateCovarianceBlender
56 */
57 public class StateCovarianceKeplerianHermiteInterpolator extends AbstractStateCovarianceInterpolator {
58
59 /**
60 * Filter defining if only the state covariance value are used or if first or/and second Keplerian derivatives should be
61 * used.
62 */
63 private final CartesianDerivativesFilter filter;
64
65 /**
66 * Constructor using an output local orbital frame and :
67 * <ul>
68 * <li>Default number of interpolation points of {@code DEFAULT_INTERPOLATION_POINTS}</li>
69 * <li>Default extrapolation threshold value ({@code DEFAULT_EXTRAPOLATION_THRESHOLD_SEC} s)</li>
70 * <li>Use of position and two time derivatives during interpolation</li>
71 * </ul>
72 * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation
73 * points (about 10-20 points) in order to avoid <a href="http://en.wikipedia.org/wiki/Runge%27s_phenomenon">Runge's
74 * phenomenon</a> and numerical problems (including NaN appearing).
75 * <p>
76 * <b>BEWARE:</b> If the output local orbital frame is not considered pseudo-inertial, all the covariance components
77 * related to the velocity will be poorly interpolated. <b>Only the position covariance should be considered in this
78 * case.</b>
79 *
80 * @param orbitInterpolator orbit interpolator
81 * @param outLOF output local orbital frame
82 */
83 public StateCovarianceKeplerianHermiteInterpolator(final TimeInterpolator<Orbit> orbitInterpolator,
84 final LOFType outLOF) {
85 this(DEFAULT_INTERPOLATION_POINTS, orbitInterpolator, outLOF);
86 }
87
88 /**
89 * Constructor using an output local orbital frame and :
90 * <ul>
91 * <li>Default extrapolation threshold value ({@code DEFAULT_EXTRAPOLATION_THRESHOLD_SEC} s)</li>
92 * <li>Use of position and two time derivatives during interpolation</li>
93 * </ul>
94 * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation
95 * points (about 10-20 points) in order to avoid <a href="http://en.wikipedia.org/wiki/Runge%27s_phenomenon">Runge's
96 * phenomenon</a> and numerical problems (including NaN appearing).
97 * <p>
98 * <b>BEWARE:</b> If the output local orbital frame is not considered pseudo-inertial, all the covariance components
99 * related to the velocity will be poorly interpolated. <b>Only the position covariance should be considered in this
100 * case.</b>
101 *
102 * @param interpolationPoints number of interpolation points
103 * @param orbitInterpolator orbit interpolator
104 * @param outLOF output local orbital frame
105 */
106 public StateCovarianceKeplerianHermiteInterpolator(final int interpolationPoints,
107 final TimeInterpolator<Orbit> orbitInterpolator,
108 final LOFType outLOF) {
109 this(interpolationPoints, orbitInterpolator, CartesianDerivativesFilter.USE_PVA,
110 outLOF);
111 }
112
113 /**
114 * Constructor using an output local orbital frame and :
115 * <ul>
116 * <li>Use of position and two time derivatives during interpolation</li>
117 * </ul>
118 * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation
119 * points (about 10-20 points) in order to avoid <a href="http://en.wikipedia.org/wiki/Runge%27s_phenomenon">Runge's
120 * phenomenon</a> and numerical problems (including NaN appearing).
121 * <p>
122 * <b>BEWARE:</b> If the output local orbital frame is not considered pseudo-inertial, all the covariance components
123 * related to the velocity will be poorly interpolated. <b>Only the position covariance should be considered in this
124 * case.</b>
125 *
126 * @param interpolationPoints number of interpolation points
127 * @param orbitInterpolator orbit interpolator
128 * @param outLOF output local orbital frame
129 * @param filter filter for derivatives from the sample to use in position-velocity-acceleration interpolation
130 */
131 public StateCovarianceKeplerianHermiteInterpolator(final int interpolationPoints,
132 final TimeInterpolator<Orbit> orbitInterpolator,
133 final CartesianDerivativesFilter filter,
134 final LOFType outLOF) {
135 this(interpolationPoints, DEFAULT_EXTRAPOLATION_THRESHOLD_SEC, orbitInterpolator, filter, outLOF);
136 }
137
138 /**
139 * Constructor using an output local orbital frame.
140 * <p>
141 * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation
142 * points (about 10-20 points) in order to avoid <a href="http://en.wikipedia.org/wiki/Runge%27s_phenomenon">Runge's
143 * phenomenon</a> and numerical problems (including NaN appearing).
144 * <p>
145 * <b>BEWARE:</b> If the output local orbital frame is not considered pseudo-inertial, all the covariance components
146 * related to the velocity will be poorly interpolated. <b>Only the position covariance should be considered in this
147 * case.</b>
148 *
149 * @param interpolationPoints number of interpolation points
150 * @param extrapolationThreshold extrapolation threshold beyond which the propagation will fail
151 * @param orbitInterpolator orbit interpolator
152 * @param outLOF output local orbital frame
153 * @param filter filter defining if only the state covariance value are used or if first or/and second Keplerian
154 * derivatives should be used during the interpolation.
155 */
156 public StateCovarianceKeplerianHermiteInterpolator(final int interpolationPoints, final double extrapolationThreshold,
157 final TimeInterpolator<Orbit> orbitInterpolator,
158 final CartesianDerivativesFilter filter, final LOFType outLOF) {
159 super(interpolationPoints, extrapolationThreshold, orbitInterpolator, outLOF);
160 this.filter = filter;
161 }
162
163 /**
164 * Constructor using an output frame and :
165 * <ul>
166 * <li>Default number of interpolation points of {@code DEFAULT_INTERPOLATION_POINTS}</li>
167 * <li>Default extrapolation threshold value ({@code DEFAULT_EXTRAPOLATION_THRESHOLD_SEC} s)</li>
168 * <li>Use of position and two time derivatives during interpolation</li>
169 * </ul>
170 * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation
171 * points (about 10-20 points) in order to avoid <a href="http://en.wikipedia.org/wiki/Runge%27s_phenomenon">Runge's
172 * phenomenon</a> and numerical problems (including NaN appearing).
173 *
174 * @param orbitInterpolator orbit interpolator
175 * @param outFrame output frame
176 * @param outOrbitType output orbit type
177 * @param outPositionAngleType output position angle
178 */
179 public StateCovarianceKeplerianHermiteInterpolator(final TimeInterpolator<Orbit> orbitInterpolator, final Frame outFrame,
180 final OrbitType outOrbitType, final PositionAngleType outPositionAngleType) {
181 this(DEFAULT_INTERPOLATION_POINTS, orbitInterpolator, outFrame, outOrbitType, outPositionAngleType);
182 }
183
184 /**
185 * Constructor using an output frame and :
186 * <ul>
187 * <li>Default number of interpolation points of {@code DEFAULT_INTERPOLATION_POINTS}</li>
188 * <li>Use of position and two time derivatives during interpolation</li>
189 * </ul>
190 * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation
191 * points (about 10-20 points) in order to avoid <a href="http://en.wikipedia.org/wiki/Runge%27s_phenomenon">Runge's
192 * phenomenon</a> and numerical problems (including NaN appearing).
193 *
194 * @param interpolationPoints number of interpolation points
195 * @param orbitInterpolator orbit interpolator
196 * @param outFrame output frame
197 * @param outOrbitType output orbit type
198 * @param outPositionAngleType output position angle
199 */
200 public StateCovarianceKeplerianHermiteInterpolator(final int interpolationPoints,
201 final TimeInterpolator<Orbit> orbitInterpolator, final Frame outFrame,
202 final OrbitType outOrbitType, final PositionAngleType outPositionAngleType) {
203 this(interpolationPoints, DEFAULT_EXTRAPOLATION_THRESHOLD_SEC, orbitInterpolator, CartesianDerivativesFilter.USE_PVA,
204 outFrame, outOrbitType, outPositionAngleType);
205 }
206
207 /**
208 * Constructor using an output frame and :
209 * <ul>
210 * <li>Default extrapolation threshold value ({@code DEFAULT_EXTRAPOLATION_THRESHOLD_SEC} s)</li>
211 * </ul>
212 * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation
213 * points (about 10-20 points) in order to avoid <a href="http://en.wikipedia.org/wiki/Runge%27s_phenomenon">Runge's
214 * phenomenon</a> and numerical problems (including NaN appearing).
215 *
216 * @param interpolationPoints number of interpolation points
217 * @param orbitInterpolator orbit interpolator
218 * @param filter filter defining if only the state covariance value are used or if first or/and second Keplerian
219 * derivatives should be used during the interpolation.
220 * @param outFrame output frame
221 * @param outOrbitType output orbit type
222 * @param outPositionAngleType output position angle
223 */
224 public StateCovarianceKeplerianHermiteInterpolator(final int interpolationPoints,
225 final TimeInterpolator<Orbit> orbitInterpolator,
226 final CartesianDerivativesFilter filter, final Frame outFrame,
227 final OrbitType outOrbitType, final PositionAngleType outPositionAngleType) {
228 this(interpolationPoints, DEFAULT_EXTRAPOLATION_THRESHOLD_SEC, orbitInterpolator, filter, outFrame, outOrbitType,
229 outPositionAngleType);
230 }
231
232 /**
233 * Constructor using an output frame.
234 * <p>
235 * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation
236 * points (about 10-20 points) in order to avoid <a href="http://en.wikipedia.org/wiki/Runge%27s_phenomenon">Runge's
237 * phenomenon</a> and numerical problems (including NaN appearing).
238 *
239 * @param interpolationPoints number of interpolation points
240 * @param extrapolationThreshold extrapolation threshold beyond which the propagation will fail
241 * @param orbitInterpolator orbit interpolator
242 * @param filter filter defining if only the state covariance value are used or if first or/and second Keplerian
243 * derivatives should be used during the interpolation.
244 * @param outFrame output frame
245 * @param outOrbitType output orbit type
246 * @param outPositionAngleType output position angle
247 */
248 public StateCovarianceKeplerianHermiteInterpolator(final int interpolationPoints, final double extrapolationThreshold,
249 final TimeInterpolator<Orbit> orbitInterpolator,
250 final CartesianDerivativesFilter filter, final Frame outFrame,
251 final OrbitType outOrbitType, final PositionAngleType outPositionAngleType) {
252 super(interpolationPoints, extrapolationThreshold, orbitInterpolator, outFrame, outOrbitType, outPositionAngleType);
253 this.filter = filter;
254 }
255
256 /** Get Filter defining if only the state covariance value are used or if first or/and second Keplerian derivatives
257 * should be used.
258 * @return Filter defining if only the state covariance value are used or if first or/and second Keplerian derivatives
259 * should be used.
260 */
261 public CartesianDerivativesFilter getFilter() {
262 return filter;
263 }
264
265 /** {@inheritDoc} */
266 @Override
267 protected StateCovariance computeInterpolatedCovarianceInOrbitFrame(
268 final List<TimeStampedPair<Orbit, StateCovariance>> uncertainStates,
269 final Orbit interpolatedOrbit) {
270
271 // Compute and combine covariances value and time derivatives
272 final List<double[][][]> covarianceValueAndDerivativesList = new ArrayList<>();
273 for (TimeStampedPair<Orbit, StateCovariance> uncertainState : uncertainStates) {
274 final double[][][] currentCovarianceValueAndDerivatives =
275 computeAndCombineCovarianceValueAndDerivatives(uncertainState, interpolatedOrbit);
276
277 covarianceValueAndDerivativesList.add(currentCovarianceValueAndDerivatives);
278 }
279
280 // Interpolate covariance matrix in equinoctial elements
281 final RealMatrix interpolatedCovarianceMatrixInEqui =
282 computeInterpolatedStateCovariance(interpolatedOrbit.getDate(),
283 uncertainStates,
284 covarianceValueAndDerivativesList);
285
286 return new StateCovariance(interpolatedCovarianceMatrixInEqui,
287 interpolatedOrbit.getDate(), interpolatedOrbit.getFrame(),
288 OrbitType.EQUINOCTIAL, DEFAULT_POSITION_ANGLE);
289 }
290
291 /**
292 * Compute and combine state covariance matrix and its two Keplerian time derivatives.
293 *
294 * @param uncertainState orbit and its associated covariance
295 * @param interpolatedOrbit interpolated orbit
296 *
297 * @return state covariance matrix and its two time derivatives
298 */
299 private double[][][] computeAndCombineCovarianceValueAndDerivatives(
300 final TimeStampedPair<Orbit, StateCovariance> uncertainState, final Orbit interpolatedOrbit) {
301
302 // Get orbit and associated covariance
303 final Orbit orbit = uncertainState.getFirst();
304 final StateCovariance covariance = uncertainState.getSecond();
305
306 // Express covariance in interpolated orbit frame for consistency among the sample
307 final StateCovariance covarianceInOrbitFrame = covariance.changeCovarianceFrame(orbit, interpolatedOrbit.getFrame());
308
309 // Convert to equinoctial elements to avoid singularities
310 final StateCovariance covarianceInOrbitFrameInEqui =
311 covarianceInOrbitFrame.changeCovarianceType(orbit, OrbitType.EQUINOCTIAL, DEFAULT_POSITION_ANGLE);
312
313 // Get matrix
314 final RealMatrix covarianceInOrbitFrameInEquiMatrix = covarianceInOrbitFrameInEqui.getMatrix();
315
316 // Compute covariance first and second time derivative according to instance filter
317 final int dim = StateCovariance.STATE_DIMENSION;
318
319 final RealMatrix covarianceMatrixFirstDerInKep;
320 final RealMatrix covarianceMatrixSecondDerInKep;
321
322 switch (filter) {
323 case USE_P:
324 covarianceMatrixFirstDerInKep = MatrixUtils.createRealMatrix(dim, dim);
325 covarianceMatrixSecondDerInKep = MatrixUtils.createRealMatrix(dim, dim);
326 break;
327 case USE_PV:
328 covarianceMatrixFirstDerInKep = computeCovarianceFirstDerivative(orbit, covarianceInOrbitFrameInEquiMatrix);
329 covarianceMatrixSecondDerInKep = MatrixUtils.createRealMatrix(dim, dim);
330 break;
331 case USE_PVA:
332 covarianceMatrixFirstDerInKep = computeCovarianceFirstDerivative(orbit, covarianceInOrbitFrameInEquiMatrix);
333 covarianceMatrixSecondDerInKep =
334 computeCovarianceSecondDerivative(orbit, covarianceInOrbitFrameInEquiMatrix);
335 break;
336 default:
337 // Should never happen
338 throw new OrekitInternalError(null);
339 }
340
341 // Combine and output the state covariance and its first two time derivative in a single array
342 return combineCovarianceValueAndDerivatives(covarianceInOrbitFrameInEquiMatrix,
343 covarianceMatrixFirstDerInKep,
344 covarianceMatrixSecondDerInKep);
345 }
346
347 /**
348 * Compute interpolated state covariance in equinoctial elements using a Hermite interpolator.
349 *
350 * @param interpolationDate interpolation date
351 * @param uncertainStates list of orbits and their associated covariances
352 * @param covarianceValueAndDerivativesList list of covariances and their associated first and second time derivatives
353 *
354 * @return interpolated state covariance in equinoctial elements
355 *
356 * @see HermiteInterpolator
357 */
358 private RealMatrix computeInterpolatedStateCovariance(final AbsoluteDate interpolationDate,
359 final List<TimeStampedPair<Orbit, StateCovariance>> uncertainStates,
360 final List<double[][][]> covarianceValueAndDerivativesList) {
361
362 final RealMatrix interpolatedCovarianceMatrix = new BlockRealMatrix(new double[ROW_DIM][COLUMN_DIM]);
363
364 // Interpolate each element in the covariance matrix
365 for (int i = 0; i < ROW_DIM; i++) {
366 for (int j = 0; j < COLUMN_DIM; j++) {
367
368 // Create an interpolator for each element
369 final HermiteInterpolator tempInterpolator = new HermiteInterpolator();
370
371 // Fill interpolator with all samples value and associated derivatives
372 for (int k = 0; k < uncertainStates.size(); k++) {
373 final TimeStampedPair<Orbit, StateCovariance> currentUncertainStates = uncertainStates.get(k);
374
375 final double[][][] currentCovarianceValueAndDerivatives = covarianceValueAndDerivativesList.get(k);
376
377 final double deltaT = currentUncertainStates.getDate().durationFrom(interpolationDate);
378
379 addSampleToInterpolator(tempInterpolator, deltaT, currentCovarianceValueAndDerivatives[i][j]);
380 }
381
382 // Interpolate
383 interpolatedCovarianceMatrix.setEntry(i, j, tempInterpolator.value(0)[0]);
384 }
385 }
386
387 return interpolatedCovarianceMatrix;
388 }
389
390 /**
391 * Add sample to given interpolator.
392 *
393 * @param interpolator interpolator to add sample to
394 * @param deltaT abscissa for interpolation
395 * @param valueAndDerivatives value and associated derivatives to add
396 */
397 private void addSampleToInterpolator(final HermiteInterpolator interpolator, final double deltaT,
398 final double[] valueAndDerivatives) {
399 switch (filter) {
400 case USE_P:
401 interpolator.addSamplePoint(deltaT, new double[] { valueAndDerivatives[0] });
402 break;
403 case USE_PV:
404 interpolator.addSamplePoint(deltaT,
405 new double[] { valueAndDerivatives[0] },
406 new double[] { valueAndDerivatives[1] });
407 break;
408 case USE_PVA:
409 interpolator.addSamplePoint(deltaT,
410 new double[] { valueAndDerivatives[0] },
411 new double[] { valueAndDerivatives[1] },
412 new double[] { valueAndDerivatives[2] });
413 break;
414 default:
415 // Should never happen
416 throw new OrekitInternalError(null);
417 }
418 }
419
420 /**
421 * Compute state covariance first Keplerian time derivative.
422 *
423 * @param orbit orbit
424 * @param covarianceMatrixInEqui state covariance matrix in equinoctial elements
425 *
426 * @return state covariance first time derivative
427 */
428 private RealMatrix computeCovarianceFirstDerivative(final Orbit orbit,
429 final RealMatrix covarianceMatrixInEqui) {
430
431 final RealMatrix covarianceFirstDerivative = new BlockRealMatrix(ROW_DIM, COLUMN_DIM);
432
433 // Compute common term used in papers
434 final double m = orbit.getMeanAnomalyDotWrtA();
435
436 // Compute first time derivative of each element in the covariance matrix
437 for (int i = 0; i < ROW_DIM; i++) {
438 for (int j = 0; j < COLUMN_DIM; j++) {
439 if (i != 5 && j != 5) {
440 covarianceFirstDerivative.setEntry(i, j, 0);
441 }
442 else if (i == 5 && j != 5) {
443
444 final double value = covarianceMatrixInEqui.getEntry(0, j) * m;
445
446 covarianceFirstDerivative.setEntry(i, j, value);
447 covarianceFirstDerivative.setEntry(j, i, value);
448 }
449 else {
450 final double value = 2 * covarianceMatrixInEqui.getEntry(0, 5) * m;
451
452 covarianceFirstDerivative.setEntry(i, j, value);
453 }
454 }
455 }
456
457 return covarianceFirstDerivative;
458
459 }
460
461 /**
462 * Compute state covariance second Keplerian time derivative.
463 *
464 * @param orbit orbit
465 * @param covarianceMatrixInEqui state covariance matrix in equinoctial elements
466 *
467 * @return state covariance second time derivative
468 */
469 private RealMatrix computeCovarianceSecondDerivative(final Orbit orbit,
470 final RealMatrix covarianceMatrixInEqui) {
471
472 final RealMatrix covarianceSecondDerivative = new BlockRealMatrix(ROW_DIM, COLUMN_DIM);
473
474 // Compute common term used in papers
475 final double m = orbit.getMeanAnomalyDotWrtA();
476
477 // Compute second time derivative of each element in the covariance matrix
478 for (int i = 0; i < ROW_DIM; i++) {
479 for (int j = 0; j < COLUMN_DIM; j++) {
480 if (i == 5 && j == 5) {
481
482 final double value = 2 * covarianceMatrixInEqui.getEntry(0, 0) * m * m;
483
484 covarianceSecondDerivative.setEntry(i, j, value);
485 }
486 else {
487 covarianceSecondDerivative.setEntry(i, j, 0);
488 }
489 }
490 }
491
492 return covarianceSecondDerivative;
493
494 }
495
496 /**
497 * Combine state covariance matrix and its two Keplerian time derivatives.
498 *
499 * @param covarianceMatrixInEqui covariance matrix in equinoctial elements
500 * @param covarianceMatrixFirstDerInEqui covariance matrix first time derivative in equinoctial elements
501 * @param covarianceMatrixSecondDerInEqui covariance matrix second time derivative in equinoctial elements
502 *
503 * @return state covariance matrix and its two time derivatives
504 */
505 private double[][][] combineCovarianceValueAndDerivatives(final RealMatrix covarianceMatrixInEqui,
506 final RealMatrix covarianceMatrixFirstDerInEqui,
507 final RealMatrix covarianceMatrixSecondDerInEqui) {
508
509 final double[][][] covarianceValueAndDerivativesArray = new double[ROW_DIM][COLUMN_DIM][3];
510
511 // Combine covariance and its first two time derivatives in a single 3D array
512 for (int i = 0; i < ROW_DIM; i++) {
513 for (int j = 0; j < COLUMN_DIM; j++) {
514 covarianceValueAndDerivativesArray[i][j][0] = covarianceMatrixInEqui.getEntry(i, j);
515 covarianceValueAndDerivativesArray[i][j][1] = covarianceMatrixFirstDerInEqui.getEntry(i, j);
516 covarianceValueAndDerivativesArray[i][j][2] = covarianceMatrixSecondDerInEqui.getEntry(i, j);
517 }
518 }
519
520 return covarianceValueAndDerivativesArray;
521 }
522 }