ExpandableStatefulODE.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.List;

  20. import org.apache.commons.math4.legacy.exception.DimensionMismatchException;
  21. import org.apache.commons.math4.legacy.exception.MaxCountExceededException;


  22. /**
  23.  * This class represents a combined set of first order differential equations,
  24.  * with at least a primary set of equations expandable by some sets of secondary
  25.  * equations.
  26.  * <p>
  27.  * One typical use case is the computation of the Jacobian matrix for some ODE.
  28.  * In this case, the primary set of equations corresponds to the raw ODE, and we
  29.  * add to this set another bunch of secondary equations which represent the Jacobian
  30.  * matrix of the primary set.
  31.  * </p>
  32.  * <p>
  33.  * We want the integrator to use <em>only</em> the primary set to estimate the
  34.  * errors and hence the step sizes. It should <em>not</em> use the secondary
  35.  * equations in this computation. The {@link AbstractIntegrator integrator} will
  36.  * be able to know where the primary set ends and so where the secondary sets begin.
  37.  * </p>
  38.  *
  39.  * @see FirstOrderDifferentialEquations
  40.  * @see JacobianMatrices
  41.  *
  42.  * @since 3.0
  43.  */

  44. public class ExpandableStatefulODE {

  45.     /** Primary differential equation. */
  46.     private final FirstOrderDifferentialEquations primary;

  47.     /** Mapper for primary equation. */
  48.     private final EquationsMapper primaryMapper;

  49.     /** Time. */
  50.     private double time;

  51.     /** State. */
  52.     private final double[] primaryState;

  53.     /** State derivative. */
  54.     private final double[] primaryStateDot;

  55.     /** Components of the expandable ODE. */
  56.     private List<SecondaryComponent> components;

  57.     /** Build an expandable set from its primary ODE set.
  58.      * @param primary the primary set of differential equations to be integrated.
  59.      */
  60.     public ExpandableStatefulODE(final FirstOrderDifferentialEquations primary) {
  61.         final int n          = primary.getDimension();
  62.         this.primary         = primary;
  63.         this.primaryMapper   = new EquationsMapper(0, n);
  64.         this.time            = Double.NaN;
  65.         this.primaryState    = new double[n];
  66.         this.primaryStateDot = new double[n];
  67.         this.components      = new ArrayList<>();
  68.     }

  69.     /** Get the primary set of differential equations.
  70.      * @return primary set of differential equations
  71.      */
  72.     public FirstOrderDifferentialEquations getPrimary() {
  73.         return primary;
  74.     }

  75.     /** Return the dimension of the complete set of equations.
  76.      * <p>
  77.      * The complete set of equations correspond to the primary set plus all secondary sets.
  78.      * </p>
  79.      * @return dimension of the complete set of equations
  80.      */
  81.     public int getTotalDimension() {
  82.         if (components.isEmpty()) {
  83.             // there are no secondary equations, the complete set is limited to the primary set
  84.             return primaryMapper.getDimension();
  85.         } else {
  86.             // there are secondary equations, the complete set ends after the last set
  87.             final EquationsMapper lastMapper = components.get(components.size() - 1).mapper;
  88.             return lastMapper.getFirstIndex() + lastMapper.getDimension();
  89.         }
  90.     }

  91.     /** Get the current time derivative of the complete state vector.
  92.      * @param t current value of the independent <I>time</I> variable
  93.      * @param y array containing the current value of the complete state vector
  94.      * @param yDot placeholder array where to put the time derivative of the complete state vector
  95.      * @exception MaxCountExceededException if the number of functions evaluations is exceeded
  96.      * @exception DimensionMismatchException if arrays dimensions do not match equations settings
  97.      */
  98.     public void computeDerivatives(final double t, final double[] y, final double[] yDot)
  99.         throws MaxCountExceededException, DimensionMismatchException {

  100.         // compute derivatives of the primary equations
  101.         primaryMapper.extractEquationData(y, primaryState);
  102.         primary.computeDerivatives(t, primaryState, primaryStateDot);

  103.         // Add contribution for secondary equations
  104.         for (final SecondaryComponent component : components) {
  105.             component.mapper.extractEquationData(y, component.state);
  106.             component.equation.computeDerivatives(t, primaryState, primaryStateDot,
  107.                                                   component.state, component.stateDot);
  108.             component.mapper.insertEquationData(component.stateDot, yDot);
  109.         }

  110.         primaryMapper.insertEquationData(primaryStateDot, yDot);
  111.     }

  112.     /** Add a set of secondary equations to be integrated along with the primary set.
  113.      * @param secondary secondary equations set
  114.      * @return index of the secondary equation in the expanded state
  115.      */
  116.     public int addSecondaryEquations(final SecondaryEquations secondary) {

  117.         final int firstIndex;
  118.         if (components.isEmpty()) {
  119.             // lazy creation of the components list
  120.             components = new ArrayList<>();
  121.             firstIndex = primary.getDimension();
  122.         } else {
  123.             final SecondaryComponent last = components.get(components.size() - 1);
  124.             firstIndex = last.mapper.getFirstIndex() + last.mapper.getDimension();
  125.         }

  126.         components.add(new SecondaryComponent(secondary, firstIndex));

  127.         return components.size() - 1;
  128.     }

  129.     /** Get an equations mapper for the primary equations set.
  130.      * @return mapper for the primary set
  131.      * @see #getSecondaryMappers()
  132.      */
  133.     public EquationsMapper getPrimaryMapper() {
  134.         return primaryMapper;
  135.     }

  136.     /** Get the equations mappers for the secondary equations sets.
  137.      * @return equations mappers for the secondary equations sets
  138.      * @see #getPrimaryMapper()
  139.      */
  140.     public EquationsMapper[] getSecondaryMappers() {
  141.         final EquationsMapper[] mappers = new EquationsMapper[components.size()];
  142.         for (int i = 0; i < mappers.length; ++i) {
  143.             mappers[i] = components.get(i).mapper;
  144.         }
  145.         return mappers;
  146.     }

  147.     /** Set current time.
  148.      * @param time current time
  149.      */
  150.     public void setTime(final double time) {
  151.         this.time = time;
  152.     }

  153.     /** Get current time.
  154.      * @return current time
  155.      */
  156.     public double getTime() {
  157.         return time;
  158.     }

  159.     /** Set primary part of the current state.
  160.      * @param primaryState primary part of the current state
  161.      * @throws DimensionMismatchException if the dimension of the array does not
  162.      * match the primary set
  163.      */
  164.     public void setPrimaryState(final double[] primaryState) throws DimensionMismatchException {

  165.         // safety checks
  166.         if (primaryState.length != this.primaryState.length) {
  167.             throw new DimensionMismatchException(primaryState.length, this.primaryState.length);
  168.         }

  169.         // set the data
  170.         System.arraycopy(primaryState, 0, this.primaryState, 0, primaryState.length);
  171.     }

  172.     /** Get primary part of the current state.
  173.      * @return primary part of the current state
  174.      */
  175.     public double[] getPrimaryState() {
  176.         return primaryState.clone();
  177.     }

  178.     /** Get primary part of the current state derivative.
  179.      * @return primary part of the current state derivative
  180.      */
  181.     public double[] getPrimaryStateDot() {
  182.         return primaryStateDot.clone();
  183.     }

  184.     /** Set secondary part of the current state.
  185.      * @param index index of the part to set as returned by {@link
  186.      * #addSecondaryEquations(SecondaryEquations)}
  187.      * @param secondaryState secondary part of the current state
  188.      * @throws DimensionMismatchException if the dimension of the partial state does not
  189.      * match the selected equations set dimension
  190.      */
  191.     public void setSecondaryState(final int index, final double[] secondaryState)
  192.         throws DimensionMismatchException {

  193.         // get either the secondary state
  194.         double[] localArray = components.get(index).state;

  195.         // safety checks
  196.         if (secondaryState.length != localArray.length) {
  197.             throw new DimensionMismatchException(secondaryState.length, localArray.length);
  198.         }

  199.         // set the data
  200.         System.arraycopy(secondaryState, 0, localArray, 0, secondaryState.length);
  201.     }

  202.     /** Get secondary part of the current state.
  203.      * @param index index of the part to set as returned by {@link
  204.      * #addSecondaryEquations(SecondaryEquations)}
  205.      * @return secondary part of the current state
  206.      */
  207.     public double[] getSecondaryState(final int index) {
  208.         return components.get(index).state.clone();
  209.     }

  210.     /** Get secondary part of the current state derivative.
  211.      * @param index index of the part to set as returned by {@link
  212.      * #addSecondaryEquations(SecondaryEquations)}
  213.      * @return secondary part of the current state derivative
  214.      */
  215.     public double[] getSecondaryStateDot(final int index) {
  216.         return components.get(index).stateDot.clone();
  217.     }

  218.     /** Set the complete current state.
  219.      * @param completeState complete current state to copy data from
  220.      * @throws DimensionMismatchException if the dimension of the complete state does not
  221.      * match the complete equations sets dimension
  222.      */
  223.     public void setCompleteState(final double[] completeState)
  224.         throws DimensionMismatchException {

  225.         // safety checks
  226.         if (completeState.length != getTotalDimension()) {
  227.             throw new DimensionMismatchException(completeState.length, getTotalDimension());
  228.         }

  229.         // set the data
  230.         primaryMapper.extractEquationData(completeState, primaryState);
  231.         for (final SecondaryComponent component : components) {
  232.             component.mapper.extractEquationData(completeState, component.state);
  233.         }
  234.     }

  235.     /** Get the complete current state.
  236.      * @return complete current state
  237.      * @throws DimensionMismatchException if the dimension of the complete state does not
  238.      * match the complete equations sets dimension
  239.      */
  240.     public double[] getCompleteState() throws DimensionMismatchException {

  241.         // allocate complete array
  242.         double[] completeState = new double[getTotalDimension()];

  243.         // set the data
  244.         primaryMapper.insertEquationData(primaryState, completeState);
  245.         for (final SecondaryComponent component : components) {
  246.             component.mapper.insertEquationData(component.state, completeState);
  247.         }

  248.         return completeState;
  249.     }

  250.     /** Components of the compound stateful ODE. */
  251.     private static final class SecondaryComponent {

  252.         /** Secondary differential equation. */
  253.         private final SecondaryEquations equation;

  254.         /** Mapper between local and complete arrays. */
  255.         private final EquationsMapper mapper;

  256.         /** State. */
  257.         private final double[] state;

  258.         /** State derivative. */
  259.         private final double[] stateDot;

  260.         /** Simple constructor.
  261.          * @param equation secondary differential equation
  262.          * @param firstIndex index to use for the first element in the complete arrays
  263.          */
  264.         SecondaryComponent(final SecondaryEquations equation, final int firstIndex) {
  265.             final int n   = equation.getDimension();
  266.             this.equation = equation;
  267.             mapper        = new EquationsMapper(firstIndex, n);
  268.             state         = new double[n];
  269.             stateDot      = new double[n];
  270.         }
  271.     }
  272. }