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.FieldODEStateInterpolator;
  43. import org.hipparchus.ode.sampling.FieldODEStepHandler;
  44. import org.hipparchus.util.FastMath;
  45. import org.hipparchus.util.Incrementor;

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

  51.     /** Step handler. */
  52.     private final List<FieldODEStepHandler<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 final List<FieldDetectorBasedEventState<T>> detectorBasedEventsStates;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  199.         setStateInitialized(false);

  200.         return s0WithDerivatives;

  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 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 MathIllegalArgumentException if arrays dimensions do not match equations settings
  219.      * @exception MathIllegalStateException 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.      * CalculusFieldElement) integrate}
  223.      */
  224.     public T[] computeDerivatives(final T t, final T[] y)
  225.         throws MathIllegalArgumentException, MathIllegalStateException, NullPointerException {
  226.         evaluations.increment();
  227.         return equations.computeDerivatives(t, y);
  228.     }

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

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

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

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

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

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

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

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

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


  294.             do {

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

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

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

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

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

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

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

  326.                     if (isLastStep) {

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

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

  338.                     }

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

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

  356.                     // prepare handling of the remaining part of the step
  357.                     previousState = eventState;
  358.                     restricted = restricted.restrictStep(eventState, currentState);

  359.                     if (action == Action.RESET_EVENTS) {
  360.                         continue resetEvents;
  361.                     }

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

  368.                 }

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

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

  382.             doneWithStep = true;
  383.         } while (!doneWithStep);


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

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

  392.         return currentState;

  393.     }

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

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

  410.     }

  411.     /** Check if a reset occurred while last step was accepted.
  412.      * @return true if a reset occurred while last step was accepted
  413.      */
  414.     protected boolean resetOccurred() {
  415.         return resetOccurred;
  416.     }

  417.     /** Set the current step size.
  418.      * @param stepSize step size to set
  419.      */
  420.     protected void setStepSize(final T stepSize) {
  421.         this.stepSize = stepSize;
  422.     }

  423.     /** Get the current step size.
  424.      * @return current step size
  425.      */
  426.     protected T getStepSize() {
  427.         return stepSize;
  428.     }

  429.     /** Set current step start.
  430.      * @param stepStart step start
  431.      */
  432.     protected void setStepStart(final FieldODEStateAndDerivative<T> stepStart) {
  433.         this.stepStart = stepStart;
  434.     }

  435.     /**  {@inheritDoc} */
  436.     @Override
  437.     public FieldODEStateAndDerivative<T> getStepStart() {
  438.         return stepStart;
  439.     }

  440.     /** Set the last state flag.
  441.      * @param isLastStep if true, this step is the last one
  442.      */
  443.     protected void setIsLastStep(final boolean isLastStep) {
  444.         this.isLastStep = isLastStep;
  445.     }

  446.     /** Check if this step is the last one.
  447.      * @return true if this step is the last one
  448.      */
  449.     protected boolean isLastStep() {
  450.         return isLastStep;
  451.     }

  452. }