1   /* Copyright 2002-2013 CS Systèmes d'Information
2    * Licensed to CS Systèmes d'Information (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.orbits;
18  
19  import java.io.Serializable;
20  
21  import org.apache.commons.math3.geometry.euclidean.threed.Vector3D;
22  import org.apache.commons.math3.linear.DecompositionSolver;
23  import org.apache.commons.math3.linear.MatrixUtils;
24  import org.apache.commons.math3.linear.QRDecomposition;
25  import org.apache.commons.math3.linear.RealMatrix;
26  import org.apache.commons.math3.util.FastMath;
27  import org.orekit.errors.OrekitException;
28  import org.orekit.errors.OrekitMessages;
29  import org.orekit.frames.Frame;
30  import org.orekit.frames.Transform;
31  import org.orekit.time.AbsoluteDate;
32  import org.orekit.time.TimeInterpolable;
33  import org.orekit.time.TimeShiftable;
34  import org.orekit.time.TimeStamped;
35  import org.orekit.utils.PVCoordinates;
36  import org.orekit.utils.PVCoordinatesProvider;
37  
38  /**
39   * This class handles orbital parameters.
40  
41   * <p>
42   * For user convenience, both the Cartesian and the equinoctial elements
43   * are provided by this class, regardless of the canonical representation
44   * implemented in the derived class (which may be classical keplerian
45   * elements for example).
46   * </p>
47   * <p>
48   * The parameters are defined in a frame specified by the user. It is important
49   * to make sure this frame is consistent: it probably is inertial and centered
50   * on the central body. This information is used for example by some
51   * force models.
52   * </p>
53   * <p>
54   * The object <code>OrbitalParameters</code> is guaranteed to be immutable.
55   * </p>
56   * @author Luc Maisonobe
57   * @author Guylaine Prat
58   * @author Fabien Maussion
59   * @author V&eacute;ronique Pommier-Maurussane
60   */
61  public abstract class Orbit
62      implements TimeStamped, TimeShiftable<Orbit>, TimeInterpolable<Orbit>,
63                 Serializable, PVCoordinatesProvider {
64  
65      /** Serializable UID. */
66      private static final long serialVersionUID = 438733454597999578L;
67  
68      /** Frame in which are defined the orbital parameters. */
69      private final Frame frame;
70  
71      /** Date of the orbital parameters. */
72      private final AbsoluteDate date;
73  
74      /** Value of mu used to compute position and velocity (m<sup>3</sup>/s<sup>2</sup>). */
75      private final double mu;
76  
77      /** Computed PVCoordinates. */
78      private transient PVCoordinates pvCoordinates;
79  
80      /** Jacobian of the orbital parameters with mean angle with respect to the Cartesian coordinates. */
81      private transient double[][] jacobianMeanWrtCartesian;
82  
83      /** Jacobian of the Cartesian coordinates with respect to the orbital parameters with mean angle. */
84      private transient double[][] jacobianWrtParametersMean;
85  
86      /** Jacobian of the orbital parameters with eccentric angle with respect to the Cartesian coordinates. */
87      private transient double[][] jacobianEccentricWrtCartesian;
88  
89      /** Jacobian of the Cartesian coordinates with respect to the orbital parameters with eccentric angle. */
90      private transient double[][] jacobianWrtParametersEccentric;
91  
92      /** Jacobian of the orbital parameters with true angle with respect to the Cartesian coordinates. */
93      private transient double[][] jacobianTrueWrtCartesian;
94  
95      /** Jacobian of the Cartesian coordinates with respect to the orbital parameters with true angle. */
96      private transient double[][] jacobianWrtParametersTrue;
97  
98      /** Default constructor.
99       * Build a new instance with arbitrary default elements.
100      * @param frame the frame in which the parameters are defined
101      * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
102      * @param date date of the orbital parameters
103      * @param mu central attraction coefficient (m^3/s^2)
104      * @exception IllegalArgumentException if frame is not a {@link
105      * Frame#isPseudoInertial pseudo-inertial frame}
106      */
107     protected Orbit(final Frame frame, final AbsoluteDate date, final double mu)
108         throws IllegalArgumentException {
109         ensurePseudoInertialFrame(frame);
110         this.date                      = date;
111         this.mu                        = mu;
112         this.pvCoordinates             = null;
113         this.frame                     = frame;
114         jacobianMeanWrtCartesian       = null;
115         jacobianWrtParametersMean      = null;
116         jacobianEccentricWrtCartesian  = null;
117         jacobianWrtParametersEccentric = null;
118         jacobianTrueWrtCartesian       = null;
119         jacobianWrtParametersTrue      = null;
120     }
121 
122     /** Set the orbit from Cartesian parameters.
123      * @param pvCoordinates the position and velocity in the inertial frame
124      * @param frame the frame in which the {@link PVCoordinates} are defined
125      * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
126      * @param date date of the orbital parameters
127      * @param mu central attraction coefficient (m^3/s^2)
128      * @exception IllegalArgumentException if frame is not a {@link
129      * Frame#isPseudoInertial pseudo-inertial frame}
130      */
131     protected Orbit(final PVCoordinates pvCoordinates, final Frame frame,
132                     final AbsoluteDate date, final double mu)
133         throws IllegalArgumentException {
134         ensurePseudoInertialFrame(frame);
135         this.date = date;
136         this.mu = mu;
137         this.pvCoordinates = pvCoordinates;
138         this.frame = frame;
139     }
140 
141     /** Get the orbit type.
142      * @return orbit type
143      */
144     public abstract OrbitType getType();
145 
146     /** Ensure the defining frame is a pseudo-inertial frame.
147      * @param frame frame to check
148      * @exception IllegalArgumentException if frame is not a {@link
149      * Frame#isPseudoInertial pseudo-inertial frame}
150      */
151     private static void ensurePseudoInertialFrame(final Frame frame)
152         throws IllegalArgumentException {
153         if (!frame.isPseudoInertial()) {
154             throw OrekitException.createIllegalArgumentException(
155                 OrekitMessages.NON_PSEUDO_INERTIAL_FRAME_NOT_SUITABLE_FOR_DEFINING_ORBITS,
156                 frame.getName());
157         }
158     }
159 
160     /** Get the frame in which the orbital parameters are defined.
161      * @return frame in which the orbital parameters are defined
162      */
163     public Frame getFrame() {
164         return frame;
165     }
166 
167     /** Get the semi-major axis.
168      * <p>Note that the semi-major axis is considered negative for hyperbolic orbits.</p>
169      * @return semi-major axis (m)
170      */
171     public abstract double getA();
172 
173     /** Get the first component of the equinoctial eccentricity vector.
174      * @return first component of the equinoctial eccentricity vector
175      */
176     public abstract double getEquinoctialEx();
177 
178     /** Get the second component of the equinoctial eccentricity vector.
179      * @return second component of the equinoctial eccentricity vector
180      */
181     public abstract double getEquinoctialEy();
182 
183     /** Get the first component of the inclination vector.
184      * @return first component of the inclination vector
185      */
186     public abstract double getHx();
187 
188     /** Get the second component of the inclination vector.
189      * @return second component of the inclination vector
190      */
191     public abstract double getHy();
192 
193     /** Get the eccentric longitude argument.
194      * @return E + &omega; + &Omega; eccentric longitude argument (rad)
195      */
196     public abstract double getLE();
197 
198     /** Get the true longitude argument.
199      * @return v + &omega; + &Omega; true longitude argument (rad)
200      */
201     public abstract double getLv();
202 
203     /** Get the mean longitude argument.
204      * @return M + &omega; + &Omega; mean longitude argument (rad)
205      */
206     public abstract double getLM();
207 
208     // Additional orbital elements
209 
210     /** Get the eccentricity.
211      * @return eccentricity
212      */
213     public abstract double getE();
214 
215     /** Get the inclination.
216      * @return inclination (rad)
217      */
218     public abstract double getI();
219 
220     /** Get the central acceleration constant.
221      * @return central acceleration constant
222      */
223     public double getMu() {
224         return mu;
225     }
226 
227     /** Get the keplerian period.
228      * <p>The keplerian period is computed directly from semi major axis
229      * and central acceleration constant.</p>
230      * @return keplerian period in seconds, or positive infinity for hyperbolic orbits
231      */
232     public double getKeplerianPeriod() {
233         final double a = getA();
234         return (a < 0) ? Double.POSITIVE_INFINITY : 2.0 * FastMath.PI * a * FastMath.sqrt(a / mu);
235     }
236 
237     /** Get the keplerian mean motion.
238      * <p>The keplerian mean motion is computed directly from semi major axis
239      * and central acceleration constant.</p>
240      * @return keplerian mean motion in radians per second
241      */
242     public double getKeplerianMeanMotion() {
243         final double absA = FastMath.abs(getA());
244         return FastMath.sqrt(mu / absA) / absA;
245     }
246 
247     /** Get the date of orbital parameters.
248      * @return date of the orbital parameters
249      */
250     public AbsoluteDate getDate() {
251         return date;
252     }
253 
254     /** Get the {@link PVCoordinates} in a specified frame.
255      * @param outputFrame frame in which the position/velocity coordinates shall be computed
256      * @return pvCoordinates in the specified output frame
257      * @exception OrekitException if transformation between frames cannot be computed
258      * @see #getPVCoordinates()
259      */
260     public PVCoordinates getPVCoordinates(final Frame outputFrame)
261         throws OrekitException {
262         if (pvCoordinates == null) {
263             pvCoordinates = initPVCoordinates();
264         }
265 
266         // If output frame requested is the same as definition frame,
267         // PV coordinates are returned directly
268         if (outputFrame == frame) {
269             return pvCoordinates;
270         }
271 
272         // Else, PV coordinates are transformed to output frame
273         final Transform t = frame.getTransformTo(outputFrame, date);
274         return t.transformPVCoordinates(pvCoordinates);
275     }
276 
277     /** {@inheritDoc} */
278     public PVCoordinates getPVCoordinates(final AbsoluteDate otherDate, final Frame otherFrame)
279         throws OrekitException {
280         return shiftedBy(otherDate.durationFrom(getDate())).getPVCoordinates(otherFrame);
281     }
282 
283 
284     /** Get the {@link PVCoordinates} in definition frame.
285      * @return pvCoordinates in the definition frame
286      * @see #getPVCoordinates(Frame)
287      */
288     public PVCoordinates getPVCoordinates() {
289         if (pvCoordinates == null) {
290             pvCoordinates = initPVCoordinates();
291         }
292         return pvCoordinates;
293     }
294 
295     /** Compute the position/velocity coordinates from the canonical parameters.
296      * @return computed position/velocity coordinates
297      */
298     protected abstract PVCoordinates initPVCoordinates();
299 
300     /** Get a time-shifted orbit.
301      * <p>
302      * The orbit can be slightly shifted to close dates. This shift is based on
303      * a simple keplerian model. It is <em>not</em> intended as a replacement
304      * for proper orbit and attitude propagation but should be sufficient for
305      * small time shifts or coarse accuracy.
306      * </p>
307      * @param dt time shift in seconds
308      * @return a new orbit, shifted with respect to the instance (which is immutable)
309      */
310     public abstract Orbit shiftedBy(final double dt);
311 
312     /** Compute the Jacobian of the orbital parameters with respect to the Cartesian parameters.
313      * <p>
314      * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with
315      * respect to Cartesian coordinate j. This means each row correspond to one orbital parameter
316      * whereas columns 0 to 5 correspond to the Cartesian coordinates x, y, z, xDot, yDot and zDot.
317      * </p>
318      * @param type type of the position angle to use
319      * @param jacobian placeholder 6x6 (or larger) matrix to be filled with the Jacobian, if matrix
320      * is larger than 6x6, only the 6x6 upper left corner will be modified
321      */
322     public void getJacobianWrtCartesian(final PositionAngle type, final double[][] jacobian) {
323 
324         final double[][] cachedJacobian;
325         synchronized (this) {
326             switch (type) {
327             case MEAN :
328                 if (jacobianMeanWrtCartesian == null) {
329                     // first call, we need to compute the jacobian and cache it
330                     jacobianMeanWrtCartesian = computeJacobianMeanWrtCartesian();
331                 }
332                 cachedJacobian = jacobianMeanWrtCartesian;
333                 break;
334             case ECCENTRIC :
335                 if (jacobianEccentricWrtCartesian == null) {
336                     // first call, we need to compute the jacobian and cache it
337                     jacobianEccentricWrtCartesian = computeJacobianEccentricWrtCartesian();
338                 }
339                 cachedJacobian = jacobianEccentricWrtCartesian;
340                 break;
341             case TRUE :
342                 if (jacobianTrueWrtCartesian == null) {
343                     // first call, we need to compute the jacobian and cache it
344                     jacobianTrueWrtCartesian = computeJacobianTrueWrtCartesian();
345                 }
346                 cachedJacobian = jacobianTrueWrtCartesian;
347                 break;
348             default :
349                 throw OrekitException.createInternalError(null);
350             }
351         }
352 
353         // fill the user provided array
354         for (int i = 0; i < cachedJacobian.length; ++i) {
355             System.arraycopy(cachedJacobian[i], 0, jacobian[i], 0, cachedJacobian[i].length);
356         }
357 
358     }
359 
360     /** Compute the Jacobian of the Cartesian parameters with respect to the orbital parameters.
361      * <p>
362      * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with
363      * respect to Cartesian coordinate j. This means each row correspond to one orbital parameter
364      * whereas columns 0 to 5 correspond to the Cartesian coordinates x, y, z, xDot, yDot and zDot.
365      * </p>
366      * @param type type of the position angle to use
367      * @param jacobian placeholder 6x6 (or larger) matrix to be filled with the Jacobian, if matrix
368      * is larger than 6x6, only the 6x6 upper left corner will be modified
369      */
370     public void getJacobianWrtParameters(final PositionAngle type, final double[][] jacobian) {
371 
372         final double[][] cachedJacobian;
373         synchronized (this) {
374             switch (type) {
375             case MEAN :
376                 if (jacobianWrtParametersMean == null) {
377                     // first call, we need to compute the jacobian and cache it
378                     jacobianWrtParametersMean = createInverseJacobian(type);
379                 }
380                 cachedJacobian = jacobianWrtParametersMean;
381                 break;
382             case ECCENTRIC :
383                 if (jacobianWrtParametersEccentric == null) {
384                     // first call, we need to compute the jacobian and cache it
385                     jacobianWrtParametersEccentric = createInverseJacobian(type);
386                 }
387                 cachedJacobian = jacobianWrtParametersEccentric;
388                 break;
389             case TRUE :
390                 if (jacobianWrtParametersTrue == null) {
391                     // first call, we need to compute the jacobian and cache it
392                     jacobianWrtParametersTrue = createInverseJacobian(type);
393                 }
394                 cachedJacobian = jacobianWrtParametersTrue;
395                 break;
396             default :
397                 throw OrekitException.createInternalError(null);
398             }
399         }
400 
401         // fill the user-provided array
402         for (int i = 0; i < cachedJacobian.length; ++i) {
403             System.arraycopy(cachedJacobian[i], 0, jacobian[i], 0, cachedJacobian[i].length);
404         }
405 
406     }
407 
408     /** Create an inverse Jacobian.
409      * @param type type of the position angle to use
410      * @return inverse Jacobian
411      */
412     private double[][] createInverseJacobian(final PositionAngle type) {
413 
414         // get the direct Jacobian
415         final double[][] directJacobian = new double[6][6];
416         getJacobianWrtCartesian(type, directJacobian);
417 
418         // invert the direct Jacobian
419         final RealMatrix matrix = MatrixUtils.createRealMatrix(directJacobian);
420         final DecompositionSolver solver = new QRDecomposition(matrix).getSolver();
421         return solver.getInverse().getData();
422 
423     }
424 
425     /** Compute the Jacobian of the orbital parameters with mean angle with respect to the Cartesian parameters.
426      * <p>
427      * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with
428      * respect to Cartesian coordinate j. This means each row correspond to one orbital parameter
429      * whereas columns 0 to 5 correspond to the Cartesian coordinates x, y, z, xDot, yDot and zDot.
430      * </p>
431      * @return 6x6 Jacobian matrix
432      * @see #computeJacobianEccentricWrtCartesian()
433      * @see #computeJacobianTrueWrtCartesian()
434      */
435     protected abstract double[][] computeJacobianMeanWrtCartesian();
436 
437     /** Compute the Jacobian of the orbital parameters with eccentric angle with respect to the Cartesian parameters.
438      * <p>
439      * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with
440      * respect to Cartesian coordinate j. This means each row correspond to one orbital parameter
441      * whereas columns 0 to 5 correspond to the Cartesian coordinates x, y, z, xDot, yDot and zDot.
442      * </p>
443      * @return 6x6 Jacobian matrix
444      * @see #computeJacobianMeanWrtCartesian()
445      * @see #computeJacobianTrueWrtCartesian()
446      */
447     protected abstract double[][] computeJacobianEccentricWrtCartesian();
448 
449     /** Compute the Jacobian of the orbital parameters with true angle with respect to the Cartesian parameters.
450      * <p>
451      * Element {@code jacobian[i][j]} is the derivative of parameter i of the orbit with
452      * respect to Cartesian coordinate j. This means each row correspond to one orbital parameter
453      * whereas columns 0 to 5 correspond to the Cartesian coordinates x, y, z, xDot, yDot and zDot.
454      * </p>
455      * @return 6x6 Jacobian matrix
456      * @see #computeJacobianMeanWrtCartesian()
457      * @see #computeJacobianEccentricWrtCartesian()
458      */
459     protected abstract double[][] computeJacobianTrueWrtCartesian();
460 
461     /** Add the contribution of the Keplerian motion to parameters derivatives
462      * <p>
463      * This method is used by integration-based propagators to evaluate the part of Keplerian
464      * motion to evolution of the orbital state.
465      * </p>
466      * @param type type of the position angle in the state
467      * @param gm attraction coefficient to use
468      * @param pDot array containing orbital state derivatives to update (the Keplerian
469      * part must be <em>added</em> to the array components, as the array may already
470      * contain some non-zero elements corresponding to non-Keplerian parts)
471      */
472     public abstract void addKeplerContribution(final PositionAngle type, final double gm, double[] pDot);
473 
474         /** Fill a Jacobian half row with a single vector.
475      * @param a coefficient of the vector
476      * @param v vector
477      * @param row Jacobian matrix row
478      * @param j index of the first element to set (row[j], row[j+1] and row[j+2] will all be set)
479      */
480     protected static void fillHalfRow(final double a, final Vector3D v, final double[] row, final int j) {
481         row[j]     = a * v.getX();
482         row[j + 1] = a * v.getY();
483         row[j + 2] = a * v.getZ();
484     }
485 
486     /** Fill a Jacobian half row with a linear combination of vectors.
487      * @param a1 coefficient of the first vector
488      * @param v1 first vector
489      * @param a2 coefficient of the second vector
490      * @param v2 second vector
491      * @param row Jacobian matrix row
492      * @param j index of the first element to set (row[j], row[j+1] and row[j+2] will all be set)
493      */
494     protected static void fillHalfRow(final double a1, final Vector3D v1, final double a2, final Vector3D v2,
495                                       final double[] row, final int j) {
496         row[j]     = a1 * v1.getX() + a2 * v2.getX();
497         row[j + 1] = a1 * v1.getY() + a2 * v2.getY();
498         row[j + 2] = a1 * v1.getZ() + a2 * v2.getZ();
499     }
500 
501     /** Fill a Jacobian half row with a linear combination of vectors.
502      * @param a1 coefficient of the first vector
503      * @param v1 first vector
504      * @param a2 coefficient of the second vector
505      * @param v2 second vector
506      * @param a3 coefficient of the third vector
507      * @param v3 third vector
508      * @param row Jacobian matrix row
509      * @param j index of the first element to set (row[j], row[j+1] and row[j+2] will all be set)
510      */
511     protected static void fillHalfRow(final double a1, final Vector3D v1, final double a2, final Vector3D v2,
512                                       final double a3, final Vector3D v3,
513                                       final double[] row, final int j) {
514         row[j]     = a1 * v1.getX() + a2 * v2.getX() + a3 * v3.getX();
515         row[j + 1] = a1 * v1.getY() + a2 * v2.getY() + a3 * v3.getY();
516         row[j + 2] = a1 * v1.getZ() + a2 * v2.getZ() + a3 * v3.getZ();
517     }
518 
519     /** Fill a Jacobian half row with a linear combination of vectors.
520      * @param a1 coefficient of the first vector
521      * @param v1 first vector
522      * @param a2 coefficient of the second vector
523      * @param v2 second vector
524      * @param a3 coefficient of the third vector
525      * @param v3 third vector
526      * @param a4 coefficient of the fourth vector
527      * @param v4 fourth vector
528      * @param row Jacobian matrix row
529      * @param j index of the first element to set (row[j], row[j+1] and row[j+2] will all be set)
530      */
531     protected static void fillHalfRow(final double a1, final Vector3D v1, final double a2, final Vector3D v2,
532                                       final double a3, final Vector3D v3, final double a4, final Vector3D v4,
533                                       final double[] row, final int j) {
534         row[j]     = a1 * v1.getX() + a2 * v2.getX() + a3 * v3.getX() + a4 * v4.getX();
535         row[j + 1] = a1 * v1.getY() + a2 * v2.getY() + a3 * v3.getY() + a4 * v4.getY();
536         row[j + 2] = a1 * v1.getZ() + a2 * v2.getZ() + a3 * v3.getZ() + a4 * v4.getZ();
537     }
538 
539     /** Fill a Jacobian half row with a linear combination of vectors.
540      * @param a1 coefficient of the first vector
541      * @param v1 first vector
542      * @param a2 coefficient of the second vector
543      * @param v2 second vector
544      * @param a3 coefficient of the third vector
545      * @param v3 third vector
546      * @param a4 coefficient of the fourth vector
547      * @param v4 fourth vector
548      * @param a5 coefficient of the fifth vector
549      * @param v5 fifth vector
550      * @param row Jacobian matrix row
551      * @param j index of the first element to set (row[j], row[j+1] and row[j+2] will all be set)
552      */
553     protected static void fillHalfRow(final double a1, final Vector3D v1, final double a2, final Vector3D v2,
554                                       final double a3, final Vector3D v3, final double a4, final Vector3D v4,
555                                       final double a5, final Vector3D v5,
556                                       final double[] row, final int j) {
557         row[j]     = a1 * v1.getX() + a2 * v2.getX() + a3 * v3.getX() + a4 * v4.getX() + a5 * v5.getX();
558         row[j + 1] = a1 * v1.getY() + a2 * v2.getY() + a3 * v3.getY() + a4 * v4.getY() + a5 * v5.getY();
559         row[j + 2] = a1 * v1.getZ() + a2 * v2.getZ() + a3 * v3.getZ() + a4 * v4.getZ() + a5 * v5.getZ();
560     }
561 
562     /** Fill a Jacobian half row with a linear combination of vectors.
563      * @param a1 coefficient of the first vector
564      * @param v1 first vector
565      * @param a2 coefficient of the second vector
566      * @param v2 second vector
567      * @param a3 coefficient of the third vector
568      * @param v3 third vector
569      * @param a4 coefficient of the fourth vector
570      * @param v4 fourth vector
571      * @param a5 coefficient of the fifth vector
572      * @param v5 fifth vector
573      * @param a6 coefficient of the sixth vector
574      * @param v6 sixth vector
575      * @param row Jacobian matrix row
576      * @param j index of the first element to set (row[j], row[j+1] and row[j+2] will all be set)
577      */
578     protected static void fillHalfRow(final double a1, final Vector3D v1, final double a2, final Vector3D v2,
579                                       final double a3, final Vector3D v3, final double a4, final Vector3D v4,
580                                       final double a5, final Vector3D v5, final double a6, final Vector3D v6,
581                                       final double[] row, final int j) {
582         row[j]     = a1 * v1.getX() + a2 * v2.getX() + a3 * v3.getX() + a4 * v4.getX() + a5 * v5.getX() + a6 * v6.getX();
583         row[j + 1] = a1 * v1.getY() + a2 * v2.getY() + a3 * v3.getY() + a4 * v4.getY() + a5 * v5.getY() + a6 * v6.getY();
584         row[j + 2] = a1 * v1.getZ() + a2 * v2.getZ() + a3 * v3.getZ() + a4 * v4.getZ() + a5 * v5.getZ() + a6 * v6.getZ();
585     }
586 
587 }