1   /* Copyright 2002-2015 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.integration;
18  
19  import java.util.ArrayList;
20  import java.util.Collection;
21  import java.util.Collections;
22  import java.util.HashMap;
23  import java.util.List;
24  import java.util.Map;
25  
26  import org.apache.commons.math3.exception.MathIllegalArgumentException;
27  import org.apache.commons.math3.exception.MathIllegalStateException;
28  import org.apache.commons.math3.ode.AbstractIntegrator;
29  import org.apache.commons.math3.ode.ContinuousOutputModel;
30  import org.apache.commons.math3.ode.EquationsMapper;
31  import org.apache.commons.math3.ode.ExpandableStatefulODE;
32  import org.apache.commons.math3.ode.FirstOrderDifferentialEquations;
33  import org.apache.commons.math3.ode.SecondaryEquations;
34  import org.apache.commons.math3.ode.sampling.StepHandler;
35  import org.apache.commons.math3.ode.sampling.StepInterpolator;
36  import org.apache.commons.math3.util.Precision;
37  import org.orekit.attitudes.AttitudeProvider;
38  import org.orekit.errors.OrekitException;
39  import org.orekit.errors.OrekitExceptionWrapper;
40  import org.orekit.errors.OrekitMessages;
41  import org.orekit.errors.PropagationException;
42  import org.orekit.frames.Frame;
43  import org.orekit.orbits.Orbit;
44  import org.orekit.orbits.OrbitType;
45  import org.orekit.orbits.PositionAngle;
46  import org.orekit.propagation.AbstractPropagator;
47  import org.orekit.propagation.BoundedPropagator;
48  import org.orekit.propagation.SpacecraftState;
49  import org.orekit.propagation.events.EventDetector;
50  import org.orekit.propagation.events.handlers.EventHandler;
51  import org.orekit.propagation.sampling.OrekitStepHandler;
52  import org.orekit.propagation.sampling.OrekitStepInterpolator;
53  import org.orekit.time.AbsoluteDate;
54  
55  
56  /** Common handling of {@link org.orekit.propagation.Propagator Propagator}
57   *  methods for both numerical and semi-analytical propagators.
58   *  @author Luc Maisonobe
59   */
60  public abstract class AbstractIntegratedPropagator extends AbstractPropagator {
61  
62      /** Event detectors not related to force models. */
63      private final List<EventDetector> detectors;
64  
65      /** Integrator selected by the user for the orbital extrapolation process. */
66      private final AbstractIntegrator integrator;
67  
68      /** Mode handler. */
69      private ModeHandler modeHandler;
70  
71      /** Additional equations. */
72      private List<AdditionalEquations> additionalEquations;
73  
74      /** Counter for differential equations calls. */
75      private int calls;
76  
77      /** Mapper between raw double components and space flight dynamics objects. */
78      private StateMapper stateMapper;
79  
80      /** Complete equation to be integrated. */
81      private ExpandableStatefulODE mathODE;
82  
83      /** Underlying raw rawInterpolator. */
84      private StepInterpolator mathInterpolator;
85  
86      /** Output only the mean orbit. <br/>
87       * <p>
88       * This is used only in the case of semianalitical propagators where there is a clear separation between
89       * mean and short periodic elements. It is ignored by the Numerical propagator.
90       * </p>
91       */
92      private boolean meanOrbit;
93  
94      /** Build a new instance.
95       * @param integrator numerical integrator to use for propagation.
96       * @param meanOrbit output only the mean orbit.
97       */
98      protected AbstractIntegratedPropagator(final AbstractIntegrator integrator, final boolean meanOrbit) {
99          detectors           = new ArrayList<EventDetector>();
100         additionalEquations = new ArrayList<AdditionalEquations>();
101         this.integrator     = integrator;
102         this.meanOrbit      = meanOrbit;
103     }
104 
105     /** Initialize the mapper. */
106     protected void initMapper() {
107         stateMapper = createMapper(null, Double.NaN, null, null, null, null);
108     }
109 
110     /**  {@inheritDoc} */
111     public void setAttitudeProvider(final AttitudeProvider attitudeProvider) {
112         super.setAttitudeProvider(attitudeProvider);
113         stateMapper = createMapper(stateMapper.getReferenceDate(), stateMapper.getMu(),
114                                    stateMapper.getOrbitType(), stateMapper.getPositionAngleType(),
115                                    attitudeProvider, stateMapper.getFrame());
116     }
117 
118     /** Set propagation orbit type.
119      * @param orbitType orbit type to use for propagation
120      */
121     protected void setOrbitType(final OrbitType orbitType) {
122         stateMapper = createMapper(stateMapper.getReferenceDate(), stateMapper.getMu(),
123                                    orbitType, stateMapper.getPositionAngleType(),
124                                    stateMapper.getAttitudeProvider(), stateMapper.getFrame());
125     }
126 
127     /** Get propagation parameter type.
128      * @return orbit type used for propagation
129      */
130     protected OrbitType getOrbitType() {
131         return stateMapper.getOrbitType();
132     }
133 
134     /** Check if only the mean elements should be used in a semianalitical propagation.
135      * @return true if only mean elements have to be used
136      */
137     protected boolean isMeanOrbit() {
138         return meanOrbit;
139     }
140 
141     /** Set position angle type.
142      * <p>
143      * The position parameter type is meaningful only if {@link
144      * #getOrbitType() propagation orbit type}
145      * support it. As an example, it is not meaningful for propagation
146      * in {@link OrbitType#CARTESIAN Cartesian} parameters.
147      * </p>
148      * @param positionAngleType angle type to use for propagation
149      */
150     protected void setPositionAngleType(final PositionAngle positionAngleType) {
151         stateMapper = createMapper(stateMapper.getReferenceDate(), stateMapper.getMu(),
152                                    stateMapper.getOrbitType(), positionAngleType,
153                                    stateMapper.getAttitudeProvider(), stateMapper.getFrame());
154     }
155 
156     /** Get propagation parameter type.
157      * @return angle type to use for propagation
158      */
159     protected PositionAngle getPositionAngleType() {
160         return stateMapper.getPositionAngleType();
161     }
162 
163     /** Set the central attraction coefficient μ.
164      * @param mu central attraction coefficient (m³/s²)
165      */
166     public void setMu(final double mu) {
167         stateMapper = createMapper(stateMapper.getReferenceDate(), mu,
168                                    stateMapper.getOrbitType(), stateMapper.getPositionAngleType(),
169                                    stateMapper.getAttitudeProvider(), stateMapper.getFrame());
170     }
171 
172     /** Get the central attraction coefficient μ.
173      * @return mu central attraction coefficient (m³/s²)
174      * @see #setMu(double)
175      */
176     public double getMu() {
177         return stateMapper.getMu();
178     }
179 
180     /** Get the number of calls to the differential equations computation method.
181      * <p>The number of calls is reset each time the {@link #propagate(AbsoluteDate)}
182      * method is called.</p>
183      * @return number of calls to the differential equations computation method
184      */
185     public int getCalls() {
186         return calls;
187     }
188 
189     /** {@inheritDoc} */
190     @Override
191     public boolean isAdditionalStateManaged(final String name) {
192 
193         // first look at already integrated states
194         if (super.isAdditionalStateManaged(name)) {
195             return true;
196         }
197 
198         // then look at states we integrate ourselves
199         for (final AdditionalEquations equation : additionalEquations) {
200             if (equation.getName().equals(name)) {
201                 return true;
202             }
203         }
204 
205         return false;
206     }
207 
208     /** {@inheritDoc} */
209     @Override
210     public String[] getManagedAdditionalStates() {
211         final String[] alreadyIntegrated = super.getManagedAdditionalStates();
212         final String[] managed = new String[alreadyIntegrated.length + additionalEquations.size()];
213         System.arraycopy(alreadyIntegrated, 0, managed, 0, alreadyIntegrated.length);
214         for (int i = 0; i < additionalEquations.size(); ++i) {
215             managed[i + alreadyIntegrated.length] = additionalEquations.get(i).getName();
216         }
217         return managed;
218     }
219 
220     /** Add a set of user-specified equations to be integrated along with the orbit propagation.
221      * @param additional additional equations
222      * @exception OrekitException if a set of equations with the same name is already present
223      */
224     public void addAdditionalEquations(final AdditionalEquations additional)
225         throws OrekitException {
226 
227         // check if the name is already used
228         if (isAdditionalStateManaged(additional.getName())) {
229             // this set of equations is already registered, complain
230             throw new OrekitException(OrekitMessages.ADDITIONAL_STATE_NAME_ALREADY_IN_USE,
231                                       additional.getName());
232         }
233 
234         // this is really a new set of equations, add it
235         additionalEquations.add(additional);
236 
237     }
238 
239     /** {@inheritDoc} */
240     public void addEventDetector(final EventDetector detector) {
241         detectors.add(detector);
242     }
243 
244     /** {@inheritDoc} */
245     public Collection<EventDetector> getEventsDetectors() {
246         return Collections.unmodifiableCollection(detectors);
247     }
248 
249     /** {@inheritDoc} */
250     public void clearEventsDetectors() {
251         detectors.clear();
252     }
253 
254     /** Set up all user defined event detectors.
255      */
256     protected void setUpUserEventDetectors() {
257         for (final EventDetector detector : detectors) {
258             setUpEventDetector(integrator, detector);
259         }
260     }
261 
262     /** Wrap an Orekit event detector and register it to the integrator.
263      * @param integ integrator into which event detector should be registered
264      * @param detector event detector to wrap
265      */
266     protected void setUpEventDetector(final AbstractIntegrator integ, final EventDetector detector) {
267         integ.addEventHandler(new AdaptedEventDetector(detector),
268                               detector.getMaxCheckInterval(),
269                               detector.getThreshold(),
270                               detector.getMaxIterationCount());
271     }
272 
273     /** {@inheritDoc}
274      * <p>Note that this method has the side effect of replacing the step handlers
275      * of the underlying integrator set up in the {@link
276      * #AbstractIntegratedPropagator(AbstractIntegrator, boolean) constructor}. So if a specific
277      * step handler is needed, it should be added after this method has been callled.</p>
278      */
279     public void setSlaveMode() {
280         super.setSlaveMode();
281         if (integrator != null) {
282             integrator.clearStepHandlers();
283         }
284         modeHandler = null;
285     }
286 
287     /** {@inheritDoc}
288      * <p>Note that this method has the side effect of replacing the step handlers
289      * of the underlying integrator set up in the {@link
290      * #AbstractIntegratedPropagator(AbstractIntegrator, boolean) constructor}. So if a specific
291      * step handler is needed, it should be added after this method has been callled.</p>
292      */
293     public void setMasterMode(final OrekitStepHandler handler) {
294         super.setMasterMode(handler);
295         integrator.clearStepHandlers();
296         final AdaptedStepHandler wrapped = new AdaptedStepHandler(handler);
297         integrator.addStepHandler(wrapped);
298         modeHandler = wrapped;
299     }
300 
301     /** {@inheritDoc}
302      * <p>Note that this method has the side effect of replacing the step handlers
303      * of the underlying integrator set up in the {@link
304      * #AbstractIntegratedPropagator(AbstractIntegrator, boolean) constructor}. So if a specific
305      * step handler is needed, it should be added after this method has been called.</p>
306      */
307     public void setEphemerisMode() {
308         super.setEphemerisMode();
309         integrator.clearStepHandlers();
310         final EphemerisModeHandler ephemeris = new EphemerisModeHandler();
311         modeHandler = ephemeris;
312         integrator.addStepHandler(ephemeris);
313     }
314 
315     /** {@inheritDoc} */
316     public BoundedPropagator getGeneratedEphemeris()
317         throws IllegalStateException {
318         if (getMode() != EPHEMERIS_GENERATION_MODE) {
319             throw OrekitException.createIllegalStateException(OrekitMessages.PROPAGATOR_NOT_IN_EPHEMERIS_GENERATION_MODE);
320         }
321         return ((EphemerisModeHandler) modeHandler).getEphemeris();
322     }
323 
324     /** Create a mapper between raw double components and spacecraft state.
325     /** Simple constructor.
326      * <p>
327      * The position parameter type is meaningful only if {@link
328      * #getOrbitType() propagation orbit type}
329      * support it. As an example, it is not meaningful for propagation
330      * in {@link OrbitType#CARTESIAN Cartesian} parameters.
331      * </p>
332      * @param referenceDate reference date
333      * @param mu central attraction coefficient (m³/s²)
334      * @param orbitType orbit type to use for mapping
335      * @param positionAngleType angle type to use for propagation
336      * @param attitudeProvider attitude provider
337      * @param frame inertial frame
338      * @return new mapper
339      */
340     protected abstract StateMapper createMapper(final AbsoluteDate referenceDate, final double mu,
341                                                 final OrbitType orbitType, final PositionAngle positionAngleType,
342                                                 final AttitudeProvider attitudeProvider, final Frame frame);
343 
344     /** Get the differential equations to integrate (for main state only).
345      * @param integ numerical integrator to use for propagation.
346      * @return differential equations for main state
347      */
348     protected abstract MainStateEquations getMainStateEquations(final AbstractIntegrator integ);
349 
350     /** {@inheritDoc} */
351     public SpacecraftState propagate(final AbsoluteDate target) throws PropagationException {
352         try {
353             if (getStartDate() == null) {
354                 if (getInitialState() == null) {
355                     throw new PropagationException(OrekitMessages.INITIAL_STATE_NOT_SPECIFIED_FOR_ORBIT_PROPAGATION);
356                 }
357                 setStartDate(getInitialState().getDate());
358             }
359             return propagate(getStartDate(), target);
360         } catch (OrekitException oe) {
361 
362             // recover a possible embedded PropagationException
363             for (Throwable t = oe; t != null; t = t.getCause()) {
364                 if (t instanceof PropagationException) {
365                     throw (PropagationException) t;
366                 }
367             }
368             throw new PropagationException(oe);
369 
370         }
371     }
372 
373     /** {@inheritDoc} */
374     public SpacecraftState propagate(final AbsoluteDate tStart, final AbsoluteDate tEnd)
375         throws PropagationException {
376         try {
377 
378             if (getInitialState() == null) {
379                 throw new PropagationException(OrekitMessages.INITIAL_STATE_NOT_SPECIFIED_FOR_ORBIT_PROPAGATION);
380             }
381 
382             if (!tStart.equals(getInitialState().getDate())) {
383                 // if propagation start date is not initial date,
384                 // propagate from initial to start date without event detection
385                 propagate(tStart, false);
386             }
387 
388             // propagate from start date to end date with event detection
389             return propagate(tEnd, true);
390 
391         } catch (OrekitException oe) {
392 
393             // recover a possible embedded PropagationException
394             for (Throwable t = oe; t != null; t = t.getCause()) {
395                 if (t instanceof PropagationException) {
396                     throw (PropagationException) t;
397                 }
398             }
399             throw new PropagationException(oe);
400 
401         }
402     }
403 
404     /** Propagation with or without event detection.
405      * @param tEnd target date to which orbit should be propagated
406      * @param activateHandlers if true, step and event handlers should be activated
407      * @return state at end of propagation
408      * @exception PropagationException if orbit cannot be propagated
409      */
410     protected SpacecraftState propagate(final AbsoluteDate tEnd, final boolean activateHandlers)
411         throws PropagationException {
412         try {
413 
414             if (getInitialState().getDate().equals(tEnd)) {
415                 // don't extrapolate
416                 return getInitialState();
417             }
418 
419             // space dynamics view
420             stateMapper = createMapper(getInitialState().getDate(), stateMapper.getMu(),
421                                        stateMapper.getOrbitType(), stateMapper.getPositionAngleType(),
422                                        stateMapper.getAttitudeProvider(), getInitialState().getFrame());
423 
424 
425             // set propagation orbit type
426             final Orbit initialOrbit = stateMapper.getOrbitType().convertType(getInitialState().getOrbit());
427             if (Double.isNaN(getMu())) {
428                 setMu(initialOrbit.getMu());
429             }
430 
431             if (getInitialState().getMass() <= 0.0) {
432                 throw new PropagationException(OrekitMessages.SPACECRAFT_MASS_BECOMES_NEGATIVE,
433                                                getInitialState().getMass());
434             }
435 
436             integrator.clearEventHandlers();
437 
438             // set up events added by user
439             setUpUserEventDetectors();
440 
441             // convert space flight dynamics API to math API
442             mathODE = createODE(integrator);
443             mathInterpolator = null;
444 
445             // initialize mode handler
446             if (modeHandler != null) {
447                 modeHandler.initialize(activateHandlers);
448             }
449 
450             // mathematical integration
451             try {
452                 beforeIntegration(getInitialState(), tEnd);
453                 integrator.integrate(mathODE, tEnd.durationFrom(getInitialState().getDate()));
454                 afterIntegration();
455             } catch (OrekitExceptionWrapper oew) {
456                 throw oew.getException();
457             }
458 
459             // get final state
460             SpacecraftState finalState =
461                     stateMapper.mapArrayToState(mathODE.getTime(), mathODE.getPrimaryState(), meanOrbit);
462             finalState = updateAdditionalStates(finalState);
463             for (int i = 0; i < additionalEquations.size(); ++i) {
464                 final double[] secondary = mathODE.getSecondaryState(i);
465                 finalState = finalState.addAdditionalState(additionalEquations.get(i).getName(),
466                                                            secondary);
467             }
468             resetInitialState(finalState);
469             setStartDate(finalState.getDate());
470 
471             return finalState;
472 
473         } catch (PropagationException pe) {
474             throw pe;
475         } catch (OrekitException oe) {
476             throw new PropagationException(oe);
477         } catch (MathIllegalArgumentException miae) {
478             throw PropagationException.unwrap(miae);
479         } catch (MathIllegalStateException mise) {
480             throw PropagationException.unwrap(mise);
481         }
482     }
483 
484     /** Create an ODE with all equations.
485      * @param integ numerical integrator to use for propagation.
486      * @return a new ode
487      * @exception OrekitException if initial state cannot be mapped
488      */
489     private ExpandableStatefulODE createODE(final AbstractIntegrator integ)
490         throws OrekitException {
491 
492         // retrieve initial state
493         final double[] initialStateVector  = new double[getBasicDimension()];
494         stateMapper.mapStateToArray(getInitialState(), initialStateVector);
495 
496         // main part of the ODE
497         final ExpandableStatefulODE ode =
498                 new ExpandableStatefulODE(new ConvertedMainStateEquations(getMainStateEquations(integ)));
499         ode.setTime(0.0);
500         ode.setPrimaryState(initialStateVector);
501 
502         // secondary part of the ODE
503         for (int i = 0; i < additionalEquations.size(); ++i) {
504             final AdditionalEquations additional = additionalEquations.get(i);
505             final double[] data = getInitialState().getAdditionalState(additional.getName());
506             final SecondaryEquations secondary =
507                     new ConvertedSecondaryStateEquations(additional, data.length);
508             ode.addSecondaryEquations(secondary);
509             ode.setSecondaryState(i, data);
510         }
511 
512         return ode;
513 
514     }
515 
516     /** Method called just before integration.
517      * <p>
518      * The default implementation does nothing, it may be specialized in subclasses.
519      * </p>
520      * @param initialState initial state
521      * @param tEnd target date at which state should be propagated
522      * @exception OrekitException if hook cannot be run
523      */
524     protected void beforeIntegration(final SpacecraftState initialState,
525                                      final AbsoluteDate tEnd)
526         throws OrekitException {
527         // do nothing by default
528     }
529 
530     /** Method called just after integration.
531      * <p>
532      * The default implementation does nothing, it may be specialized in subclasses.
533      * </p>
534      * @exception OrekitException if hook cannot be run
535      */
536     protected void afterIntegration()
537         throws OrekitException {
538         // do nothing by default
539     }
540 
541     /** Get state vector dimension without additional parameters.
542      * @return state vector dimension without additional parameters.
543      */
544     public int getBasicDimension() {
545         return 7;
546 
547     }
548 
549     /** Get the integrator used by the propagator.
550      * @return the integrator.
551      */
552     protected AbstractIntegrator getIntegrator() {
553         return integrator;
554     }
555 
556     /** Get a complete state with all additional equations.
557      * @param t current value of the independent <I>time</I> variable
558      * @param y array containing the current value of the state vector
559      * @return complete state
560      * @exception OrekitException if state cannot be mapped
561      */
562     private SpacecraftState getCompleteState(final double t, final double[] y)
563         throws OrekitException {
564 
565         // main state
566         SpacecraftState state = stateMapper.mapArrayToState(t, y, true);  //not sure of the mean orbit, should be true
567 
568         // pre-integrated additional states
569         state = updateAdditionalStates(state);
570 
571         // additional states integrated here
572         if (!additionalEquations.isEmpty()) {
573 
574             final EquationsMapper[] em = mathODE.getSecondaryMappers();
575             for (int i = 0; i < additionalEquations.size(); ++i) {
576                 final double[] secondary = new double[em[i].getDimension()];
577                 System.arraycopy(y, em[i].getFirstIndex(), secondary, 0, secondary.length);
578                 state = state.addAdditionalState(additionalEquations.get(i).getName(), secondary);
579             }
580 
581         }
582 
583         return state;
584 
585     }
586 
587     /** Differential equations for the main state (orbit, attitude and mass). */
588     public interface MainStateEquations {
589 
590         /** Compute differential equations for main state.
591          * @param state current state
592          * @return derivatives of main state
593          * @throws OrekitException if differentials cannot be computed
594          */
595         double[] computeDerivatives(final SpacecraftState state) throws OrekitException;
596 
597     }
598 
599     /** Differential equations for the main state (orbit, attitude and mass), with converted API. */
600     private class ConvertedMainStateEquations implements FirstOrderDifferentialEquations {
601 
602         /** Main state equations. */
603         private final MainStateEquations main;
604 
605         /** Simple constructor.
606          * @param main main state equations
607          */
608         public ConvertedMainStateEquations(final MainStateEquations main) {
609             this.main = main;
610             calls = 0;
611         }
612 
613         /** {@inheritDoc} */
614         public int getDimension() {
615             return getBasicDimension();
616         }
617 
618         /** {@inheritDoc} */
619         public void computeDerivatives(final double t, final double[] y, final double[] yDot)
620             throws OrekitExceptionWrapper {
621 
622             try {
623 
624                 // update space dynamics view
625                 // use only ODE elements
626                 SpacecraftState currentState = stateMapper.mapArrayToState(t, y, true);
627                 currentState = updateAdditionalStates(currentState);
628 
629                 // compute main state differentials
630                 final double[] mainDot = main.computeDerivatives(currentState);
631                 System.arraycopy(mainDot, 0, yDot, 0, mainDot.length);
632 
633             } catch (OrekitException oe) {
634                 throw new OrekitExceptionWrapper(oe);
635             }
636 
637 
638             // increment calls counter
639             ++calls;
640 
641         }
642 
643     }
644 
645     /** Differential equations for the secondary state (Jacobians, user variables ...), with converted API. */
646     private class ConvertedSecondaryStateEquations implements SecondaryEquations {
647 
648         /** Additional equations. */
649         private final AdditionalEquations equations;
650 
651         /** Dimension of the additional state. */
652         private final int dimension;
653 
654         /** Simple constructor.
655          * @param equations additional equations
656          * @param dimension dimension of the additional state
657          */
658         public ConvertedSecondaryStateEquations(final AdditionalEquations equations,
659                                                 final int dimension) {
660             this.equations = equations;
661             this.dimension = dimension;
662         }
663 
664         /** {@inheritDoc} */
665         public int getDimension() {
666             return dimension;
667         }
668 
669         /** {@inheritDoc} */
670         public void computeDerivatives(final double t, final double[] primary,
671                                        final double[] primaryDot, final double[] secondary,
672                                        final double[] secondaryDot)
673             throws OrekitExceptionWrapper {
674 
675             try {
676 
677                 // update space dynamics view
678                 // the state contains only the ODE elements
679                 SpacecraftState currentState = stateMapper.mapArrayToState(t, primary, true);
680                 currentState = updateAdditionalStates(currentState);
681                 currentState = currentState.addAdditionalState(equations.getName(), secondary);
682 
683                 // compute additional derivatives
684                 final double[] additionalMainDot =
685                         equations.computeDerivatives(currentState, secondaryDot);
686                 if (additionalMainDot != null) {
687                     // the additional equations have an effect on main equations
688                     for (int i = 0; i < additionalMainDot.length; ++i) {
689                         primaryDot[i] += additionalMainDot[i];
690                     }
691                 }
692 
693             } catch (OrekitException oe) {
694                 throw new OrekitExceptionWrapper(oe);
695             }
696 
697         }
698 
699     }
700 
701     /** Adapt an {@link org.orekit.propagation.events.EventDetector}
702      * to commons-math {@link org.apache.commons.math3.ode.events.EventHandler} interface.
703      * @param <T> class type for the generic version
704      * @author Fabien Maussion
705      */
706     private class AdaptedEventDetector
707         implements org.apache.commons.math3.ode.events.EventHandler {
708 
709         /** Underlying event detector. */
710         private final EventDetector detector;
711 
712         /** Time of the previous call to g. */
713         private double lastT;
714 
715         /** Value from the previous call to g. */
716         private double lastG;
717 
718         /** Build a wrapped event detector.
719          * @param detector event detector to wrap
720         */
721         public AdaptedEventDetector(final EventDetector detector) {
722             this.detector = detector;
723             this.lastT    = Double.NaN;
724             this.lastG    = Double.NaN;
725         }
726 
727         /** {@inheritDoc} */
728         public void init(final double t0, final double[] y0, final double t) {
729             try {
730 
731                 detector.init(getCompleteState(t0, y0), stateMapper.mapDoubleToDate(t));
732                 this.lastT = Double.NaN;
733                 this.lastG = Double.NaN;
734 
735             } catch (OrekitException oe) {
736                 throw new OrekitExceptionWrapper(oe);
737             }
738         }
739 
740         /** {@inheritDoc} */
741         public double g(final double t, final double[] y) {
742             try {
743                 if (!Precision.equals(lastT, t, 1)) {
744                     lastT = t;
745                     lastG = detector.g(getCompleteState(t, y));
746                 }
747                 return lastG;
748             } catch (OrekitException oe) {
749                 throw new OrekitExceptionWrapper(oe);
750             }
751         }
752 
753         /** {@inheritDoc} */
754         public Action eventOccurred(final double t, final double[] y, final boolean increasing) {
755             try {
756 
757                 final EventHandler.Action whatNext = detector.eventOccurred(getCompleteState(t, y), increasing);
758 
759                 switch (whatNext) {
760                 case STOP :
761                     return Action.STOP;
762                 case RESET_STATE :
763                     return Action.RESET_STATE;
764                 case RESET_DERIVATIVES :
765                     return Action.RESET_DERIVATIVES;
766                 default :
767                     return Action.CONTINUE;
768                 }
769             } catch (OrekitException oe) {
770                 throw new OrekitExceptionWrapper(oe);
771             }
772         }
773 
774         /** {@inheritDoc} */
775         public void resetState(final double t, final double[] y) {
776             try {
777                 final SpacecraftState newState = detector.resetState(getCompleteState(t, y));
778 
779                 // main part
780                 stateMapper.mapStateToArray(newState, y);
781 
782                 // secondary part
783                 final EquationsMapper[] em = mathODE.getSecondaryMappers();
784                 for (int i = 0; i < additionalEquations.size(); ++i) {
785                     final double[] secondary =
786                             newState.getAdditionalState(additionalEquations.get(i).getName());
787                     System.arraycopy(secondary, 0, y, em[i].getFirstIndex(), secondary.length);
788                 }
789 
790             } catch (OrekitException oe) {
791                 throw new OrekitExceptionWrapper(oe);
792             }
793         }
794 
795     }
796 
797     /** Adapt an {@link org.orekit.propagation.sampling.OrekitStepHandler}
798      * to commons-math {@link StepHandler} interface.
799      * @author Luc Maisonobe
800      */
801     private class AdaptedStepHandler
802         implements OrekitStepInterpolator, StepHandler, ModeHandler {
803 
804         /** Underlying handler. */
805         private final OrekitStepHandler handler;
806 
807         /** Flag for handler . */
808         private boolean activate;
809 
810         /** Build an instance.
811          * @param handler underlying handler to wrap
812          */
813         public AdaptedStepHandler(final OrekitStepHandler handler) {
814             this.handler = handler;
815         }
816 
817         /** {@inheritDoc} */
818         public void initialize(final boolean activateHandlers) {
819             this.activate = activateHandlers;
820         }
821 
822         /** {@inheritDoc} */
823         public void init(final double t0, final double[] y0, final double t) {
824             try {
825                 handler.init(getCompleteState(t0, y0), stateMapper.mapDoubleToDate(t));
826             } catch (OrekitException oe) {
827                 throw new OrekitExceptionWrapper(oe);
828             }
829         }
830 
831         /** {@inheritDoc} */
832         public void handleStep(final StepInterpolator interpolator, final boolean isLast) {
833             try {
834                 mathInterpolator = interpolator;
835                 if (activate) {
836                     handler.handleStep(this, isLast);
837                 }
838             } catch (PropagationException pe) {
839                 throw new OrekitExceptionWrapper(pe);
840             }
841         }
842 
843         /** Get the current grid date.
844          * @return current grid date
845          */
846         public AbsoluteDate getCurrentDate() {
847             return stateMapper.mapDoubleToDate(mathInterpolator.getCurrentTime());
848         }
849 
850         /** Get the previous grid date.
851          * @return previous grid date
852          */
853         public AbsoluteDate getPreviousDate() {
854             return stateMapper.mapDoubleToDate(mathInterpolator.getPreviousTime());
855         }
856 
857         /** Get the interpolated date.
858          * <p>If {@link #setInterpolatedDate(AbsoluteDate) setInterpolatedDate}
859          * has not been called, the date returned is the same as  {@link
860          * #getCurrentDate() getCurrentDate}.</p>
861          * @return interpolated date
862          * @see #setInterpolatedDate(AbsoluteDate)
863          * @see #getInterpolatedState()
864          */
865         public AbsoluteDate getInterpolatedDate() {
866             return stateMapper.mapDoubleToDate(mathInterpolator.getInterpolatedTime());
867         }
868 
869         /** Set the interpolated date.
870          * <p>It is possible to set the interpolation date outside of the current
871          * step range, but accuracy will decrease as date is farther.</p>
872          * @param date interpolated date to set
873          * @see #getInterpolatedDate()
874          * @see #getInterpolatedState()
875          */
876         public void setInterpolatedDate(final AbsoluteDate date) {
877             mathInterpolator.setInterpolatedTime(stateMapper.mapDateToDouble(date));
878         }
879 
880         /** Get the interpolated state.
881          * @return interpolated state at the current interpolation date
882          * @exception OrekitException if state cannot be interpolated or converted
883          * @see #getInterpolatedDate()
884          * @see #setInterpolatedDate(AbsoluteDate)
885          */
886         public SpacecraftState getInterpolatedState() throws OrekitException {
887             try {
888 
889                 SpacecraftState s =
890                         stateMapper.mapArrayToState(mathInterpolator.getInterpolatedTime(),
891                                                     mathInterpolator.getInterpolatedState(),
892                                                     meanOrbit);
893                 s = updateAdditionalStates(s);
894                 for (int i = 0; i < additionalEquations.size(); ++i) {
895                     final double[] secondary = mathInterpolator.getInterpolatedSecondaryState(i);
896                     s = s.addAdditionalState(additionalEquations.get(i).getName(), secondary);
897                 }
898 
899                 return s;
900 
901             } catch (OrekitExceptionWrapper oew) {
902                 throw oew.getException();
903             }
904         }
905 
906         /** Check is integration direction is forward in date.
907          * @return true if integration is forward in date
908          */
909         public boolean isForward() {
910             return mathInterpolator.isForward();
911         }
912 
913     }
914 
915     private class EphemerisModeHandler implements ModeHandler, StepHandler {
916 
917         /** Underlying raw mathematical model. */
918         private ContinuousOutputModel model;
919 
920         /** Generated ephemeris. */
921         private BoundedPropagator ephemeris;
922 
923         /** Flag for handler . */
924         private boolean activate;
925 
926         /** Creates a new instance of EphemerisModeHandler which must be
927          *  filled by the propagator.
928          */
929         public EphemerisModeHandler() {
930         }
931 
932         /** {@inheritDoc} */
933         public void initialize(final boolean activateHandlers) {
934             this.activate = activateHandlers;
935             this.model    = new ContinuousOutputModel();
936 
937             // ephemeris will be generated when last step is processed
938             this.ephemeris = null;
939 
940         }
941 
942         /** Get the generated ephemeris.
943          * @return a new instance of the generated ephemeris
944          */
945         public BoundedPropagator getEphemeris() {
946             return ephemeris;
947         }
948 
949         /** {@inheritDoc} */
950         public void handleStep(final StepInterpolator interpolator, final boolean isLast)
951             throws OrekitExceptionWrapper {
952             try {
953                 if (activate) {
954                     model.handleStep(interpolator, isLast);
955                     if (isLast) {
956 
957                         // set up the boundary dates
958                         final double tI = model.getInitialTime();
959                         final double tF = model.getFinalTime();
960                         final AbsoluteDate startDate = stateMapper.mapDoubleToDate(tI);
961                         final AbsoluteDate minDate;
962                         final AbsoluteDate maxDate;
963                         if (tF < tI) {
964                             minDate = stateMapper.mapDoubleToDate(tF);
965                             maxDate = startDate;
966                         } else {
967                             minDate = startDate;
968                             maxDate = stateMapper.mapDoubleToDate(tF);
969                         }
970 
971                         // get the initial additional states that are not managed
972                         final Map<String, double[]> unmanaged = new HashMap<String, double[]>();
973                         for (final Map.Entry<String, double[]> initial : getInitialState().getAdditionalStates().entrySet()) {
974                             if (!isAdditionalStateManaged(initial.getKey())) {
975                                 // this additional state was in the initial state, but is unknown to the propagator
976                                 // we simply copy its initial value as is
977                                 unmanaged.put(initial.getKey(), initial.getValue());
978                             }
979                         }
980 
981                         // get the names of additional states managed by differential equations
982                         final String[] names = new String[additionalEquations.size()];
983                         for (int i = 0; i < names.length; ++i) {
984                             names[i] = additionalEquations.get(i).getName();
985                         }
986 
987                         // create the ephemeris
988                         ephemeris = new IntegratedEphemeris(startDate, minDate, maxDate,
989                                                             stateMapper, meanOrbit, model, unmanaged,
990                                                             getAdditionalStateProviders(), names);
991 
992                     }
993                 }
994             } catch (OrekitException oe) {
995                 throw new OrekitExceptionWrapper(oe);
996             }
997         }
998 
999         /** {@inheritDoc} */
1000         public void init(final double t0, final double[] y0, final double t) {
1001             model.init(t0, y0, t);
1002         }
1003 
1004     }
1005 
1006 }