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.ODEStateInterpolator;
  41. import org.hipparchus.ode.sampling.ODEStepHandler;
  42. import org.hipparchus.util.FastMath;
  43. import org.hipparchus.util.Incrementor;

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

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

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

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

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

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

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

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

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

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

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

  68.     /** Differential equations to integrate. */
  69.     private ExpandableODE equations;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  188.         setStateInitialized(false);

  189.         return s0WithDerivatives;

  190.     }

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

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

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

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

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

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

  245.         ODEStateAndDerivative previousState = interpolator.getGlobalPreviousState();
  246.         final ODEStateAndDerivative currentState = interpolator.getGlobalCurrentState();
  247.         ODEStateInterpolator restricted = interpolator;


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

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

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

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

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

  278.             do {

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

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

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

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

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

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

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

  309.                     if (isLastStep) {

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

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

  321.                     }

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

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

  339.                     // prepare handling of the remaining part of the step
  340.                     previousState = eventState;
  341.                     restricted = restricted.restrictStep(eventState, currentState);

  342.                     if (action == Action.RESET_EVENTS) {
  343.                         continue resetEvents;
  344.                     }

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

  351.                 }

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

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

  365.             doneWithStep = true;
  366.         } while (!doneWithStep);

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

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

  375.         return currentState;

  376.     }

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

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

  393.     }

  394.     /** Check if a reset occurred while last step was accepted.
  395.      * @return true if a reset occurred while last step was accepted
  396.      */
  397.     protected boolean resetOccurred() {
  398.         return resetOccurred;
  399.     }

  400.     /** Set the current step size.
  401.      * @param stepSize step size to set
  402.      */
  403.     protected void setStepSize(final double stepSize) {
  404.         this.stepSize = stepSize;
  405.     }

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

  418.     /**  {@inheritDoc} */
  419.     @Override
  420.     public ODEStateAndDerivative getStepStart() {
  421.         return stepStart;
  422.     }

  423.     /** Set the last state flag.
  424.      * @param isLastStep if true, this step is the last one
  425.      */
  426.     protected void setIsLastStep(final boolean isLastStep) {
  427.         this.isLastStep = isLastStep;
  428.     }

  429.     /** Check if this step is the last one.
  430.      * @return true if this step is the last one
  431.      */
  432.     protected boolean isLastStep() {
  433.         return isLastStep;
  434.     }

  435. }