View Javadoc
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  
19  import java.util.ArrayList;
20  import java.util.List;
21  
22  import org.apache.commons.math4.legacy.exception.DimensionMismatchException;
23  import org.apache.commons.math4.legacy.exception.MaxCountExceededException;
24  
25  
26  /**
27   * This class represents a combined set of first order differential equations,
28   * with at least a primary set of equations expandable by some sets of secondary
29   * equations.
30   * <p>
31   * One typical use case is the computation of the Jacobian matrix for some ODE.
32   * In this case, the primary set of equations corresponds to the raw ODE, and we
33   * add to this set another bunch of secondary equations which represent the Jacobian
34   * matrix of the primary set.
35   * </p>
36   * <p>
37   * We want the integrator to use <em>only</em> the primary set to estimate the
38   * errors and hence the step sizes. It should <em>not</em> use the secondary
39   * equations in this computation. The {@link AbstractIntegrator integrator} will
40   * be able to know where the primary set ends and so where the secondary sets begin.
41   * </p>
42   *
43   * @see FirstOrderDifferentialEquations
44   * @see JacobianMatrices
45   *
46   * @since 3.0
47   */
48  
49  public class ExpandableStatefulODE {
50  
51      /** Primary differential equation. */
52      private final FirstOrderDifferentialEquations primary;
53  
54      /** Mapper for primary equation. */
55      private final EquationsMapper primaryMapper;
56  
57      /** Time. */
58      private double time;
59  
60      /** State. */
61      private final double[] primaryState;
62  
63      /** State derivative. */
64      private final double[] primaryStateDot;
65  
66      /** Components of the expandable ODE. */
67      private List<SecondaryComponent> components;
68  
69      /** Build an expandable set from its primary ODE set.
70       * @param primary the primary set of differential equations to be integrated.
71       */
72      public ExpandableStatefulODE(final FirstOrderDifferentialEquations primary) {
73          final int n          = primary.getDimension();
74          this.primary         = primary;
75          this.primaryMapper   = new EquationsMapper(0, n);
76          this.time            = Double.NaN;
77          this.primaryState    = new double[n];
78          this.primaryStateDot = new double[n];
79          this.components      = new ArrayList<>();
80      }
81  
82      /** Get the primary set of differential equations.
83       * @return primary set of differential equations
84       */
85      public FirstOrderDifferentialEquations getPrimary() {
86          return primary;
87      }
88  
89      /** Return the dimension of the complete set of equations.
90       * <p>
91       * The complete set of equations correspond to the primary set plus all secondary sets.
92       * </p>
93       * @return dimension of the complete set of equations
94       */
95      public int getTotalDimension() {
96          if (components.isEmpty()) {
97              // there are no secondary equations, the complete set is limited to the primary set
98              return primaryMapper.getDimension();
99          } else {
100             // there are secondary equations, the complete set ends after the last set
101             final EquationsMapper lastMapper = components.get(components.size() - 1).mapper;
102             return lastMapper.getFirstIndex() + lastMapper.getDimension();
103         }
104     }
105 
106     /** Get the current time derivative of the complete state vector.
107      * @param t current value of the independent <I>time</I> variable
108      * @param y array containing the current value of the complete state vector
109      * @param yDot placeholder array where to put the time derivative of the complete state vector
110      * @exception MaxCountExceededException if the number of functions evaluations is exceeded
111      * @exception DimensionMismatchException if arrays dimensions do not match equations settings
112      */
113     public void computeDerivatives(final double t, final double[] y, final double[] yDot)
114         throws MaxCountExceededException, DimensionMismatchException {
115 
116         // compute derivatives of the primary equations
117         primaryMapper.extractEquationData(y, primaryState);
118         primary.computeDerivatives(t, primaryState, primaryStateDot);
119 
120         // Add contribution for secondary equations
121         for (final SecondaryComponent component : components) {
122             component.mapper.extractEquationData(y, component.state);
123             component.equation.computeDerivatives(t, primaryState, primaryStateDot,
124                                                   component.state, component.stateDot);
125             component.mapper.insertEquationData(component.stateDot, yDot);
126         }
127 
128         primaryMapper.insertEquationData(primaryStateDot, yDot);
129     }
130 
131     /** Add a set of secondary equations to be integrated along with the primary set.
132      * @param secondary secondary equations set
133      * @return index of the secondary equation in the expanded state
134      */
135     public int addSecondaryEquations(final SecondaryEquations secondary) {
136 
137         final int firstIndex;
138         if (components.isEmpty()) {
139             // lazy creation of the components list
140             components = new ArrayList<>();
141             firstIndex = primary.getDimension();
142         } else {
143             final SecondaryComponent last = components.get(components.size() - 1);
144             firstIndex = last.mapper.getFirstIndex() + last.mapper.getDimension();
145         }
146 
147         components.add(new SecondaryComponent(secondary, firstIndex));
148 
149         return components.size() - 1;
150     }
151 
152     /** Get an equations mapper for the primary equations set.
153      * @return mapper for the primary set
154      * @see #getSecondaryMappers()
155      */
156     public EquationsMapper getPrimaryMapper() {
157         return primaryMapper;
158     }
159 
160     /** Get the equations mappers for the secondary equations sets.
161      * @return equations mappers for the secondary equations sets
162      * @see #getPrimaryMapper()
163      */
164     public EquationsMapper[] getSecondaryMappers() {
165         final EquationsMapper[] mappers = new EquationsMapper[components.size()];
166         for (int i = 0; i < mappers.length; ++i) {
167             mappers[i] = components.get(i).mapper;
168         }
169         return mappers;
170     }
171 
172     /** Set current time.
173      * @param time current time
174      */
175     public void setTime(final double time) {
176         this.time = time;
177     }
178 
179     /** Get current time.
180      * @return current time
181      */
182     public double getTime() {
183         return time;
184     }
185 
186     /** Set primary part of the current state.
187      * @param primaryState primary part of the current state
188      * @throws DimensionMismatchException if the dimension of the array does not
189      * match the primary set
190      */
191     public void setPrimaryState(final double[] primaryState) throws DimensionMismatchException {
192 
193         // safety checks
194         if (primaryState.length != this.primaryState.length) {
195             throw new DimensionMismatchException(primaryState.length, this.primaryState.length);
196         }
197 
198         // set the data
199         System.arraycopy(primaryState, 0, this.primaryState, 0, primaryState.length);
200     }
201 
202     /** Get primary part of the current state.
203      * @return primary part of the current state
204      */
205     public double[] getPrimaryState() {
206         return primaryState.clone();
207     }
208 
209     /** Get primary part of the current state derivative.
210      * @return primary part of the current state derivative
211      */
212     public double[] getPrimaryStateDot() {
213         return primaryStateDot.clone();
214     }
215 
216     /** Set secondary part of the current state.
217      * @param index index of the part to set as returned by {@link
218      * #addSecondaryEquations(SecondaryEquations)}
219      * @param secondaryState secondary part of the current state
220      * @throws DimensionMismatchException if the dimension of the partial state does not
221      * match the selected equations set dimension
222      */
223     public void setSecondaryState(final int index, final double[] secondaryState)
224         throws DimensionMismatchException {
225 
226         // get either the secondary state
227         double[] localArray = components.get(index).state;
228 
229         // safety checks
230         if (secondaryState.length != localArray.length) {
231             throw new DimensionMismatchException(secondaryState.length, localArray.length);
232         }
233 
234         // set the data
235         System.arraycopy(secondaryState, 0, localArray, 0, secondaryState.length);
236     }
237 
238     /** Get secondary part of the current state.
239      * @param index index of the part to set as returned by {@link
240      * #addSecondaryEquations(SecondaryEquations)}
241      * @return secondary part of the current state
242      */
243     public double[] getSecondaryState(final int index) {
244         return components.get(index).state.clone();
245     }
246 
247     /** Get secondary part of the current state derivative.
248      * @param index index of the part to set as returned by {@link
249      * #addSecondaryEquations(SecondaryEquations)}
250      * @return secondary part of the current state derivative
251      */
252     public double[] getSecondaryStateDot(final int index) {
253         return components.get(index).stateDot.clone();
254     }
255 
256     /** Set the complete current state.
257      * @param completeState complete current state to copy data from
258      * @throws DimensionMismatchException if the dimension of the complete state does not
259      * match the complete equations sets dimension
260      */
261     public void setCompleteState(final double[] completeState)
262         throws DimensionMismatchException {
263 
264         // safety checks
265         if (completeState.length != getTotalDimension()) {
266             throw new DimensionMismatchException(completeState.length, getTotalDimension());
267         }
268 
269         // set the data
270         primaryMapper.extractEquationData(completeState, primaryState);
271         for (final SecondaryComponent component : components) {
272             component.mapper.extractEquationData(completeState, component.state);
273         }
274     }
275 
276     /** Get the complete current state.
277      * @return complete current state
278      * @throws DimensionMismatchException if the dimension of the complete state does not
279      * match the complete equations sets dimension
280      */
281     public double[] getCompleteState() throws DimensionMismatchException {
282 
283         // allocate complete array
284         double[] completeState = new double[getTotalDimension()];
285 
286         // set the data
287         primaryMapper.insertEquationData(primaryState, completeState);
288         for (final SecondaryComponent component : components) {
289             component.mapper.insertEquationData(component.state, completeState);
290         }
291 
292         return completeState;
293     }
294 
295     /** Components of the compound stateful ODE. */
296     private static final class SecondaryComponent {
297 
298         /** Secondary differential equation. */
299         private final SecondaryEquations equation;
300 
301         /** Mapper between local and complete arrays. */
302         private final EquationsMapper mapper;
303 
304         /** State. */
305         private final double[] state;
306 
307         /** State derivative. */
308         private final double[] stateDot;
309 
310         /** Simple constructor.
311          * @param equation secondary differential equation
312          * @param firstIndex index to use for the first element in the complete arrays
313          */
314         SecondaryComponent(final SecondaryEquations equation, final int firstIndex) {
315             final int n   = equation.getDimension();
316             this.equation = equation;
317             mapper        = new EquationsMapper(firstIndex, n);
318             state         = new double[n];
319             stateDot      = new double[n];
320         }
321     }
322 }