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.  *      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.core.Field;
  27. import org.apache.commons.math4.legacy.core.RealFieldElement;
  28. import org.apache.commons.math4.legacy.analysis.solvers.BracketedRealFieldUnivariateSolver;
  29. import org.apache.commons.math4.legacy.analysis.solvers.FieldBracketingNthOrderBrentSolver;
  30. import org.apache.commons.math4.legacy.exception.DimensionMismatchException;
  31. import org.apache.commons.math4.legacy.exception.MaxCountExceededException;
  32. import org.apache.commons.math4.legacy.exception.NoBracketingException;
  33. import org.apache.commons.math4.legacy.exception.NumberIsTooSmallException;
  34. import org.apache.commons.math4.legacy.exception.util.LocalizedFormats;
  35. import org.apache.commons.math4.legacy.ode.events.FieldEventHandler;
  36. import org.apache.commons.math4.legacy.ode.events.FieldEventState;
  37. import org.apache.commons.math4.legacy.ode.sampling.AbstractFieldStepInterpolator;
  38. import org.apache.commons.math4.legacy.ode.sampling.FieldStepHandler;
  39. import org.apache.commons.math4.core.jdkmath.JdkMath;
  40. import org.apache.commons.math4.legacy.core.IntegerSequence;

  41. /**
  42.  * Base class managing common boilerplate for all integrators.
  43.  * @param <T> the type of the field elements
  44.  * @since 3.6
  45.  */
  46. public abstract class AbstractFieldIntegrator<T extends RealFieldElement<T>> implements FirstOrderFieldIntegrator<T> {

  47.     /** Default relative accuracy. */
  48.     private static final double DEFAULT_RELATIVE_ACCURACY = 1e-14;

  49.     /** Default function value accuracy. */
  50.     private static final double DEFAULT_FUNCTION_VALUE_ACCURACY = 1e-15;

  51.     /** Step handler. */
  52.     private Collection<FieldStepHandler<T>> stepHandlers;

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

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

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

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

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

  63.     /** Events states. */
  64.     private Collection<FieldEventState<T>> eventsStates;

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

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

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

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

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

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

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

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

  103.     /** {@inheritDoc} */
  104.     @Override
  105.     public Collection<FieldStepHandler<T>> getStepHandlers() {
  106.         return Collections.unmodifiableCollection(stepHandlers);
  107.     }

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

  113.     /** {@inheritDoc} */
  114.     @Override
  115.     public void addEventHandler(final FieldEventHandler<T> handler,
  116.                                 final double maxCheckInterval,
  117.                                 final double convergence,
  118.                                 final int maxIterationCount) {
  119.         addEventHandler(handler, maxCheckInterval, convergence,
  120.                         maxIterationCount,
  121.                         new FieldBracketingNthOrderBrentSolver<>(field.getZero().add(DEFAULT_RELATIVE_ACCURACY),
  122.                                                                   field.getZero().add(convergence),
  123.                                                                   field.getZero().add(DEFAULT_FUNCTION_VALUE_ACCURACY),
  124.                                                                   5));
  125.     }

  126.     /** {@inheritDoc} */
  127.     @Override
  128.     public void addEventHandler(final FieldEventHandler<T> handler,
  129.                                 final double maxCheckInterval,
  130.                                 final double convergence,
  131.                                 final int maxIterationCount,
  132.                                 final BracketedRealFieldUnivariateSolver<T> solver) {
  133.         eventsStates.add(new FieldEventState<>(handler, maxCheckInterval, field.getZero().add(convergence),
  134.                                                 maxIterationCount, solver));
  135.     }

  136.     /** {@inheritDoc} */
  137.     @Override
  138.     public Collection<FieldEventHandler<T>> getEventHandlers() {
  139.         final List<FieldEventHandler<T>> list = new ArrayList<>(eventsStates.size());
  140.         for (FieldEventState<T> state : eventsStates) {
  141.             list.add(state.getEventHandler());
  142.         }
  143.         return Collections.unmodifiableCollection(list);
  144.     }

  145.     /** {@inheritDoc} */
  146.     @Override
  147.     public void clearEventHandlers() {
  148.         eventsStates.clear();
  149.     }

  150.     /** {@inheritDoc} */
  151.     @Override
  152.     public FieldODEStateAndDerivative<T> getCurrentStepStart() {
  153.         return stepStart;
  154.     }

  155.     /** {@inheritDoc} */
  156.     @Override
  157.     public T getCurrentSignedStepsize() {
  158.         return stepSize;
  159.     }

  160.     /** {@inheritDoc} */
  161.     @Override
  162.     public void setMaxEvaluations(int maxEvaluations) {
  163.         evaluations = evaluations.withMaximalCount((maxEvaluations < 0) ? Integer.MAX_VALUE : maxEvaluations);
  164.     }

  165.     /** {@inheritDoc} */
  166.     @Override
  167.     public int getMaxEvaluations() {
  168.         return evaluations.getMaximalCount();
  169.     }

  170.     /** {@inheritDoc} */
  171.     @Override
  172.     public int getEvaluations() {
  173.         return evaluations.getCount();
  174.     }

  175.     /** Prepare the start of an integration.
  176.      * @param eqn equations to integrate
  177.      * @param t0 start value of the independent <i>time</i> variable
  178.      * @param y0 array containing the start value of the state vector
  179.      * @param t target time for the integration
  180.      * @return initial state with derivatives added
  181.      */
  182.     protected FieldODEStateAndDerivative<T> initIntegration(final FieldExpandableODE<T> eqn,
  183.                                                             final T t0, final T[] y0, final T t) {

  184.         this.equations = eqn;
  185.         evaluations    = evaluations.withStart(0);

  186.         // initialize ODE
  187.         eqn.init(t0, y0, t);

  188.         // set up derivatives of initial state
  189.         final T[] y0Dot = computeDerivatives(t0, y0);
  190.         final FieldODEStateAndDerivative<T> state0 = new FieldODEStateAndDerivative<>(t0, y0, y0Dot);

  191.         // initialize event handlers
  192.         for (final FieldEventState<T> state : eventsStates) {
  193.             state.getEventHandler().init(state0, t);
  194.         }

  195.         // initialize step handlers
  196.         for (FieldStepHandler<T> handler : stepHandlers) {
  197.             handler.init(state0, t);
  198.         }

  199.         setStateInitialized(false);

  200.         return state0;
  201.     }

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

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

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

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

  238.     /** Accept a step, triggering events and step handlers.
  239.      * @param interpolator step interpolator
  240.      * @param tEnd final integration time
  241.      * @return state at end of step
  242.      * @exception MaxCountExceededException if the interpolator throws one because
  243.      * the number of functions evaluations is exceeded
  244.      * @exception NoBracketingException if the location of an event cannot be bracketed
  245.      * @exception DimensionMismatchException if arrays dimensions do not match equations settings
  246.      */
  247.     protected FieldODEStateAndDerivative<T> acceptStep(final AbstractFieldStepInterpolator<T> interpolator,
  248.                                                        final T tEnd)
  249.         throws MaxCountExceededException, DimensionMismatchException, NoBracketingException {

  250.             FieldODEStateAndDerivative<T> previousState = interpolator.getGlobalPreviousState();
  251.             final FieldODEStateAndDerivative<T> currentState = interpolator.getGlobalCurrentState();

  252.             // initialize the events states if needed
  253.             if (! statesInitialized) {
  254.                 for (FieldEventState<T> state : eventsStates) {
  255.                     state.reinitializeBegin(interpolator);
  256.                 }
  257.                 statesInitialized = true;
  258.             }

  259.             // search for next events that may occur during the step
  260.             final int orderingSign = interpolator.isForward() ? +1 : -1;
  261.             SortedSet<FieldEventState<T>> occurringEvents = new TreeSet<>(new Comparator<FieldEventState<T>>() {

  262.                 /** {@inheritDoc} */
  263.                 @Override
  264.                 public int compare(FieldEventState<T> es0, FieldEventState<T> es1) {
  265.                     return orderingSign * Double.compare(es0.getEventTime().getReal(), es1.getEventTime().getReal());
  266.                 }
  267.             });

  268.             for (final FieldEventState<T> state : eventsStates) {
  269.                 if (state.evaluateStep(interpolator)) {
  270.                     // the event occurs during the current step
  271.                     occurringEvents.add(state);
  272.                 }
  273.             }

  274.             AbstractFieldStepInterpolator<T> restricted = interpolator;
  275.             while (!occurringEvents.isEmpty()) {

  276.                 // handle the chronologically first event
  277.                 final Iterator<FieldEventState<T>> iterator = occurringEvents.iterator();
  278.                 final FieldEventState<T> currentEvent = iterator.next();
  279.                 iterator.remove();

  280.                 // get state at event time
  281.                 final FieldODEStateAndDerivative<T> eventState = restricted.getInterpolatedState(currentEvent.getEventTime());

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

  284.                 // advance all event states to current time
  285.                 for (final FieldEventState<T> state : eventsStates) {
  286.                     state.stepAccepted(eventState);
  287.                     isLastStep = isLastStep || state.stop();
  288.                 }

  289.                 // handle the first part of the step, up to the event
  290.                 for (final FieldStepHandler<T> handler : stepHandlers) {
  291.                     handler.handleStep(restricted, isLastStep);
  292.                 }

  293.                 if (isLastStep) {
  294.                     // the event asked to stop integration
  295.                     return eventState;
  296.                 }

  297.                 FieldODEState<T> newState = null;
  298.                 resetOccurred = false;
  299.                 for (final FieldEventState<T> state : eventsStates) {
  300.                     newState = state.reset(eventState);
  301.                     if (newState != null) {
  302.                         // some event handler has triggered changes that
  303.                         // invalidate the derivatives, we need to recompute them
  304.                         final T[] y    = equations.getMapper().mapState(newState);
  305.                         final T[] yDot = computeDerivatives(newState.getTime(), y);
  306.                         resetOccurred = true;
  307.                         return equations.getMapper().mapStateAndDerivative(newState.getTime(), y, yDot);
  308.                     }
  309.                 }

  310.                 // prepare handling of the remaining part of the step
  311.                 previousState = eventState;
  312.                 restricted = restricted.restrictStep(eventState, currentState);

  313.                 // check if the same event occurs again in the remaining part of the step
  314.                 if (currentEvent.evaluateStep(restricted)) {
  315.                     // the event occurs during the current step
  316.                     occurringEvents.add(currentEvent);
  317.                 }
  318.             }

  319.             // last part of the step, after the last event
  320.             for (final FieldEventState<T> state : eventsStates) {
  321.                 state.stepAccepted(currentState);
  322.                 isLastStep = isLastStep || state.stop();
  323.             }
  324.             isLastStep = isLastStep || currentState.getTime().subtract(tEnd).abs().getReal() <= JdkMath.ulp(tEnd.getReal());

  325.             // handle the remaining part of the step, after all events if any
  326.             for (FieldStepHandler<T> handler : stepHandlers) {
  327.                 handler.handleStep(restricted, isLastStep);
  328.             }

  329.             return currentState;
  330.     }

  331.     /** Check the integration span.
  332.      * @param eqn set of differential equations
  333.      * @param t target time for the integration
  334.      * @exception NumberIsTooSmallException if integration span is too small
  335.      * @exception DimensionMismatchException if adaptive step size integrators
  336.      * tolerance arrays dimensions are not compatible with equations settings
  337.      */
  338.     protected void sanityChecks(final FieldODEState<T> eqn, final T t)
  339.         throws NumberIsTooSmallException, DimensionMismatchException {

  340.         final double threshold = 1000 * JdkMath.ulp(JdkMath.max(JdkMath.abs(eqn.getTime().getReal()),
  341.                                                                   JdkMath.abs(t.getReal())));
  342.         final double dt = eqn.getTime().subtract(t).abs().getReal();
  343.         if (dt <= threshold) {
  344.             throw new NumberIsTooSmallException(LocalizedFormats.TOO_SMALL_INTEGRATION_INTERVAL,
  345.                                                 dt, threshold, false);
  346.         }
  347.     }

  348.     /** Check if a reset occurred while last step was accepted.
  349.      * @return true if a reset occurred while last step was accepted
  350.      */
  351.     protected boolean resetOccurred() {
  352.         return resetOccurred;
  353.     }

  354.     /** Set the current step size.
  355.      * @param stepSize step size to set
  356.      */
  357.     protected void setStepSize(final T stepSize) {
  358.         this.stepSize = stepSize;
  359.     }

  360.     /** Get the current step size.
  361.      * @return current step size
  362.      */
  363.     protected T getStepSize() {
  364.         return stepSize;
  365.     }
  366.     /** Set current step start.
  367.      * @param stepStart step start
  368.      */
  369.     protected void setStepStart(final FieldODEStateAndDerivative<T> stepStart) {
  370.         this.stepStart = stepStart;
  371.     }

  372.     /** Getcurrent step start.
  373.      * @return current step start
  374.      */
  375.     protected FieldODEStateAndDerivative<T> getStepStart() {
  376.         return stepStart;
  377.     }

  378.     /** Set the last state flag.
  379.      * @param isLastStep if true, this step is the last one
  380.      */
  381.     protected void setIsLastStep(final boolean isLastStep) {
  382.         this.isLastStep = isLastStep;
  383.     }

  384.     /** Check if this step is the last one.
  385.      * @return true if this step is the last one
  386.      */
  387.     protected boolean isLastStep() {
  388.         return isLastStep;
  389.     }
  390. }