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