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.propagation.semianalytical.dsst.forces;
18  
19  import org.apache.commons.math3.analysis.UnivariateVectorFunction;
20  import org.apache.commons.math3.geometry.euclidean.threed.Vector3D;
21  import org.apache.commons.math3.util.FastMath;
22  import org.orekit.errors.OrekitException;
23  import org.orekit.errors.OrekitExceptionWrapper;
24  import org.orekit.propagation.SpacecraftState;
25  import org.orekit.propagation.semianalytical.dsst.utilities.AuxiliaryElements;
26  
27  /** Common handling of {@link DSSTForceModel} methods for Gaussian contributions to DSST propagation.
28   * <p>
29   * This abstract class allows to provide easily a subset of {@link DSSTForceModel} methods
30   * for specific Gaussian contributions (i.e. atmospheric drag and solar radiation pressure).
31   * </p><p>
32   * Gaussian contributions can be expressed as: da<sub>i</sub>/dt = &delta;a<sub>i</sub>/&delta;v . q<br>
33   * where:
34   * <ul>
35   * <li>a<sub>i</sub> are the six equinoctial elements</li>
36   * <li>v is the velocity vector</li>
37   * <li>q is the perturbing acceleration due to the considered force</li>
38   * </ul>
39   * The averaging process and other considerations lead to integrate this contribution
40   * over the true longitude L possibly taking into account some limits.
41   * </p><p>
42   * Only two methods must be implemented by derived classes:
43   * {@link #getAcceleration(SpacecraftState, Vector3D, Vector3D)} and
44   * {@link #getLLimits(SpacecraftState)}.
45   * </p>
46   * @author Pascal Parraud
47   */
48  public abstract class AbstractGaussianContribution implements DSSTForceModel {
49  
50      /** Available orders for Gauss quadrature. */
51      private static final int[] GAUSS_ORDER = {12, 16, 20, 24, 32, 40, 48};
52  
53      /** Max rank in Gauss quadrature orders array. */
54      private static final int MAX_ORDER_RANK = GAUSS_ORDER.length - 1;
55  
56      // CHECKSTYLE: stop VisibilityModifierCheck
57  
58      /** Retrograde factor. */
59      protected double I;
60  
61      /** a. */
62      protected double a;
63      /** e<sub>x</sub>. */
64      protected double k;
65      /** e<sub>y</sub>. */
66      protected double h;
67      /** h<sub>x</sub>. */
68      protected double q;
69      /** h<sub>y</sub>. */
70      protected double p;
71  
72      /** Eccentricity. */
73      protected double ecc;
74  
75      /** Kepler mean motion: n = sqrt(&mu; / a<sup>3</sup>). */
76      protected double n;
77  
78      /** Equinoctial frame f vector. */
79      protected Vector3D f;
80      /** Equinoctial frame g vector. */
81      protected Vector3D g;
82      /** Equinoctial frame w vector. */
83      protected Vector3D w;
84  
85      /** A = sqrt(&mu; * a). */
86      protected double A;
87      /** B = sqrt(1 - h<sup>2</sup> - k<sup>2</sup>). */
88      protected double B;
89      /** C = 1 + p<sup>2</sup> + q<sup>2</sup>. */
90      protected double C;
91  
92      /** 2 / (n<sup>2</sup> * a) . */
93      protected double ton2a;
94      /** 1 / A .*/
95      protected double ooA;
96      /** 1 / (A * B) .*/
97      protected double ooAB;
98      /** C / (2 * A * B) .*/
99      protected double co2AB;
100     /** 1 / (1 + B) .*/
101     protected double ooBpo;
102     /** 1 / &mu; .*/
103     protected double ooMu;
104 
105     // CHECKSTYLE: resume VisibilityModifierCheck
106 
107     /** Gauss integrator. */
108     private final double threshold;
109 
110     /** Gauss integrator. */
111     private GaussQuadrature integrator;
112 
113     /** Flag for Gauss order computation. */
114     private boolean isDirty;
115 
116     /** Build a new instance.
117      *
118      *  @param threshold tolerance for the choice of the Gauss quadrature order
119      */
120     protected AbstractGaussianContribution(final double threshold) {
121         this.threshold  = threshold;
122         this.integrator = new GaussQuadrature(GAUSS_ORDER[MAX_ORDER_RANK]);
123         this.isDirty    = true;
124     }
125 
126     /** {@inheritDoc} */
127     public void initialize(final AuxiliaryElements aux)
128         throws OrekitException {
129         // Nothing to do for gaussian contributions at the beginning of the propagation.
130     }
131 
132     /** {@inheritDoc} */
133     public void initializeStep(final AuxiliaryElements aux)
134         throws OrekitException {
135 
136         // Equinoctial elements
137         a  = aux.getSma();
138         k  = aux.getK();
139         h  = aux.getH();
140         q  = aux.getQ();
141         p  = aux.getP();
142 
143         // Retrograde factor
144         I = aux.getRetrogradeFactor();
145 
146         // Eccentricity
147         ecc = aux.getEcc();
148 
149         // Equinoctial coefficients
150         A = aux.getA();
151         B = aux.getB();
152         C = aux.getC();
153 
154         // Equinoctial frame vectors
155         f = aux.getVectorF();
156         g = aux.getVectorG();
157         w = aux.getVectorW();
158 
159         // Kepler mean motion
160         n = A / (a * a);
161 
162         // 1 / A
163         ooA = 1. / A;
164         // 1 / AB
165         ooAB = ooA / B;
166         // C / 2AB
167         co2AB = C * ooAB / 2.;
168         // 1 / (1 + B)
169         ooBpo = 1. / (1. + B);
170         // 2 / (n² * a)
171         ton2a = 2. / (n * n * a);
172         // 1 / mu
173         ooMu  = 1. / aux.getMu();
174     }
175 
176     /** {@inheritDoc} */
177     public double[] getMeanElementRate(final SpacecraftState state) throws OrekitException {
178 
179         double[] meanElementRate = new double[6];
180         // Computes the limits for the integral
181         final double[] ll = getLLimits(state);
182         // Computes integrated mean element rates if Llow < Lhigh
183         if (ll[0] < ll[1]) {
184             meanElementRate = getMeanElementRate(state, integrator, ll[0], ll[1]);
185             if (isDirty) {
186                 boolean next = true;
187                 for (int i = 0; i < MAX_ORDER_RANK && next; i++) {
188                     final double[] meanRates = getMeanElementRate(state, new GaussQuadrature(GAUSS_ORDER[i]), ll[0], ll[1]);
189                     if (getRatesDiff(meanElementRate, meanRates) < threshold) {
190                         integrator = new GaussQuadrature(GAUSS_ORDER[i]);
191                         next = false;
192                     }
193                 }
194                 isDirty = false;
195             }
196         }
197         return meanElementRate;
198     }
199 
200     /** Compute the acceleration due to the non conservative perturbing force.
201      *
202      *  @param state current state information: date, kinematics, attitude
203      *  @param position spacecraft position
204      *  @param velocity spacecraft velocity
205      *  @return the perturbing acceleration
206      *  @exception OrekitException if some specific error occurs
207      */
208     protected abstract Vector3D getAcceleration(final SpacecraftState state,
209                                                 final Vector3D position,
210                                                 final Vector3D velocity) throws OrekitException;
211 
212     /** Compute the limits in L, the true longitude, for integration.
213      *
214      *  @param  state current state information: date, kinematics, attitude
215      *  @return the integration limits in L
216      *  @exception OrekitException if some specific error occurs
217      */
218     protected abstract double[] getLLimits(final SpacecraftState state) throws OrekitException;
219 
220     /** Computes the mean equinoctial elements rates da<sub>i</sub> / dt.
221      *
222      *  @param state current state
223      *  @param gauss Gauss quadrature
224      *  @param low lower bound of the integral interval
225      *  @param high upper bound of the integral interval
226      *  @return the mean element rates
227      *  @throws OrekitException if some specific error occurs
228      */
229     private double[] getMeanElementRate(final SpacecraftState state,
230                                         final GaussQuadrature gauss,
231                                         final double low,
232                                         final double high) throws OrekitException {
233         final double[] meanElementRate = gauss.integrate(new IntegrableFunction(state), low, high);
234         // Constant multiplier for integral
235         final double coef = 1. / (2. * FastMath.PI * B);
236         // Corrects mean element rates
237         for (int i = 0; i < 6; i++) {
238             meanElementRate[i] *= coef;
239         }
240         return meanElementRate;
241     }
242 
243     /** Estimates the weighted magnitude of the difference between 2 sets of equinoctial elements rates.
244      *
245      *  @param meanRef reference rates
246      *  @param meanCur current rates
247      *  @return estimated magnitude of weighted differences
248      */
249     private double getRatesDiff(final double[] meanRef, final double[] meanCur) {
250         double maxDiff = FastMath.abs(meanRef[0] - meanCur[0]) / a;
251         // Corrects mean element rates
252         for (int i = 1; i < meanRef.length; i++) {
253             final double diff = FastMath.abs(meanRef[i] - meanCur[i]);
254             if (maxDiff < diff) maxDiff = diff;
255         }
256         return maxDiff;
257     }
258 
259     /** Internal class for numerical quadrature. */
260     private class IntegrableFunction implements UnivariateVectorFunction {
261 
262         /** Current state. */
263         private final SpacecraftState state;
264 
265         /** Build a new instance.
266          *  @param  state current state information: date, kinematics, attitude
267          */
268         public IntegrableFunction(final SpacecraftState state) {
269             this.state = state;
270         }
271 
272         /** {@inheritDoc} */
273         public double[] value(final double x) {
274             final double cosL = FastMath.cos(x);
275             final double sinL = FastMath.sin(x);
276             final double roa  = B * B / (1. + h * sinL + k * cosL);
277             final double roa2 = roa * roa;
278             final double r    = a * roa;
279             final double X    = r * cosL;
280             final double Y    = r * sinL;
281             final double naob = n * a / B;
282             final double Xdot = -naob * (h + sinL);
283             final double Ydot =  naob * (k + cosL);
284             final Vector3D pos = new Vector3D(X, f, Y, g);
285             final Vector3D vel = new Vector3D(Xdot, f, Ydot, g);
286             // Compute acceleration
287             Vector3D acc = Vector3D.ZERO;
288             try {
289                 acc = getAcceleration(state, pos, vel);
290             } catch (OrekitException oe) {
291                 throw new OrekitExceptionWrapper(oe);
292             }
293             // Compute mean elements rates
294             final double[] val = new double[6];
295             // da/dt
296             val[0] = roa2 * getAoV(vel).dotProduct(acc);
297             // dex/dt
298             val[1] = roa2 * getKoV(X, Y, Xdot, Ydot).dotProduct(acc);
299             // dey/dt
300             val[2] = roa2 * getHoV(X, Y, Xdot, Ydot).dotProduct(acc);
301             // dhx/dt
302             val[3] = roa2 * getQoV(X).dotProduct(acc);
303             // dhy/dt
304             val[4] = roa2 * getPoV(Y).dotProduct(acc);
305             // d&lambda;/dt
306             val[5] = roa2 * getLoV(X, Y, Xdot, Ydot).dotProduct(acc);
307 
308             return val;
309         }
310 
311         /** Compute &delta;a/&delta;v.
312          *  @param vel satellite velocity
313          *  @return &delta;a/&delta;v
314          */
315         private Vector3D getAoV(final Vector3D vel) {
316             return new Vector3D(ton2a, vel);
317         }
318 
319         /** Compute &delta;h/&delta;v.
320          *  @param X satellite position component along f, equinoctial reference frame 1st vector
321          *  @param Y satellite position component along g, equinoctial reference frame 2nd vector
322          *  @param Xdot satellite velocity component along f, equinoctial reference frame 1st vector
323          *  @param Ydot satellite velocity component along g, equinoctial reference frame 2nd vector
324          *  @return &delta;h/&delta;v
325          */
326         private Vector3D getHoV(final double X, final double Y, final double Xdot, final double Ydot) {
327             final double kf = (2. * Xdot * Y - X * Ydot) * ooMu;
328             final double kg = X * Xdot * ooMu;
329             final double kw = k * (I * q * Y - p * X) * ooAB;
330             return new Vector3D(kf, f, -kg, g, kw, w);
331         }
332 
333         /** Compute &delta;k/&delta;v.
334          *  @param X satellite position component along f, equinoctial reference frame 1st vector
335          *  @param Y satellite position component along g, equinoctial reference frame 2nd vector
336          *  @param Xdot satellite velocity component along f, equinoctial reference frame 1st vector
337          *  @param Ydot satellite velocity component along g, equinoctial reference frame 2nd vector
338          *  @return &delta;k/&delta;v
339          */
340         private Vector3D getKoV(final double X, final double Y, final double Xdot, final double Ydot) {
341             final double kf = Y * Ydot * ooMu;
342             final double kg = (2. * X * Ydot - Xdot * Y) * ooMu;
343             final double kw = h * (I * q * Y - p * X) * ooAB;
344             return new Vector3D(-kf, f, kg, g, -kw, w);
345         }
346 
347         /** Compute &delta;p/&delta;v.
348          *  @param Y satellite position component along g, equinoctial reference frame 2nd vector
349          *  @return &delta;p/&delta;v
350          */
351         private Vector3D getPoV(final double Y) {
352             return new Vector3D(co2AB * Y, w);
353         }
354 
355         /** Compute &delta;q/&delta;v.
356          *  @param X satellite position component along f, equinoctial reference frame 1st vector
357          *  @return &delta;q/&delta;v
358          */
359         private Vector3D getQoV(final double X) {
360             return new Vector3D(I * co2AB * X, w);
361         }
362 
363         /** Compute &delta;&lambda;/&delta;v.
364          *  @param X satellite position component along f, equinoctial reference frame 1st vector
365          *  @param Y satellite position component along g, equinoctial reference frame 2nd vector
366          *  @param Xdot satellite velocity component along f, equinoctial reference frame 1st vector
367          *  @param Ydot satellite velocity component along g, equinoctial reference frame 2nd vector
368          *  @return &delta;&lambda;/&delta;v
369          */
370         private Vector3D getLoV(final double X, final double Y, final double Xdot, final double Ydot) {
371             final Vector3D pos = new Vector3D(X, f, Y, g);
372             final Vector3D v2  = new Vector3D(k, getHoV(X, Y, Xdot, Ydot), -h, getKoV(X, Y, Xdot, Ydot));
373             return new Vector3D(-2. * ooA, pos, ooBpo, v2, (I * q * Y - p * X) * ooA, w);
374         }
375 
376     }
377 
378     /** Class used to {@link #integrate(UnivariateVectorFunction, double, double) integrate}
379      *  a {@link org.apache.commons.math3.analysis.UnivariateVectorFunction function}
380      *  of the orbital elements using the Gaussian quadrature rule to get the acceleration.
381      */
382     private static class GaussQuadrature {
383 
384         // CHECKSTYLE: stop NoWhitespaceAfter
385 
386         // Points and weights for the available quadrature orders
387 
388         /** Points for quadrature of order 12. */
389         private static final double[] P_12 = {
390             -0.98156063424671910000,
391             -0.90411725637047490000,
392             -0.76990267419430470000,
393             -0.58731795428661740000,
394             -0.36783149899818024000,
395             -0.12523340851146890000,
396             0.12523340851146890000,
397             0.36783149899818024000,
398             0.58731795428661740000,
399             0.76990267419430470000,
400             0.90411725637047490000,
401             0.98156063424671910000
402         };
403 
404         /** Weights for quadrature of order 12. */
405         private static final double[] W_12 = {
406             0.04717533638651220000,
407             0.10693932599531830000,
408             0.16007832854334633000,
409             0.20316742672306584000,
410             0.23349253653835478000,
411             0.24914704581340286000,
412             0.24914704581340286000,
413             0.23349253653835478000,
414             0.20316742672306584000,
415             0.16007832854334633000,
416             0.10693932599531830000,
417             0.04717533638651220000
418         };
419 
420         /** Points for quadrature of order 16. */
421         private static final double[] P_16 = {
422             -0.98940093499164990000,
423             -0.94457502307323260000,
424             -0.86563120238783160000,
425             -0.75540440835500310000,
426             -0.61787624440264380000,
427             -0.45801677765722737000,
428             -0.28160355077925890000,
429             -0.09501250983763745000,
430             0.09501250983763745000,
431             0.28160355077925890000,
432             0.45801677765722737000,
433             0.61787624440264380000,
434             0.75540440835500310000,
435             0.86563120238783160000,
436             0.94457502307323260000,
437             0.98940093499164990000
438         };
439 
440         /** Weights for quadrature of order 16. */
441         private static final double[] W_16 = {
442             0.02715245941175405800,
443             0.06225352393864777000,
444             0.09515851168249283000,
445             0.12462897125553388000,
446             0.14959598881657685000,
447             0.16915651939500256000,
448             0.18260341504492360000,
449             0.18945061045506847000,
450             0.18945061045506847000,
451             0.18260341504492360000,
452             0.16915651939500256000,
453             0.14959598881657685000,
454             0.12462897125553388000,
455             0.09515851168249283000,
456             0.06225352393864777000,
457             0.02715245941175405800
458         };
459 
460         /** Points for quadrature of order 20. */
461         private static final double[] P_20 = {
462             -0.99312859918509490000,
463             -0.96397192727791390000,
464             -0.91223442825132600000,
465             -0.83911697182221890000,
466             -0.74633190646015080000,
467             -0.63605368072651510000,
468             -0.51086700195082700000,
469             -0.37370608871541955000,
470             -0.22778585114164507000,
471             -0.07652652113349734000,
472             0.07652652113349734000,
473             0.22778585114164507000,
474             0.37370608871541955000,
475             0.51086700195082700000,
476             0.63605368072651510000,
477             0.74633190646015080000,
478             0.83911697182221890000,
479             0.91223442825132600000,
480             0.96397192727791390000,
481             0.99312859918509490000
482         };
483 
484         /** Weights for quadrature of order 20. */
485         private static final double[] W_20 = {
486             0.01761400713915226400,
487             0.04060142980038684000,
488             0.06267204833410904000,
489             0.08327674157670477000,
490             0.10193011981724048000,
491             0.11819453196151844000,
492             0.13168863844917678000,
493             0.14209610931838212000,
494             0.14917298647260380000,
495             0.15275338713072600000,
496             0.15275338713072600000,
497             0.14917298647260380000,
498             0.14209610931838212000,
499             0.13168863844917678000,
500             0.11819453196151844000,
501             0.10193011981724048000,
502             0.08327674157670477000,
503             0.06267204833410904000,
504             0.04060142980038684000,
505             0.01761400713915226400
506         };
507 
508         /** Points for quadrature of order 24. */
509         private static final double[] P_24 = {
510             -0.99518721999702130000,
511             -0.97472855597130950000,
512             -0.93827455200273270000,
513             -0.88641552700440100000,
514             -0.82000198597390300000,
515             -0.74012419157855440000,
516             -0.64809365193697550000,
517             -0.54542147138883950000,
518             -0.43379350762604520000,
519             -0.31504267969616340000,
520             -0.19111886747361634000,
521             -0.06405689286260563000,
522             0.06405689286260563000,
523             0.19111886747361634000,
524             0.31504267969616340000,
525             0.43379350762604520000,
526             0.54542147138883950000,
527             0.64809365193697550000,
528             0.74012419157855440000,
529             0.82000198597390300000,
530             0.88641552700440100000,
531             0.93827455200273270000,
532             0.97472855597130950000,
533             0.99518721999702130000
534         };
535 
536         /** Weights for quadrature of order 24. */
537         private static final double[] W_24 = {
538             0.01234122979998733500,
539             0.02853138862893380600,
540             0.04427743881741981000,
541             0.05929858491543691500,
542             0.07334648141108027000,
543             0.08619016153195320000,
544             0.09761865210411391000,
545             0.10744427011596558000,
546             0.11550566805372553000,
547             0.12167047292780335000,
548             0.12583745634682825000,
549             0.12793819534675221000,
550             0.12793819534675221000,
551             0.12583745634682825000,
552             0.12167047292780335000,
553             0.11550566805372553000,
554             0.10744427011596558000,
555             0.09761865210411391000,
556             0.08619016153195320000,
557             0.07334648141108027000,
558             0.05929858491543691500,
559             0.04427743881741981000,
560             0.02853138862893380600,
561             0.01234122979998733500
562         };
563 
564         /** Points for quadrature of order 32. */
565         private static final double[] P_32 = {
566             -0.99726386184948160000,
567             -0.98561151154526840000,
568             -0.96476225558750640000,
569             -0.93490607593773970000,
570             -0.89632115576605220000,
571             -0.84936761373256990000,
572             -0.79448379596794250000,
573             -0.73218211874028970000,
574             -0.66304426693021520000,
575             -0.58771575724076230000,
576             -0.50689990893222950000,
577             -0.42135127613063540000,
578             -0.33186860228212767000,
579             -0.23928736225213710000,
580             -0.14447196158279646000,
581             -0.04830766568773831000,
582             0.04830766568773831000,
583             0.14447196158279646000,
584             0.23928736225213710000,
585             0.33186860228212767000,
586             0.42135127613063540000,
587             0.50689990893222950000,
588             0.58771575724076230000,
589             0.66304426693021520000,
590             0.73218211874028970000,
591             0.79448379596794250000,
592             0.84936761373256990000,
593             0.89632115576605220000,
594             0.93490607593773970000,
595             0.96476225558750640000,
596             0.98561151154526840000,
597             0.99726386184948160000
598         };
599 
600         /** Weights for quadrature of order 32. */
601         private static final double[] W_32 = {
602             0.00701861000947013600,
603             0.01627439473090571200,
604             0.02539206530926214200,
605             0.03427386291302141000,
606             0.04283589802222658600,
607             0.05099805926237621600,
608             0.05868409347853559000,
609             0.06582222277636193000,
610             0.07234579410884862000,
611             0.07819389578707042000,
612             0.08331192422694673000,
613             0.08765209300440380000,
614             0.09117387869576390000,
615             0.09384439908080441000,
616             0.09563872007927487000,
617             0.09654008851472784000,
618             0.09654008851472784000,
619             0.09563872007927487000,
620             0.09384439908080441000,
621             0.09117387869576390000,
622             0.08765209300440380000,
623             0.08331192422694673000,
624             0.07819389578707042000,
625             0.07234579410884862000,
626             0.06582222277636193000,
627             0.05868409347853559000,
628             0.05099805926237621600,
629             0.04283589802222658600,
630             0.03427386291302141000,
631             0.02539206530926214200,
632             0.01627439473090571200,
633             0.00701861000947013600
634         };
635 
636         /** Points for quadrature of order 40. */
637         private static final double[] P_40 = {
638             -0.99823770971055930000,
639             -0.99072623869945710000,
640             -0.97725994998377420000,
641             -0.95791681921379170000,
642             -0.93281280827867660000,
643             -0.90209880696887420000,
644             -0.86595950321225960000,
645             -0.82461223083331170000,
646             -0.77830565142651940000,
647             -0.72731825518992710000,
648             -0.67195668461417960000,
649             -0.61255388966798030000,
650             -0.54946712509512820000,
651             -0.48307580168617870000,
652             -0.41377920437160500000,
653             -0.34199409082575850000,
654             -0.26815218500725370000,
655             -0.19269758070137110000,
656             -0.11608407067525522000,
657             -0.03877241750605081600,
658             0.03877241750605081600,
659             0.11608407067525522000,
660             0.19269758070137110000,
661             0.26815218500725370000,
662             0.34199409082575850000,
663             0.41377920437160500000,
664             0.48307580168617870000,
665             0.54946712509512820000,
666             0.61255388966798030000,
667             0.67195668461417960000,
668             0.72731825518992710000,
669             0.77830565142651940000,
670             0.82461223083331170000,
671             0.86595950321225960000,
672             0.90209880696887420000,
673             0.93281280827867660000,
674             0.95791681921379170000,
675             0.97725994998377420000,
676             0.99072623869945710000,
677             0.99823770971055930000
678         };
679 
680         /** Weights for quadrature of order 40. */
681         private static final double[] W_40 = {
682             0.00452127709853309800,
683             0.01049828453115270400,
684             0.01642105838190797300,
685             0.02224584919416689000,
686             0.02793700698002338000,
687             0.03346019528254786500,
688             0.03878216797447199000,
689             0.04387090818567333000,
690             0.04869580763507221000,
691             0.05322784698393679000,
692             0.05743976909939157000,
693             0.06130624249292891000,
694             0.06480401345660108000,
695             0.06791204581523394000,
696             0.07061164739128681000,
697             0.07288658239580408000,
698             0.07472316905796833000,
699             0.07611036190062619000,
700             0.07703981816424793000,
701             0.07750594797842482000,
702             0.07750594797842482000,
703             0.07703981816424793000,
704             0.07611036190062619000,
705             0.07472316905796833000,
706             0.07288658239580408000,
707             0.07061164739128681000,
708             0.06791204581523394000,
709             0.06480401345660108000,
710             0.06130624249292891000,
711             0.05743976909939157000,
712             0.05322784698393679000,
713             0.04869580763507221000,
714             0.04387090818567333000,
715             0.03878216797447199000,
716             0.03346019528254786500,
717             0.02793700698002338000,
718             0.02224584919416689000,
719             0.01642105838190797300,
720             0.01049828453115270400,
721             0.00452127709853309800
722         };
723 
724         /** Points for quadrature of order 48. */
725         private static final double[] P_48 = {
726             -0.99877100725242610000,
727             -0.99353017226635080000,
728             -0.98412458372282700000,
729             -0.97059159254624720000,
730             -0.95298770316043080000,
731             -0.93138669070655440000,
732             -0.90587913671556960000,
733             -0.87657202027424800000,
734             -0.84358826162439350000,
735             -0.80706620402944250000,
736             -0.76715903251574020000,
737             -0.72403413092381470000,
738             -0.67787237963266400000,
739             -0.62886739677651370000,
740             -0.57722472608397270000,
741             -0.52316097472223300000,
742             -0.46690290475095840000,
743             -0.40868648199071680000,
744             -0.34875588629216070000,
745             -0.28736248735545555000,
746             -0.22476379039468908000,
747             -0.16122235606889174000,
748             -0.09700469920946270000,
749             -0.03238017096286937000,
750             0.03238017096286937000,
751             0.09700469920946270000,
752             0.16122235606889174000,
753             0.22476379039468908000,
754             0.28736248735545555000,
755             0.34875588629216070000,
756             0.40868648199071680000,
757             0.46690290475095840000,
758             0.52316097472223300000,
759             0.57722472608397270000,
760             0.62886739677651370000,
761             0.67787237963266400000,
762             0.72403413092381470000,
763             0.76715903251574020000,
764             0.80706620402944250000,
765             0.84358826162439350000,
766             0.87657202027424800000,
767             0.90587913671556960000,
768             0.93138669070655440000,
769             0.95298770316043080000,
770             0.97059159254624720000,
771             0.98412458372282700000,
772             0.99353017226635080000,
773             0.99877100725242610000
774         };
775 
776         /** Weights for quadrature of order 48. */
777         private static final double[] W_48 = {
778             0.00315334605230596250,
779             0.00732755390127620800,
780             0.01147723457923446900,
781             0.01557931572294386600,
782             0.01961616045735556700,
783             0.02357076083932435600,
784             0.02742650970835688000,
785             0.03116722783279807000,
786             0.03477722256477045000,
787             0.03824135106583080600,
788             0.04154508294346483000,
789             0.04467456085669424000,
790             0.04761665849249054000,
791             0.05035903555385448000,
792             0.05289018948519365000,
793             0.05519950369998416500,
794             0.05727729210040315000,
795             0.05911483969839566000,
796             0.06070443916589384000,
797             0.06203942315989268000,
798             0.06311419228625403000,
799             0.06392423858464817000,
800             0.06446616443595010000,
801             0.06473769681268386000,
802             0.06473769681268386000,
803             0.06446616443595010000,
804             0.06392423858464817000,
805             0.06311419228625403000,
806             0.06203942315989268000,
807             0.06070443916589384000,
808             0.05911483969839566000,
809             0.05727729210040315000,
810             0.05519950369998416500,
811             0.05289018948519365000,
812             0.05035903555385448000,
813             0.04761665849249054000,
814             0.04467456085669424000,
815             0.04154508294346483000,
816             0.03824135106583080600,
817             0.03477722256477045000,
818             0.03116722783279807000,
819             0.02742650970835688000,
820             0.02357076083932435600,
821             0.01961616045735556700,
822             0.01557931572294386600,
823             0.01147723457923446900,
824             0.00732755390127620800,
825             0.00315334605230596250
826         };
827         // CHECKSTYLE: resume NoWhitespaceAfter
828 
829         /** Node points. */
830         private final double[] nodePoints;
831 
832         /** Node weights. */
833         private final double[] nodeWeights;
834 
835         /** Creates a Gauss integrator of the given order.
836          *
837          *  @param numberOfPoints Order of the integration rule.
838          */
839         public GaussQuadrature(final int numberOfPoints) {
840 
841             switch(numberOfPoints) {
842             case 12 :
843                 this.nodePoints  = P_12.clone();
844                 this.nodeWeights = W_12.clone();
845                 break;
846             case 16 :
847                 this.nodePoints  = P_16.clone();
848                 this.nodeWeights = W_16.clone();
849                 break;
850             case 20 :
851                 this.nodePoints  = P_20.clone();
852                 this.nodeWeights = W_20.clone();
853                 break;
854             case 24 :
855                 this.nodePoints  = P_24.clone();
856                 this.nodeWeights = W_24.clone();
857                 break;
858             case 32 :
859                 this.nodePoints  = P_32.clone();
860                 this.nodeWeights = W_32.clone();
861                 break;
862             case 40 :
863                 this.nodePoints  = P_40.clone();
864                 this.nodeWeights = W_40.clone();
865                 break;
866             case 48 :
867             default :
868                 this.nodePoints  = P_48.clone();
869                 this.nodeWeights = W_48.clone();
870                 break;
871             }
872 
873         }
874 
875         /** Integrates a given function on the given interval.
876          *
877          *  @param f Function to integrate.
878          *  @param lowerBound Lower bound of the integration interval.
879          *  @param upperBound Upper bound of the integration interval.
880          *  @return the integral of the weighted function.
881          */
882         public double[] integrate(final UnivariateVectorFunction f,
883                                   final double lowerBound, final double upperBound) {
884 
885             final double[] adaptedPoints  = nodePoints.clone();
886             final double[] adaptedWeights = nodeWeights.clone();
887             transform(adaptedPoints, adaptedWeights, lowerBound, upperBound);
888             return basicIntegrate(f, adaptedPoints, adaptedWeights);
889         }
890 
891         /** Performs a change of variable so that the integration
892          *  can be performed on an arbitrary interval {@code [a, b]}.
893          *  <p>
894          *  It is assumed that the natural interval is {@code [-1, 1]}.
895          *  </p>
896          *
897          * @param points  Points to adapt to the new interval.
898          * @param weights Weights to adapt to the new interval.
899          * @param a Lower bound of the integration interval.
900          * @param b Lower bound of the integration interval.
901          */
902         private void transform(final double[] points, final double[] weights,
903                                final double a, final double b) {
904             // Scaling
905             final double scale = (b - a) / 2;
906             final double shift = a + scale;
907             for (int i = 0; i < points.length; i++) {
908                 points[i]   = points[i] * scale + shift;
909                 weights[i] *= scale;
910             }
911         }
912 
913         /** Returns an estimate of the integral of {@code f(x) * w(x)},
914          *  where {@code w} is a weight function that depends on the actual
915          *  flavor of the Gauss integration scheme.
916          *
917          * @param f Function to integrate.
918          * @param points  Nodes.
919          * @param weights Nodes weights.
920          * @return the integral of the weighted function.
921          */
922         private double[] basicIntegrate(final UnivariateVectorFunction f,
923                                         final double[] points,
924                                         final double[] weights) {
925             double x = points[0];
926             double w = weights[0];
927             double[] v = f.value(x);
928             final double[] y = new double[v.length];
929             for (int j = 0; j < v.length; j++) {
930                 y[j] = w * v[j];
931             }
932             final double[] t = y.clone();
933             final double[] c = new double[v.length];
934             final double[] s = t.clone();
935             for (int i = 1; i < points.length; i++) {
936                 x = points[i];
937                 w = weights[i];
938                 v = f.value(x);
939                 for (int j = 0; j < v.length; j++) {
940                     y[j] = w * v[j] - c[j];
941                     t[j] =  s[j] + y[j];
942                     c[j] = (t[j] - s[j]) - y[j];
943                     s[j] = t[j];
944                 }
945             }
946             return s;
947         }
948 
949     }
950 }