AbstractIntegrator.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.exception.MathIllegalArgumentException;
  31. import org.hipparchus.exception.MathIllegalStateException;
  32. import org.hipparchus.ode.events.Action;
  33. import org.hipparchus.ode.events.DetectorBasedEventState;
  34. import org.hipparchus.ode.events.EventOccurrence;
  35. import org.hipparchus.ode.events.EventState;
  36. import org.hipparchus.ode.events.ODEEventDetector;
  37. import org.hipparchus.ode.events.ODEStepEndHandler;
  38. import org.hipparchus.ode.events.StepEndEventState;
  39. import org.hipparchus.ode.sampling.AbstractODEStateInterpolator;
  40. import org.hipparchus.ode.sampling.ODEStepHandler;
  41. import org.hipparchus.util.FastMath;
  42. import org.hipparchus.util.Incrementor;

  43. /**
  44.  * Base class managing common boilerplate for all integrators.
  45.  */
  46. public abstract class AbstractIntegrator implements ODEIntegrator {

  47.     /** Step handler. */
  48.     private List<ODEStepHandler> stepHandlers;

  49.     /** Current step start time. */
  50.     private ODEStateAndDerivative stepStart;

  51.     /** Current stepsize. */
  52.     private double stepSize;

  53.     /** Indicator for last step. */
  54.     private boolean isLastStep;

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

  57.     /** Events states related to event detectors. */
  58.     private List<DetectorBasedEventState> detectorBasedEventsStates;

  59.     /** Events states related to step end. */
  60.     private List<StepEndEventState> stepEndEventsStates;

  61.     /** Initialization indicator of events states. */
  62.     private boolean statesInitialized;

  63.     /** Name of the method. */
  64.     private final String name;

  65.     /** Counter for number of evaluations. */
  66.     private Incrementor evaluations;

  67.     /** Differential equations to integrate. */
  68.     private transient ExpandableODE equations;

  69.     /** Build an instance.
  70.      * @param name name of the method
  71.      */
  72.     protected AbstractIntegrator(final String name) {
  73.         this.name                 = name;
  74.         stepHandlers              = new ArrayList<>();
  75.         stepStart                 = null;
  76.         stepSize                  = Double.NaN;
  77.         detectorBasedEventsStates = new ArrayList<>();
  78.         stepEndEventsStates       = new ArrayList<>();
  79.         statesInitialized         = false;
  80.         evaluations               = new Incrementor();
  81.     }

  82.     /** {@inheritDoc} */
  83.     @Override
  84.     public String getName() {
  85.         return name;
  86.     }

  87.     /** {@inheritDoc} */
  88.     @Override
  89.     public void addStepHandler(final ODEStepHandler handler) {
  90.         stepHandlers.add(handler);
  91.     }

  92.     /** {@inheritDoc} */
  93.     @Override
  94.     public List<ODEStepHandler> getStepHandlers() {
  95.         return Collections.unmodifiableList(stepHandlers);
  96.     }

  97.     /** {@inheritDoc} */
  98.     @Override
  99.     public void clearStepHandlers() {
  100.         stepHandlers.clear();
  101.     }

  102.     /** {@inheritDoc} */
  103.     @Override
  104.     public void addEventDetector(final ODEEventDetector detector) {
  105.         detectorBasedEventsStates.add(new DetectorBasedEventState(detector));
  106.     }

  107.     /** {@inheritDoc} */
  108.     @Override
  109.     public List<ODEEventDetector> getEventDetectors() {
  110.         return detectorBasedEventsStates.stream().map(es -> es.getEventDetector()).collect(Collectors.toList());
  111.     }

  112.     /** {@inheritDoc} */
  113.     @Override
  114.     public void clearEventDetectors() {
  115.         detectorBasedEventsStates.clear();
  116.     }

  117.     /** {@inheritDoc} */
  118.     @Override
  119.     public void addStepEndHandler(ODEStepEndHandler handler) {
  120.         stepEndEventsStates.add(new StepEndEventState(handler));
  121.     }

  122.     /** {@inheritDoc} */
  123.     @Override
  124.     public List<ODEStepEndHandler> getStepEndHandlers() {
  125.         return stepEndEventsStates.stream().map(es -> es.getHandler()).collect(Collectors.toList());
  126.     }

  127.     /** {@inheritDoc} */
  128.     @Override
  129.     public void clearStepEndHandlers() {
  130.         stepEndEventsStates.clear();
  131.     }

  132.     /** {@inheritDoc} */
  133.     @Override
  134.     public double getCurrentSignedStepsize() {
  135.         return stepSize;
  136.     }

  137.     /** {@inheritDoc} */
  138.     @Override
  139.     public void setMaxEvaluations(int maxEvaluations) {
  140.         evaluations = evaluations.withMaximalCount((maxEvaluations < 0) ? Integer.MAX_VALUE : maxEvaluations);
  141.     }

  142.     /** {@inheritDoc} */
  143.     @Override
  144.     public int getMaxEvaluations() {
  145.         return evaluations.getMaximalCount();
  146.     }

  147.     /** {@inheritDoc} */
  148.     @Override
  149.     public int getEvaluations() {
  150.         return evaluations.getCount();
  151.     }

  152.     /**
  153.      * Prepare the start of an integration.
  154.      *
  155.      * @param eqn equations to integrate
  156.      * @param s0  initial state vector
  157.      * @param t   target time for the integration
  158.      * @return Initial state with computed derivatives.
  159.      */
  160.     protected ODEStateAndDerivative initIntegration(final ExpandableODE eqn,
  161.                                                     final ODEState s0, final double t) {

  162.         this.equations = eqn;
  163.         evaluations    = evaluations.withCount(0);

  164.         // initialize ODE
  165.         eqn.init(s0, t);

  166.         // set up derivatives of initial state (including primary and secondary components)
  167.         final double   t0    = s0.getTime();
  168.         final double[] y0    = s0.getCompleteState();
  169.         final double[] y0Dot = computeDerivatives(t0, y0);

  170.         // built the state
  171.         final ODEStateAndDerivative s0WithDerivatives =
  172.                         eqn.getMapper().mapStateAndDerivative(t0, y0, y0Dot);

  173.         // initialize detector based event states (both  and step end based)
  174.         detectorBasedEventsStates.stream().forEach(s -> {
  175.             s.init(s0WithDerivatives, t);
  176.             s.getEventDetector().getHandler().init(s0WithDerivatives, t, s.getEventDetector());
  177.         });

  178.         // initialize step end based event states
  179.         stepEndEventsStates.stream().forEach(s -> {
  180.             s.init(s0WithDerivatives, t);
  181.             s.getHandler().init(s0WithDerivatives, t);
  182.         });

  183.         // initialize step handlers
  184.         for (ODEStepHandler handler : stepHandlers) {
  185.             handler.init(s0WithDerivatives, t);
  186.         }

  187.         setStateInitialized(false);

  188.         return s0WithDerivatives;

  189.     }

  190.     /** Get the differential equations to integrate.
  191.      * @return differential equations to integrate
  192.      */
  193.     protected ExpandableODE getEquations() {
  194.         return equations;
  195.     }

  196.     /** Get the evaluations counter.
  197.      * @return evaluations counter
  198.      */
  199.     protected Incrementor getEvaluationsCounter() {
  200.         return evaluations;
  201.     }

  202.     /** Compute the derivatives and check the number of evaluations.
  203.      * @param t current value of the independent <I>time</I> variable
  204.      * @param y array containing the current value of the state vector
  205.      * @return state completed with derivatives
  206.      * @exception MathIllegalArgumentException if arrays dimensions do not match equations settings
  207.      * @exception MathIllegalStateException if the number of functions evaluations is exceeded
  208.      * @exception NullPointerException if the ODE equations have not been set (i.e. if this method
  209.      * is called outside of a call to {@link #integrate(ExpandableODE, ODEState, double) integrate}
  210.      */
  211.     public double[] computeDerivatives(final double t, final double[] y)
  212.         throws MathIllegalArgumentException, MathIllegalStateException, NullPointerException {
  213.         evaluations.increment();
  214.         return equations.computeDerivatives(t, y);
  215.     }

  216.     /** Increment evaluations of derivatives.
  217.      *
  218.      * @param nTimes number of evaluations to increment
  219.      */
  220.     protected void incrementEvaluations(final int nTimes) {
  221.         evaluations.increment(nTimes);
  222.     }

  223.     /** Set the stateInitialized flag.
  224.      * <p>This method must be called by integrators with the value
  225.      * {@code false} before they start integration, so a proper lazy
  226.      * initialization is done automatically on the first step.</p>
  227.      * @param stateInitialized new value for the flag
  228.      */
  229.     protected void setStateInitialized(final boolean stateInitialized) {
  230.         this.statesInitialized = stateInitialized;
  231.     }

  232.     /** Accept a step, triggering events and step handlers.
  233.      * @param interpolator step interpolator
  234.      * @param tEnd final integration time
  235.      * @return state at end of step
  236.      * @exception MathIllegalStateException if the interpolator throws one because
  237.      * the number of functions evaluations is exceeded
  238.      * @exception MathIllegalArgumentException if the location of an event cannot be bracketed
  239.      * @exception MathIllegalArgumentException if arrays dimensions do not match equations settings
  240.      */
  241.     protected ODEStateAndDerivative acceptStep(final AbstractODEStateInterpolator interpolator,
  242.                                                final double tEnd)
  243.             throws MathIllegalArgumentException, MathIllegalStateException {

  244.         ODEStateAndDerivative previousState = interpolator.getGlobalPreviousState();
  245.         final ODEStateAndDerivative currentState = interpolator.getGlobalCurrentState();
  246.         AbstractODEStateInterpolator restricted = interpolator;


  247.         // initialize the events states if needed
  248.         if (!statesInitialized) {
  249.             // initialize event states
  250.             detectorBasedEventsStates.stream().forEach(s -> s.reinitializeBegin(interpolator));
  251.             statesInitialized = true;
  252.         }

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

  255.         // search for next events that may occur during the step
  256.         final int orderingSign = interpolator.isForward() ? +1 : -1;
  257.         final Queue<EventState> occurringEvents = new PriorityQueue<>(new Comparator<EventState>() {
  258.             /** {@inheritDoc} */
  259.             @Override
  260.             public int compare(final EventState es0, final EventState es1) {
  261.                 return orderingSign * Double.compare(es0.getEventTime(), es1.getEventTime());
  262.             }
  263.         });

  264.         resetOccurred = false;
  265.         boolean doneWithStep = false;
  266.         resetEvents:
  267.         do {

  268.             // Evaluate all event detectors and end steps for events
  269.             occurringEvents.clear();
  270.             final AbstractODEStateInterpolator finalRestricted = restricted;
  271.             Stream.concat(detectorBasedEventsStates.stream(), stepEndEventsStates.stream()).
  272.             forEach(s -> { if (s.evaluateStep(finalRestricted)) {
  273.                     // the event occurs during the current step
  274.                     occurringEvents.add(s);
  275.                 }
  276.             });

  277.             do {

  278.                 eventLoop:
  279.                 while (!occurringEvents.isEmpty()) {

  280.                     // handle the chronologically first event
  281.                     final EventState currentEvent = occurringEvents.poll();

  282.                     // get state at event time
  283.                     ODEStateAndDerivative eventState = restricted.getInterpolatedState(currentEvent.getEventTime());

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

  286.                     // try to advance all event states related to detectors to current time
  287.                     for (final DetectorBasedEventState state : detectorBasedEventsStates) {
  288.                         if (state != currentEvent && state.tryAdvance(eventState, interpolator)) {
  289.                             // we need to handle another event first
  290.                             // remove event we just updated to prevent heap corruption
  291.                             occurringEvents.remove(state);
  292.                             // add it back to update its position in the heap
  293.                             occurringEvents.add(state);
  294.                             // re-queue the event we were processing
  295.                             occurringEvents.add(currentEvent);
  296.                             continue eventLoop;
  297.                         }
  298.                     }
  299.                     // all event detectors agree we can advance to the current event time

  300.                     // handle the first part of the step, up to the event
  301.                     for (final ODEStepHandler handler : stepHandlers) {
  302.                         handler.handleStep(restricted);
  303.                     }

  304.                     // acknowledge event occurrence
  305.                     final EventOccurrence occurrence = currentEvent.doEvent(eventState);
  306.                     final Action action = occurrence.getAction();
  307.                     isLastStep = action == Action.STOP;

  308.                     if (isLastStep) {

  309.                         // ensure the event is after the root if it is returned STOP
  310.                         // this lets the user integrate to a STOP event and then restart
  311.                         // integration from the same time.
  312.                         final ODEStateAndDerivative savedState = eventState;
  313.                         eventState = interpolator.getInterpolatedState(occurrence.getStopTime());
  314.                         restricted = interpolator.restrictStep(savedState, eventState);

  315.                         // handle the almost zero size last part of the final step, at event time
  316.                         for (final ODEStepHandler handler : stepHandlers) {
  317.                             handler.handleStep(restricted);
  318.                             handler.finish(restricted.getCurrentState());
  319.                         }

  320.                     }

  321.                     if (isLastStep) {
  322.                         // the event asked to stop integration
  323.                         return eventState;
  324.                     }

  325.                     if (action == Action.RESET_DERIVATIVES || action == Action.RESET_STATE) {
  326.                         // some event handler has triggered changes that
  327.                         // invalidate the derivatives, we need to recompute them
  328.                         final ODEState newState = occurrence.getNewState();
  329.                         final double[] y = newState.getCompleteState();
  330.                         final double[] yDot = computeDerivatives(newState.getTime(), y);
  331.                         resetOccurred = true;
  332.                         return equations.getMapper().mapStateAndDerivative(newState.getTime(), y, yDot);
  333.                     }
  334.                     // at this point action == Action.CONTINUE or Action.RESET_EVENTS

  335.                     // prepare handling of the remaining part of the step
  336.                     previousState = eventState;
  337.                     restricted = restricted.restrictStep(eventState, currentState);

  338.                     if (action == Action.RESET_EVENTS) {
  339.                         continue resetEvents;
  340.                     }

  341.                     // at this point action == Action.CONTINUE
  342.                     // check if the same event occurs again in the remaining part of the step
  343.                     if (currentEvent.evaluateStep(restricted)) {
  344.                         // the event occurs during the current step
  345.                         occurringEvents.add(currentEvent);
  346.                     }

  347.                 }

  348.                 // last part of the step, after the last event. Advance all event
  349.                 // detectors to the end of the step. Should never find new events unless
  350.                 // a previous event modified the g function of another event detector when
  351.                 // it returned Action.CONTINUE. Detecting such events here is unreliable
  352.                 // and RESET_EVENTS should be used instead. Other option is to replace
  353.                 // tryAdvance(...) with a doAdvance(...) that throws an exception when
  354.                 // the g function sign is not as expected.
  355.                 for (final DetectorBasedEventState state : detectorBasedEventsStates) {
  356.                     if (state.tryAdvance(currentState, interpolator)) {
  357.                         occurringEvents.add(state);
  358.                     }
  359.                 }

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

  361.             doneWithStep = true;
  362.         } while (!doneWithStep);

  363.         isLastStep = isLastStep || FastMath.abs(currentState.getTime() - tEnd) < FastMath.ulp(tEnd);

  364.         // handle the remaining part of the step, after all events if any
  365.         for (ODEStepHandler handler : stepHandlers) {
  366.             handler.handleStep(restricted);
  367.             if (isLastStep) {
  368.                 handler.finish(restricted.getCurrentState());
  369.             }
  370.         }

  371.         return currentState;

  372.     }

  373.     /** Check the integration span.
  374.      * @param initialState initial state
  375.      * @param t target time for the integration
  376.      * @exception MathIllegalArgumentException if integration span is too small
  377.      * @exception MathIllegalArgumentException if adaptive step size integrators
  378.      * tolerance arrays dimensions are not compatible with equations settings
  379.      */
  380.     protected void sanityChecks(final ODEState initialState, final double t)
  381.         throws MathIllegalArgumentException {

  382.         final double threshold = 1000 * FastMath.ulp(FastMath.max(FastMath.abs(initialState.getTime()),
  383.                                                                   FastMath.abs(t)));
  384.         final double dt = FastMath.abs(initialState.getTime() - t);
  385.         if (dt < threshold) {
  386.             throw new MathIllegalArgumentException(LocalizedODEFormats.TOO_SMALL_INTEGRATION_INTERVAL,
  387.                                                    dt, threshold, false);
  388.         }

  389.     }

  390.     /** Check if a reset occurred while last step was accepted.
  391.      * @return true if a reset occurred while last step was accepted
  392.      */
  393.     protected boolean resetOccurred() {
  394.         return resetOccurred;
  395.     }

  396.     /** Set the current step size.
  397.      * @param stepSize step size to set
  398.      */
  399.     protected void setStepSize(final double stepSize) {
  400.         this.stepSize = stepSize;
  401.     }

  402.     /** Get the current step size.
  403.      * @return current step size
  404.      */
  405.     protected double getStepSize() {
  406.         return stepSize;
  407.     }
  408.     /** Set current step start.
  409.      * @param stepStart step start
  410.      */
  411.     protected void setStepStart(final ODEStateAndDerivative stepStart) {
  412.         this.stepStart = stepStart;
  413.     }

  414.     /**  {@inheritDoc} */
  415.     @Override
  416.     public ODEStateAndDerivative getStepStart() {
  417.         return stepStart;
  418.     }

  419.     /** Set the last state flag.
  420.      * @param isLastStep if true, this step is the last one
  421.      */
  422.     protected void setIsLastStep(final boolean isLastStep) {
  423.         this.isLastStep = isLastStep;
  424.     }

  425.     /** Check if this step is the last one.
  426.      * @return true if this step is the last one
  427.      */
  428.     protected boolean isLastStep() {
  429.         return isLastStep;
  430.     }

  431. }