001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.math4.legacy.ode;
018
019import java.util.ArrayList;
020import java.util.List;
021
022import org.apache.commons.math4.legacy.exception.DimensionMismatchException;
023import org.apache.commons.math4.legacy.exception.MaxCountExceededException;
024
025
026/**
027 * This class represents a combined set of first order differential equations,
028 * with at least a primary set of equations expandable by some sets of secondary
029 * equations.
030 * <p>
031 * One typical use case is the computation of the Jacobian matrix for some ODE.
032 * In this case, the primary set of equations corresponds to the raw ODE, and we
033 * add to this set another bunch of secondary equations which represent the Jacobian
034 * matrix of the primary set.
035 * </p>
036 * <p>
037 * We want the integrator to use <em>only</em> the primary set to estimate the
038 * errors and hence the step sizes. It should <em>not</em> use the secondary
039 * equations in this computation. The {@link AbstractIntegrator integrator} will
040 * be able to know where the primary set ends and so where the secondary sets begin.
041 * </p>
042 *
043 * @see FirstOrderDifferentialEquations
044 * @see JacobianMatrices
045 *
046 * @since 3.0
047 */
048
049public class ExpandableStatefulODE {
050
051    /** Primary differential equation. */
052    private final FirstOrderDifferentialEquations primary;
053
054    /** Mapper for primary equation. */
055    private final EquationsMapper primaryMapper;
056
057    /** Time. */
058    private double time;
059
060    /** State. */
061    private final double[] primaryState;
062
063    /** State derivative. */
064    private final double[] primaryStateDot;
065
066    /** Components of the expandable ODE. */
067    private List<SecondaryComponent> components;
068
069    /** Build an expandable set from its primary ODE set.
070     * @param primary the primary set of differential equations to be integrated.
071     */
072    public ExpandableStatefulODE(final FirstOrderDifferentialEquations primary) {
073        final int n          = primary.getDimension();
074        this.primary         = primary;
075        this.primaryMapper   = new EquationsMapper(0, n);
076        this.time            = Double.NaN;
077        this.primaryState    = new double[n];
078        this.primaryStateDot = new double[n];
079        this.components      = new ArrayList<>();
080    }
081
082    /** Get the primary set of differential equations.
083     * @return primary set of differential equations
084     */
085    public FirstOrderDifferentialEquations getPrimary() {
086        return primary;
087    }
088
089    /** Return the dimension of the complete set of equations.
090     * <p>
091     * The complete set of equations correspond to the primary set plus all secondary sets.
092     * </p>
093     * @return dimension of the complete set of equations
094     */
095    public int getTotalDimension() {
096        if (components.isEmpty()) {
097            // there are no secondary equations, the complete set is limited to the primary set
098            return primaryMapper.getDimension();
099        } 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 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}