AbstractFieldIntegrator.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF 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.  *      https://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. /*
  18.  * This is not the original file distributed by the Apache Software Foundation
  19.  * It has been modified by the Hipparchus project
  20.  */

  21. package org.hipparchus.ode;

  22. import java.util.ArrayList;
  23. import java.util.Collections;
  24. import java.util.Comparator;
  25. import java.util.List;
  26. import java.util.PriorityQueue;
  27. import java.util.Queue;
  28. import java.util.stream.Collectors;
  29. import java.util.stream.Stream;

  30. import org.hipparchus.CalculusFieldElement;
  31. import org.hipparchus.Field;
  32. import org.hipparchus.exception.MathIllegalArgumentException;
  33. import org.hipparchus.exception.MathIllegalStateException;
  34. import org.hipparchus.ode.events.Action;
  35. import org.hipparchus.ode.events.FieldDetectorBasedEventState;
  36. import org.hipparchus.ode.events.FieldEventOccurrence;
  37. import org.hipparchus.ode.events.FieldEventState;
  38. import org.hipparchus.ode.events.FieldODEEventDetector;
  39. import org.hipparchus.ode.events.FieldODEStepEndHandler;
  40. import org.hipparchus.ode.events.FieldStepEndEventState;
  41. import org.hipparchus.ode.sampling.AbstractFieldODEStateInterpolator;
  42. import org.hipparchus.ode.sampling.FieldODEStepHandler;
  43. import org.hipparchus.util.FastMath;
  44. import org.hipparchus.util.Incrementor;

  45. /**
  46.  * Base class managing common boilerplate for all integrators.
  47.  * @param <T> the type of the field elements
  48.  */
  49. public abstract class AbstractFieldIntegrator<T extends CalculusFieldElement<T>> implements FieldODEIntegrator<T> {

  50.     /** Step handler. */
  51.     private final List<FieldODEStepHandler<T>> stepHandlers;

  52.     /** Current step start. */
  53.     private FieldODEStateAndDerivative<T> stepStart;

  54.     /** Current stepsize. */
  55.     private T stepSize;

  56.     /** Indicator for last step. */
  57.     private boolean isLastStep;

  58.     /** Indicator that a state or derivative reset was triggered by some event. */
  59.     private boolean resetOccurred;

  60.     /** Field to which the time and state vector elements belong. */
  61.     private final Field<T> field;

  62.     /** Events states. */
  63.     private final List<FieldDetectorBasedEventState<T>> detectorBasedEventsStates;

  64.     /** Events states related to step end. */
  65.     private final List<FieldStepEndEventState<T>> stepEndEventsStates;

  66.     /** Initialization indicator of events states. */
  67.     private boolean statesInitialized;

  68.     /** Name of the method. */
  69.     private final String name;

  70.     /** Counter for number of evaluations. */
  71.     private Incrementor evaluations;

  72.     /** Differential equations to integrate. */
  73.     private transient FieldExpandableODE<T> equations;

  74.     /** Build an instance.
  75.      * @param field field to which the time and state vector elements belong
  76.      * @param name name of the method
  77.      */
  78.     protected AbstractFieldIntegrator(final Field<T> field, final String name) {
  79.         this.field                = field;
  80.         this.name                 = name;
  81.         stepHandlers              = new ArrayList<>();
  82.         stepStart                 = null;
  83.         stepSize                  = null;
  84.         detectorBasedEventsStates = new ArrayList<>();
  85.         stepEndEventsStates       = new ArrayList<>();
  86.         statesInitialized         = false;
  87.         evaluations               = new Incrementor();
  88.     }

  89.     /** Get the field to which state vector elements belong.
  90.      * @return field to which state vector elements belong
  91.      */
  92.     public Field<T> getField() {
  93.         return field;
  94.     }

  95.     /** {@inheritDoc} */
  96.     @Override
  97.     public String getName() {
  98.         return name;
  99.     }

  100.     /** {@inheritDoc} */
  101.     @Override
  102.     public void addStepHandler(final FieldODEStepHandler<T> handler) {
  103.         stepHandlers.add(handler);
  104.     }

  105.     /** {@inheritDoc} */
  106.     @Override
  107.     public List<FieldODEStepHandler<T>> getStepHandlers() {
  108.         return Collections.unmodifiableList(stepHandlers);
  109.     }

  110.     /** {@inheritDoc} */
  111.     @Override
  112.     public void clearStepHandlers() {
  113.         stepHandlers.clear();
  114.     }

  115.     /** {@inheritDoc} */
  116.     @Override
  117.     public void addEventDetector(final FieldODEEventDetector<T> detector) {
  118.         detectorBasedEventsStates.add(new FieldDetectorBasedEventState<>(detector));
  119.     }

  120.     /** {@inheritDoc} */
  121.     @Override
  122.     public List<FieldODEEventDetector<T>> getEventDetectors() {
  123.         return detectorBasedEventsStates.stream().map(es -> es.getEventDetector()).collect(Collectors.toList());
  124.     }

  125.     /** {@inheritDoc} */
  126.     @Override
  127.     public void clearEventDetectors() {
  128.         detectorBasedEventsStates.clear();
  129.     }

  130.     /** {@inheritDoc} */
  131.     @Override
  132.     public void addStepEndHandler(FieldODEStepEndHandler<T> handler) {
  133.         stepEndEventsStates.add(new FieldStepEndEventState<>(handler));
  134.     }

  135.     /** {@inheritDoc} */
  136.     @Override
  137.     public List<FieldODEStepEndHandler<T>> getStepEndHandlers() {
  138.         return stepEndEventsStates.stream().map(es -> es.getHandler()).collect(Collectors.toList());
  139.     }

  140.     /** {@inheritDoc} */
  141.     @Override
  142.     public void clearStepEndHandlers() {
  143.         stepEndEventsStates.clear();
  144.     }

  145.     /** {@inheritDoc} */
  146.     @Override
  147.     public T getCurrentSignedStepsize() {
  148.         return stepSize;
  149.     }

  150.     /** {@inheritDoc} */
  151.     @Override
  152.     public void setMaxEvaluations(int maxEvaluations) {
  153.         evaluations = evaluations.withMaximalCount((maxEvaluations < 0) ? Integer.MAX_VALUE : maxEvaluations);
  154.     }

  155.     /** {@inheritDoc} */
  156.     @Override
  157.     public int getMaxEvaluations() {
  158.         return evaluations.getMaximalCount();
  159.     }

  160.     /** {@inheritDoc} */
  161.     @Override
  162.     public int getEvaluations() {
  163.         return evaluations.getCount();
  164.     }

  165.     /** Prepare the start of an integration.
  166.      * @param eqn equations to integrate
  167.      * @param s0 initial state vector
  168.      * @param t target time for the integration
  169.      * @return initial state with derivatives added
  170.      */
  171.     protected FieldODEStateAndDerivative<T> initIntegration(final FieldExpandableODE<T> eqn,
  172.                                                             final FieldODEState<T> s0, final T t) {

  173.         this.equations = eqn;
  174.         evaluations    = evaluations.withCount(0);

  175.         // initialize ODE
  176.         eqn.init(s0, t);

  177.         // set up derivatives of initial state (including primary and secondary components)
  178.         final T   t0    = s0.getTime();
  179.         final T[] y0    = s0.getCompleteState();
  180.         final T[] y0Dot = computeDerivatives(t0, y0);

  181.         // built the state
  182.         final FieldODEStateAndDerivative<T> s0WithDerivatives =
  183.                         eqn.getMapper().mapStateAndDerivative(t0, y0, y0Dot);

  184.         // initialize detector based event states (both  and step end based)
  185.         detectorBasedEventsStates.stream().forEach(s -> {
  186.             s.init(s0WithDerivatives, t);
  187.             s.getEventDetector().getHandler().init(s0WithDerivatives, t, s.getEventDetector());
  188.         });

  189.         // initialize step end based event states
  190.         stepEndEventsStates.stream().forEach(s -> {
  191.             s.init(s0WithDerivatives, t);
  192.             s.getHandler().init(s0WithDerivatives, t);
  193.         });

  194.         // initialize step handlers
  195.         for (FieldODEStepHandler<T> handler : stepHandlers) {
  196.             handler.init(s0WithDerivatives, t);
  197.         }

  198.         setStateInitialized(false);

  199.         return s0WithDerivatives;

  200.     }

  201.     /** Get the differential equations to integrate.
  202.      * @return differential equations to integrate
  203.      */
  204.     protected FieldExpandableODE<T> getEquations() {
  205.         return equations;
  206.     }

  207.     /** Get the evaluations counter.
  208.      * @return evaluations counter
  209.      */
  210.     protected Incrementor getEvaluationsCounter() {
  211.         return evaluations;
  212.     }

  213.     /** Compute the derivatives and check the number of evaluations.
  214.      * @param t current value of the independent <I>time</I> variable
  215.      * @param y array containing the current value of the state vector
  216.      * @return state completed with derivatives
  217.      * @exception MathIllegalArgumentException if arrays dimensions do not match equations settings
  218.      * @exception MathIllegalStateException if the number of functions evaluations is exceeded
  219.      * @exception NullPointerException if the ODE equations have not been set (i.e. if this method
  220.      * is called outside of a call to {@link #integrate(FieldExpandableODE, FieldODEState,
  221.      * CalculusFieldElement) integrate}
  222.      */
  223.     public T[] computeDerivatives(final T t, final T[] y)
  224.         throws MathIllegalArgumentException, MathIllegalStateException, NullPointerException {
  225.         evaluations.increment();
  226.         return equations.computeDerivatives(t, y);
  227.     }

  228.     /** Increment evaluations of derivatives.
  229.      *
  230.      * @param nTimes number of evaluations to increment
  231.      */
  232.     protected void incrementEvaluations(final int nTimes) {
  233.         evaluations.increment(nTimes);
  234.     }

  235.     /** Set the stateInitialized flag.
  236.      * <p>This method must be called by integrators with the value
  237.      * {@code false} before they start integration, so a proper lazy
  238.      * initialization is done automatically on the first step.</p>
  239.      * @param stateInitialized new value for the flag
  240.      */
  241.     protected void setStateInitialized(final boolean stateInitialized) {
  242.         this.statesInitialized = stateInitialized;
  243.     }

  244.     /**
  245.      * Accept a step, triggering events and step handlers.
  246.      *
  247.      * @param interpolator step interpolator
  248.      * @param tEnd         final integration time
  249.      * @return state at end of step
  250.      * @throws MathIllegalStateException    if the interpolator throws one because the
  251.      *                                      number of functions evaluations is exceeded
  252.      * @throws MathIllegalArgumentException if the location of an event cannot be
  253.      *                                      bracketed
  254.      * @throws MathIllegalArgumentException if arrays dimensions do not match equations
  255.      *                                      settings
  256.      */
  257.     protected FieldODEStateAndDerivative<T> acceptStep(final AbstractFieldODEStateInterpolator<T> interpolator,
  258.                                                        final T tEnd)
  259.             throws MathIllegalArgumentException, MathIllegalStateException {

  260.         FieldODEStateAndDerivative<T> previousState = interpolator.getGlobalPreviousState();
  261.         final FieldODEStateAndDerivative<T> currentState = interpolator.getGlobalCurrentState();
  262.         AbstractFieldODEStateInterpolator<T> restricted = interpolator;

  263.         // initialize the events states if needed
  264.         if (!statesInitialized) {
  265.             // initialize event states
  266.             detectorBasedEventsStates.stream().forEach(s -> s.reinitializeBegin(interpolator));
  267.             statesInitialized = true;
  268.         }

  269.         // set end of step
  270.         stepEndEventsStates.stream().forEach(s -> s.setStepEnd(currentState.getTime()));

  271.         // search for next events that may occur during the step
  272.         final int orderingSign = interpolator.isForward() ? +1 : -1;
  273.         final Queue<FieldEventState<T>> occurringEvents = new PriorityQueue<>(new Comparator<FieldEventState<T>>() {
  274.             /** {@inheritDoc} */
  275.             @Override
  276.             public int compare(FieldEventState<T> es0, FieldEventState<T> es1) {
  277.                 return orderingSign * Double.compare(es0.getEventTime().getReal(), es1.getEventTime().getReal());
  278.             }
  279.         });

  280.         resetOccurred = false;
  281.         boolean doneWithStep = false;
  282.         resetEvents:
  283.         do {

  284.             // Evaluate all event detectors and end steps for events
  285.             occurringEvents.clear();
  286.             final AbstractFieldODEStateInterpolator<T> finalRestricted = restricted;
  287.             Stream.concat(detectorBasedEventsStates.stream(), stepEndEventsStates.stream()).
  288.             forEach(s -> { if (s.evaluateStep(finalRestricted)) {
  289.                     // the event occurs during the current step
  290.                     occurringEvents.add(s);
  291.                 }
  292.             });


  293.             do {

  294.                 eventLoop:
  295.                 while (!occurringEvents.isEmpty()) {

  296.                     // handle the chronologically first event
  297.                     final FieldEventState<T> currentEvent = occurringEvents.poll();

  298.                     // get state at event time
  299.                     FieldODEStateAndDerivative<T> eventState =
  300.                             restricted.getInterpolatedState(currentEvent.getEventTime());

  301.                     // restrict the interpolator to the first part of the step, up to the event
  302.                     restricted = restricted.restrictStep(previousState, eventState);

  303.                     // try to advance all event states related to detectors to current time
  304.                     for (final FieldDetectorBasedEventState<T> state : detectorBasedEventsStates) {
  305.                         if (state != currentEvent && state.tryAdvance(eventState, interpolator)) {
  306.                             // we need to handle another event first
  307.                             // remove event we just updated to prevent heap corruption
  308.                             occurringEvents.remove(state);
  309.                             // add it back to update its position in the heap
  310.                             occurringEvents.add(state);
  311.                             // re-queue the event we were processing
  312.                             occurringEvents.add(currentEvent);
  313.                             continue eventLoop;
  314.                         }
  315.                     }
  316.                     // all event detectors agree we can advance to the current event time

  317.                     // handle the first part of the step, up to the event
  318.                     for (final FieldODEStepHandler<T> handler : stepHandlers) {
  319.                         handler.handleStep(restricted);
  320.                     }

  321.                     // acknowledge event occurrence
  322.                     final FieldEventOccurrence<T> occurrence = currentEvent.doEvent(eventState);
  323.                     final Action action = occurrence.getAction();
  324.                     isLastStep = action == Action.STOP;

  325.                     if (isLastStep) {

  326.                         // ensure the event is after the root if it is returned STOP
  327.                         // this lets the user integrate to a STOP event and then restart
  328.                         // integration from the same time.
  329.                         final FieldODEStateAndDerivative<T> savedState = eventState;
  330.                         eventState = interpolator.getInterpolatedState(occurrence.getStopTime());
  331.                         restricted = interpolator.restrictStep(savedState, eventState);

  332.                         // handle the almost zero size last part of the final step, at event time
  333.                         for (final FieldODEStepHandler<T> handler : stepHandlers) {
  334.                             handler.handleStep(restricted);
  335.                             handler.finish(restricted.getCurrentState());
  336.                         }

  337.                     }

  338.                     if (isLastStep) {
  339.                         // the event asked to stop integration
  340.                         return eventState;
  341.                     }

  342.                     if (action == Action.RESET_DERIVATIVES || action == Action.RESET_STATE) {
  343.                         // some event handler has triggered changes that
  344.                         // invalidate the derivatives, we need to recompute them
  345.                         final FieldODEState<T> newState = occurrence.getNewState();
  346.                         final T[] y = newState.getCompleteState();
  347.                         final T[] yDot = computeDerivatives(newState.getTime(), y);
  348.                         resetOccurred = true;
  349.                         return equations.getMapper().mapStateAndDerivative(newState.getTime(), y, yDot);
  350.                     }
  351.                     // at this point action == Action.CONTINUE or Action.RESET_EVENTS

  352.                     // prepare handling of the remaining part of the step
  353.                     previousState = eventState;
  354.                     restricted = restricted.restrictStep(eventState, currentState);

  355.                     if (action == Action.RESET_EVENTS) {
  356.                         continue resetEvents;
  357.                     }

  358.                     // at this point action == Action.CONTINUE
  359.                     // check if the same event occurs again in the remaining part of the step
  360.                     if (currentEvent.evaluateStep(restricted)) {
  361.                         // the event occurs during the current step
  362.                         occurringEvents.add(currentEvent);
  363.                     }

  364.                 }

  365.                 // last part of the step, after the last event. Advance all event
  366.                 // detectors to the end of the step. Should never find new events unless
  367.                 // a previous event modified the g function of another event detector when
  368.                 // it returned Action.CONTINUE. Detecting such events here is unreliable
  369.                 // and RESET_EVENTS should be used instead. Other option is to replace
  370.                 // tryAdvance(...) with a doAdvance(...) that throws an exception when
  371.                 // the g function sign is not as expected.
  372.                 for (final FieldDetectorBasedEventState<T> state : detectorBasedEventsStates) {
  373.                     if (state.tryAdvance(currentState, interpolator)) {
  374.                         occurringEvents.add(state);
  375.                     }
  376.                 }

  377.             } while (!occurringEvents.isEmpty());

  378.             doneWithStep = true;
  379.         } while (!doneWithStep);


  380.         isLastStep = isLastStep || currentState.getTime().subtract(tEnd).norm() < FastMath.ulp(tEnd.getReal());

  381.         // handle the remaining part of the step, after all events if any
  382.         for (FieldODEStepHandler<T> handler : stepHandlers) {
  383.             handler.handleStep(restricted);
  384.             if (isLastStep) {
  385.                 handler.finish(restricted.getCurrentState());
  386.             }
  387.         }

  388.         return currentState;

  389.     }

  390.     /** Check the integration span.
  391.      * @param initialState initial state
  392.      * @param t target time for the integration
  393.      * @exception MathIllegalArgumentException if integration span is too small
  394.      * @exception MathIllegalArgumentException if adaptive step size integrators
  395.      * tolerance arrays dimensions are not compatible with equations settings
  396.      */
  397.     protected void sanityChecks(final FieldODEState<T> initialState, final T t)
  398.         throws MathIllegalArgumentException {

  399.         final double threshold = 1000 * FastMath.ulp(FastMath.max(FastMath.abs(initialState.getTime().getReal()),
  400.                                                                   FastMath.abs(t.getReal())));
  401.         final double dt = initialState.getTime().subtract(t).norm();
  402.         if (dt < threshold) {
  403.             throw new MathIllegalArgumentException(LocalizedODEFormats.TOO_SMALL_INTEGRATION_INTERVAL,
  404.                                                    dt, threshold, false);
  405.         }

  406.     }

  407.     /** Check if a reset occurred while last step was accepted.
  408.      * @return true if a reset occurred while last step was accepted
  409.      */
  410.     protected boolean resetOccurred() {
  411.         return resetOccurred;
  412.     }

  413.     /** Set the current step size.
  414.      * @param stepSize step size to set
  415.      */
  416.     protected void setStepSize(final T stepSize) {
  417.         this.stepSize = stepSize;
  418.     }

  419.     /** Get the current step size.
  420.      * @return current step size
  421.      */
  422.     protected T getStepSize() {
  423.         return stepSize;
  424.     }

  425.     /** Set current step start.
  426.      * @param stepStart step start
  427.      */
  428.     protected void setStepStart(final FieldODEStateAndDerivative<T> stepStart) {
  429.         this.stepStart = stepStart;
  430.     }

  431.     /**  {@inheritDoc} */
  432.     @Override
  433.     public FieldODEStateAndDerivative<T> getStepStart() {
  434.         return stepStart;
  435.     }

  436.     /** Set the last state flag.
  437.      * @param isLastStep if true, this step is the last one
  438.      */
  439.     protected void setIsLastStep(final boolean isLastStep) {
  440.         this.isLastStep = isLastStep;
  441.     }

  442.     /** Check if this step is the last one.
  443.      * @return true if this step is the last one
  444.      */
  445.     protected boolean isLastStep() {
  446.         return isLastStep;
  447.     }

  448. }