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.  *      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.apache.commons.math4.legacy.ode;

  18. import java.util.ArrayList;
  19. import java.util.Collection;
  20. import java.util.Collections;
  21. import java.util.Comparator;
  22. import java.util.Iterator;
  23. import java.util.List;
  24. import java.util.SortedSet;
  25. import java.util.TreeSet;

  26. import org.apache.commons.math4.legacy.analysis.solvers.BracketingNthOrderBrentSolver;
  27. import org.apache.commons.math4.legacy.analysis.solvers.UnivariateSolver;
  28. import org.apache.commons.math4.legacy.exception.DimensionMismatchException;
  29. import org.apache.commons.math4.legacy.exception.MaxCountExceededException;
  30. import org.apache.commons.math4.legacy.exception.NoBracketingException;
  31. import org.apache.commons.math4.legacy.exception.NumberIsTooSmallException;
  32. import org.apache.commons.math4.legacy.exception.util.LocalizedFormats;
  33. import org.apache.commons.math4.legacy.ode.events.EventHandler;
  34. import org.apache.commons.math4.legacy.ode.events.EventState;
  35. import org.apache.commons.math4.legacy.ode.sampling.AbstractStepInterpolator;
  36. import org.apache.commons.math4.legacy.ode.sampling.StepHandler;
  37. import org.apache.commons.math4.core.jdkmath.JdkMath;
  38. import org.apache.commons.math4.legacy.core.IntegerSequence;
  39. import org.apache.commons.numbers.core.Precision;

  40. /**
  41.  * Base class managing common boilerplate for all integrators.
  42.  * @since 2.0
  43.  */
  44. public abstract class AbstractIntegrator implements FirstOrderIntegrator {

  45.     /** Step handler. */
  46.     protected Collection<StepHandler> stepHandlers;

  47.     /** Current step start time. */
  48.     protected double stepStart;

  49.     /** Current stepsize. */
  50.     protected double stepSize;

  51.     /** Indicator for last step. */
  52.     protected boolean isLastStep;

  53.     /** Indicator that a state or derivative reset was triggered by some event. */
  54.     protected boolean resetOccurred;

  55.     /** Events states. */
  56.     private Collection<EventState> eventsStates;

  57.     /** Initialization indicator of events states. */
  58.     private boolean statesInitialized;

  59.     /** Name of the method. */
  60.     private final String name;

  61.     /** Counter for number of evaluations. */
  62.     private IntegerSequence.Incrementor evaluations;

  63.     /** Differential equations to integrate. */
  64.     private transient ExpandableStatefulODE expandable;

  65.     /** Build an instance.
  66.      * @param name name of the method
  67.      */
  68.     public AbstractIntegrator(final String name) {
  69.         this.name = name;
  70.         stepHandlers = new ArrayList<>();
  71.         stepStart = Double.NaN;
  72.         stepSize  = Double.NaN;
  73.         eventsStates = new ArrayList<>();
  74.         statesInitialized = false;
  75.         evaluations = IntegerSequence.Incrementor.create().withMaximalCount(Integer.MAX_VALUE);
  76.     }

  77.     /** Build an instance with a null name.
  78.      */
  79.     protected AbstractIntegrator() {
  80.         this(null);
  81.     }

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

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

  92.     /** {@inheritDoc} */
  93.     @Override
  94.     public Collection<StepHandler> getStepHandlers() {
  95.         return Collections.unmodifiableCollection(stepHandlers);
  96.     }

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

  102.     /** {@inheritDoc} */
  103.     @Override
  104.     public void addEventHandler(final EventHandler handler,
  105.                                 final double maxCheckInterval,
  106.                                 final double convergence,
  107.                                 final int maxIterationCount) {
  108.         addEventHandler(handler, maxCheckInterval, convergence,
  109.                         maxIterationCount,
  110.                         new BracketingNthOrderBrentSolver(convergence, 5));
  111.     }

  112.     /** {@inheritDoc} */
  113.     @Override
  114.     public void addEventHandler(final EventHandler handler,
  115.                                 final double maxCheckInterval,
  116.                                 final double convergence,
  117.                                 final int maxIterationCount,
  118.                                 final UnivariateSolver solver) {
  119.         eventsStates.add(new EventState(handler, maxCheckInterval, convergence,
  120.                                         maxIterationCount, solver));
  121.     }

  122.     /** {@inheritDoc} */
  123.     @Override
  124.     public Collection<EventHandler> getEventHandlers() {
  125.         final List<EventHandler> list = new ArrayList<>(eventsStates.size());
  126.         for (EventState state : eventsStates) {
  127.             list.add(state.getEventHandler());
  128.         }
  129.         return Collections.unmodifiableCollection(list);
  130.     }

  131.     /** {@inheritDoc} */
  132.     @Override
  133.     public void clearEventHandlers() {
  134.         eventsStates.clear();
  135.     }

  136.     /** {@inheritDoc} */
  137.     @Override
  138.     public double getCurrentStepStart() {
  139.         return stepStart;
  140.     }

  141.     /** {@inheritDoc} */
  142.     @Override
  143.     public double getCurrentSignedStepsize() {
  144.         return stepSize;
  145.     }

  146.     /** {@inheritDoc} */
  147.     @Override
  148.     public void setMaxEvaluations(int maxEvaluations) {
  149.         evaluations = evaluations.withMaximalCount((maxEvaluations < 0) ? Integer.MAX_VALUE : maxEvaluations);
  150.     }

  151.     /** {@inheritDoc} */
  152.     @Override
  153.     public int getMaxEvaluations() {
  154.         return evaluations.getMaximalCount();
  155.     }

  156.     /** {@inheritDoc} */
  157.     @Override
  158.     public int getEvaluations() {
  159.         return evaluations.getCount();
  160.     }

  161.     /** Prepare the start of an integration.
  162.      * @param t0 start value of the independent <i>time</i> variable
  163.      * @param y0 array containing the start value of the state vector
  164.      * @param t target time for the integration
  165.      */
  166.     protected void initIntegration(final double t0, final double[] y0, final double t) {

  167.         evaluations = evaluations.withStart(0);

  168.         for (final EventState state : eventsStates) {
  169.             state.setExpandable(expandable);
  170.             state.getEventHandler().init(t0, y0, t);
  171.         }

  172.         for (StepHandler handler : stepHandlers) {
  173.             handler.init(t0, y0, t);
  174.         }

  175.         setStateInitialized(false);
  176.     }

  177.     /** Set the equations.
  178.      * @param equations equations to set
  179.      */
  180.     protected void setEquations(final ExpandableStatefulODE equations) {
  181.         this.expandable = equations;
  182.     }

  183.     /** Get the differential equations to integrate.
  184.      * @return differential equations to integrate
  185.      * @since 3.2
  186.      */
  187.     protected ExpandableStatefulODE getExpandable() {
  188.         return expandable;
  189.     }

  190.     /** Get the evaluations counter.
  191.      * @return evaluations counter
  192.      * @since 3.6
  193.      */
  194.     protected IntegerSequence.Incrementor getCounter() {
  195.         return evaluations;
  196.     }

  197.     /** {@inheritDoc} */
  198.     @Override
  199.     public double integrate(final FirstOrderDifferentialEquations equations,
  200.                             final double t0, final double[] y0, final double t, final double[] y)
  201.         throws DimensionMismatchException, NumberIsTooSmallException,
  202.                MaxCountExceededException, NoBracketingException {

  203.         if (y0.length != equations.getDimension()) {
  204.             throw new DimensionMismatchException(y0.length, equations.getDimension());
  205.         }
  206.         if (y.length != equations.getDimension()) {
  207.             throw new DimensionMismatchException(y.length, equations.getDimension());
  208.         }

  209.         // prepare expandable stateful equations
  210.         final ExpandableStatefulODE expandableODE = new ExpandableStatefulODE(equations);
  211.         expandableODE.setTime(t0);
  212.         expandableODE.setPrimaryState(y0);

  213.         // perform integration
  214.         integrate(expandableODE, t);

  215.         // extract results back from the stateful equations
  216.         System.arraycopy(expandableODE.getPrimaryState(), 0, y, 0, y.length);
  217.         return expandableODE.getTime();
  218.     }

  219.     /** Integrate a set of differential equations up to the given time.
  220.      * <p>This method solves an Initial Value Problem (IVP).</p>
  221.      * <p>The set of differential equations is composed of a main set, which
  222.      * can be extended by some sets of secondary equations. The set of
  223.      * equations must be already set up with initial time and partial states.
  224.      * At integration completion, the final time and partial states will be
  225.      * available in the same object.</p>
  226.      * <p>Since this method stores some internal state variables made
  227.      * available in its public interface during integration ({@link
  228.      * #getCurrentSignedStepsize()}), it is <em>not</em> thread-safe.</p>
  229.      * @param equations complete set of differential equations to integrate
  230.      * @param t target time for the integration
  231.      * (can be set to a value smaller than <code>t0</code> for backward integration)
  232.      * @exception NumberIsTooSmallException if integration step is too small
  233.      * @throws DimensionMismatchException if the dimension of the complete state does not
  234.      * match the complete equations sets dimension
  235.      * @exception MaxCountExceededException if the number of functions evaluations is exceeded
  236.      * @exception NoBracketingException if the location of an event cannot be bracketed
  237.      */
  238.     public abstract void integrate(ExpandableStatefulODE equations, double t)
  239.         throws NumberIsTooSmallException, DimensionMismatchException,
  240.                MaxCountExceededException, NoBracketingException;

  241.     /** Compute the derivatives and check the number of evaluations.
  242.      * @param t current value of the independent <I>time</I> variable
  243.      * @param y array containing the current value of the state vector
  244.      * @param yDot placeholder array where to put the time derivative of the state vector
  245.      * @exception MaxCountExceededException if the number of functions evaluations is exceeded
  246.      * @exception DimensionMismatchException if arrays dimensions do not match equations settings
  247.      * @exception NullPointerException if the ODE equations have not been set (i.e. if this method
  248.      * is called outside of a call to {@link #integrate(ExpandableStatefulODE, double)} or {@link
  249.      * #integrate(FirstOrderDifferentialEquations, double, double[], double, double[])})
  250.      */
  251.     public void computeDerivatives(final double t, final double[] y, final double[] yDot)
  252.         throws MaxCountExceededException, DimensionMismatchException, NullPointerException {
  253.         evaluations.increment();
  254.         expandable.computeDerivatives(t, y, yDot);
  255.     }

  256.     /** Set the stateInitialized flag.
  257.      * <p>This method must be called by integrators with the value
  258.      * {@code false} before they start integration, so a proper lazy
  259.      * initialization is done automatically on the first step.</p>
  260.      * @param stateInitialized new value for the flag
  261.      * @since 2.2
  262.      */
  263.     protected void setStateInitialized(final boolean stateInitialized) {
  264.         this.statesInitialized = stateInitialized;
  265.     }

  266.     /** Accept a step, triggering events and step handlers.
  267.      * @param interpolator step interpolator
  268.      * @param y state vector at step end time, must be reset if an event
  269.      * asks for resetting or if an events stops integration during the step
  270.      * @param yDot placeholder array where to put the time derivative of the state vector
  271.      * @param tEnd final integration time
  272.      * @return time at end of step
  273.      * @exception MaxCountExceededException if the interpolator throws one because
  274.      * the number of functions evaluations is exceeded
  275.      * @exception NoBracketingException if the location of an event cannot be bracketed
  276.      * @exception DimensionMismatchException if arrays dimensions do not match equations settings
  277.      * @since 2.2
  278.      */
  279.     protected double acceptStep(final AbstractStepInterpolator interpolator,
  280.                                 final double[] y, final double[] yDot, final double tEnd)
  281.         throws MaxCountExceededException, DimensionMismatchException, NoBracketingException {

  282.             double previousT = interpolator.getGlobalPreviousTime();
  283.             final double currentT = interpolator.getGlobalCurrentTime();

  284.             // initialize the events states if needed
  285.             if (! statesInitialized) {
  286.                 for (EventState state : eventsStates) {
  287.                     state.reinitializeBegin(interpolator);
  288.                 }
  289.                 statesInitialized = true;
  290.             }

  291.             // search for next events that may occur during the step
  292.             final int orderingSign = interpolator.isForward() ? +1 : -1;
  293.             SortedSet<EventState> occurringEvents = new TreeSet<>(new Comparator<EventState>() {

  294.                 /** {@inheritDoc} */
  295.                 @Override
  296.                 public int compare(EventState es0, EventState es1) {
  297.                     return orderingSign * Double.compare(es0.getEventTime(), es1.getEventTime());
  298.                 }
  299.             });

  300.             for (final EventState state : eventsStates) {
  301.                 if (state.evaluateStep(interpolator)) {
  302.                     // the event occurs during the current step
  303.                     occurringEvents.add(state);
  304.                 }
  305.             }

  306.             while (!occurringEvents.isEmpty()) {

  307.                 // handle the chronologically first event
  308.                 final Iterator<EventState> iterator = occurringEvents.iterator();
  309.                 final EventState currentEvent = iterator.next();
  310.                 iterator.remove();

  311.                 // restrict the interpolator to the first part of the step, up to the event
  312.                 final double eventT = currentEvent.getEventTime();
  313.                 interpolator.setSoftPreviousTime(previousT);
  314.                 interpolator.setSoftCurrentTime(eventT);

  315.                 // get state at event time
  316.                 interpolator.setInterpolatedTime(eventT);
  317.                 final double[] eventYComplete = new double[y.length];
  318.                 expandable.getPrimaryMapper().insertEquationData(interpolator.getInterpolatedState(),
  319.                                                                  eventYComplete);
  320.                 int index = 0;
  321.                 for (EquationsMapper secondary : expandable.getSecondaryMappers()) {
  322.                     secondary.insertEquationData(interpolator.getInterpolatedSecondaryState(index++),
  323.                                                  eventYComplete);
  324.                 }

  325.                 // advance all event states to current time
  326.                 for (final EventState state : eventsStates) {
  327.                     state.stepAccepted(eventT, eventYComplete);
  328.                     isLastStep = isLastStep || state.stop();
  329.                 }

  330.                 // handle the first part of the step, up to the event
  331.                 for (final StepHandler handler : stepHandlers) {
  332.                     handler.handleStep(interpolator, isLastStep);
  333.                 }

  334.                 if (isLastStep) {
  335.                     // the event asked to stop integration
  336.                     System.arraycopy(eventYComplete, 0, y, 0, y.length);
  337.                     return eventT;
  338.                 }

  339.                 resetOccurred = false;
  340.                 final boolean needReset = currentEvent.reset(eventT, eventYComplete);
  341.                 if (needReset) {
  342.                     // some event handler has triggered changes that
  343.                     // invalidate the derivatives, we need to recompute them
  344.                     interpolator.setInterpolatedTime(eventT);
  345.                     System.arraycopy(eventYComplete, 0, y, 0, y.length);
  346.                     computeDerivatives(eventT, y, yDot);
  347.                     resetOccurred = true;
  348.                     return eventT;
  349.                 }

  350.                 // prepare handling of the remaining part of the step
  351.                 previousT = eventT;
  352.                 interpolator.setSoftPreviousTime(eventT);
  353.                 interpolator.setSoftCurrentTime(currentT);

  354.                 // check if the same event occurs again in the remaining part of the step
  355.                 if (currentEvent.evaluateStep(interpolator)) {
  356.                     // the event occurs during the current step
  357.                     occurringEvents.add(currentEvent);
  358.                 }
  359.             }

  360.             // last part of the step, after the last event
  361.             interpolator.setInterpolatedTime(currentT);
  362.             final double[] currentY = new double[y.length];
  363.             expandable.getPrimaryMapper().insertEquationData(interpolator.getInterpolatedState(),
  364.                                                              currentY);
  365.             int index = 0;
  366.             for (EquationsMapper secondary : expandable.getSecondaryMappers()) {
  367.                 secondary.insertEquationData(interpolator.getInterpolatedSecondaryState(index++),
  368.                                              currentY);
  369.             }
  370.             for (final EventState state : eventsStates) {
  371.                 state.stepAccepted(currentT, currentY);
  372.                 isLastStep = isLastStep || state.stop();
  373.             }
  374.             isLastStep = isLastStep || Precision.equals(currentT, tEnd, 1);

  375.             // handle the remaining part of the step, after all events if any
  376.             for (StepHandler handler : stepHandlers) {
  377.                 handler.handleStep(interpolator, isLastStep);
  378.             }

  379.             return currentT;
  380.     }

  381.     /** Check the integration span.
  382.      * @param equations set of differential equations
  383.      * @param t target time for the integration
  384.      * @exception NumberIsTooSmallException if integration span is too small
  385.      * @exception DimensionMismatchException if adaptive step size integrators
  386.      * tolerance arrays dimensions are not compatible with equations settings
  387.      */
  388.     protected void sanityChecks(final ExpandableStatefulODE equations, final double t)
  389.         throws NumberIsTooSmallException, DimensionMismatchException {

  390.         final double threshold = 1000 * JdkMath.ulp(JdkMath.max(JdkMath.abs(equations.getTime()),
  391.                                                                   JdkMath.abs(t)));
  392.         final double dt = JdkMath.abs(equations.getTime() - t);
  393.         if (dt <= threshold) {
  394.             throw new NumberIsTooSmallException(LocalizedFormats.TOO_SMALL_INTEGRATION_INTERVAL,
  395.                                                 dt, threshold, false);
  396.         }
  397.     }
  398. }