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    *      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  /*
19   * This is not the original file distributed by the Apache Software Foundation
20   * It has been modified by the Hipparchus project
21   */
22  
23  package org.hipparchus.ode.nonstiff;
24  
25  
26  import org.hipparchus.CalculusFieldElement;
27  import org.hipparchus.Field;
28  import org.hipparchus.exception.MathIllegalArgumentException;
29  import org.hipparchus.exception.MathIllegalStateException;
30  import org.hipparchus.ode.AbstractFieldIntegrator;
31  import org.hipparchus.ode.FieldEquationsMapper;
32  import org.hipparchus.ode.FieldExpandableODE;
33  import org.hipparchus.ode.FieldODEState;
34  import org.hipparchus.ode.FieldODEStateAndDerivative;
35  import org.hipparchus.util.MathArrays;
36  
37  /**
38   * This class implements the common part of all fixed step Runge-Kutta
39   * integrators for Ordinary Differential Equations.
40   *
41   * <p>These methods are explicit Runge-Kutta methods, their Butcher
42   * arrays are as follows :</p>
43   * <pre>
44   *    0  |
45   *   c2  | a21
46   *   c3  | a31  a32
47   *   ... |        ...
48   *   cs  | as1  as2  ...  ass-1
49   *       |--------------------------
50   *       |  b1   b2  ...   bs-1  bs
51   * </pre>
52   *
53   * @see EulerFieldIntegrator
54   * @see ClassicalRungeKuttaFieldIntegrator
55   * @see GillFieldIntegrator
56   * @see MidpointFieldIntegrator
57   * @param <T> the type of the field elements
58   */
59  
60  public abstract class RungeKuttaFieldIntegrator<T extends CalculusFieldElement<T>>
61      extends AbstractFieldIntegrator<T> implements FieldExplicitRungeKuttaIntegrator<T> {
62  
63      /** Time steps from Butcher array (without the first zero). */
64      private final T[] c;
65  
66      /** Internal weights from Butcher array (without the first empty row). */
67      private final T[][] a;
68  
69      /** External weights for the high order method from Butcher array. */
70      private final T[] b;
71  
72      /** Time steps from Butcher array (without the first zero). */
73      private double[] realC = new double[0];
74  
75      /** Internal weights from Butcher array (without the first empty row). Real version, optional. */
76      private double[][] realA = new double[0][];
77  
78      /** External weights for the high order method from Butcher array. Real version, optional. */
79      private double[] realB = new double[0];
80  
81      /** Integration step. */
82      private final T step;
83  
84      /** Flag setting whether coefficients in Butcher array are interpreted as Field or real numbers. */
85      private boolean usingFieldCoefficients;
86  
87      /** Simple constructor.
88       * Build a Runge-Kutta integrator with the given
89       * step. The default step handler does nothing.
90       * @param field field to which the time and state vector elements belong
91       * @param name name of the method
92       * @param step integration step
93       */
94      protected RungeKuttaFieldIntegrator(final Field<T> field, final String name, final T step) {
95          super(field, name);
96          this.c    = getC();
97          this.a    = getA();
98          this.b    = getB();
99          this.step = step.abs();
100         this.usingFieldCoefficients = false;
101     }
102 
103     /** Getter for the default, positive step-size assigned at constructor level.
104      * @return step
105      */
106     public T getDefaultStep() {
107         return this.step;
108     }
109 
110     /**
111      * Setter for the flag between real or Field coefficients in the Butcher array.
112      *
113      * @param usingFieldCoefficients new value for flag
114      */
115     public void setUsingFieldCoefficients(boolean usingFieldCoefficients) {
116         this.usingFieldCoefficients = usingFieldCoefficients;
117     }
118 
119     /** {@inheritDoc} */
120     @Override
121     public boolean isUsingFieldCoefficients() {
122         return usingFieldCoefficients;
123     }
124 
125     /** {@inheritDoc} */
126     @Override
127     public int getNumberOfStages() {
128         return b.length;
129     }
130 
131     /** Create an interpolator.
132      * @param forward integration direction indicator
133      * @param yDotK slopes at the intermediate points
134      * @param globalPreviousState start of the global step
135      * @param globalCurrentState end of the global step
136      * @param mapper equations mapper for the all equations
137      * @return external weights for the high order method from Butcher array
138      */
139     protected abstract RungeKuttaFieldStateInterpolator<T> createInterpolator(boolean forward, T[][] yDotK,
140                                                                              FieldODEStateAndDerivative<T> globalPreviousState,
141                                                                              FieldODEStateAndDerivative<T> globalCurrentState,
142                                                                              FieldEquationsMapper<T> mapper);
143 
144     /** {@inheritDoc} */
145     @Override
146     protected FieldODEStateAndDerivative<T> initIntegration(FieldExpandableODE<T> eqn, FieldODEState<T> s0, T t) {
147         if (!isUsingFieldCoefficients()) {
148             realA = getRealA();
149             realB = getRealB();
150             realC = getRealC();
151         }
152         return super.initIntegration(eqn, s0, t);
153     }
154 
155     /** {@inheritDoc} */
156     @Override
157     public FieldODEStateAndDerivative<T> integrate(final FieldExpandableODE<T> equations,
158                                                    final FieldODEState<T> initialState, final T finalTime)
159         throws MathIllegalArgumentException, MathIllegalStateException {
160 
161         sanityChecks(initialState, finalTime);
162         setStepStart(initIntegration(equations, initialState, finalTime));
163         final boolean forward = finalTime.subtract(initialState.getTime()).getReal() > 0;
164 
165         // create some internal working arrays
166         final int   stages = getNumberOfStages();
167         final T[][] yDotK  = MathArrays.buildArray(getField(), stages, -1);
168         MathArrays.buildArray(getField(), equations.getMapper().getTotalDimension());
169 
170         // set up integration control objects
171         if (forward) {
172             if (getStepStart().getTime().add(step).subtract(finalTime).getReal() >= 0) {
173                 setStepSize(finalTime.subtract(getStepStart().getTime()));
174             } else {
175                 setStepSize(step);
176             }
177         } else {
178             if (getStepStart().getTime().subtract(step).subtract(finalTime).getReal() <= 0) {
179                 setStepSize(finalTime.subtract(getStepStart().getTime()));
180             } else {
181                 setStepSize(step.negate());
182             }
183         }
184 
185         // main integration loop
186         setIsLastStep(false);
187         do {
188 
189             // first stage
190             final T[] y = getStepStart().getCompleteState();
191             yDotK[0]    = getStepStart().getCompleteDerivative();
192 
193             // next stages
194             final T[] yTmp;
195             if (isUsingFieldCoefficients()) {
196                 FieldExplicitRungeKuttaIntegrator.applyInternalButcherWeights(getEquations(), getStepStart().getTime(),
197                         y, getStepSize(), a, c, yDotK);
198                 yTmp = FieldExplicitRungeKuttaIntegrator.applyExternalButcherWeights(y, yDotK, getStepSize(), b);
199             } else {
200                 FieldExplicitRungeKuttaIntegrator.applyInternalButcherWeights(getEquations(), getStepStart().getTime(),
201                         y, getStepSize(), realA, realC, yDotK);
202                 yTmp = FieldExplicitRungeKuttaIntegrator.applyExternalButcherWeights(y, yDotK, getStepSize(), realB);
203             }
204 
205             incrementEvaluations(stages - 1);
206 
207             final T stepEnd   = getStepStart().getTime().add(getStepSize());
208             final T[] yDotTmp = computeDerivatives(stepEnd, yTmp);
209             final FieldODEStateAndDerivative<T> stateTmp = equations.getMapper().mapStateAndDerivative(stepEnd, yTmp, yDotTmp);
210 
211             // discrete events handling
212             setStepStart(acceptStep(createInterpolator(forward, yDotK, getStepStart(), stateTmp, equations.getMapper()),
213                                     finalTime));
214 
215             if (!isLastStep()) {
216 
217                 // stepsize control for next step
218                 final T  nextT      = getStepStart().getTime().add(getStepSize());
219                 final boolean nextIsLast = forward ?
220                                            (nextT.subtract(finalTime).getReal() >= 0) :
221                                            (nextT.subtract(finalTime).getReal() <= 0);
222                 if (nextIsLast) {
223                     setStepSize(finalTime.subtract(getStepStart().getTime()));
224                 }
225             }
226 
227         } while (!isLastStep());
228 
229         final FieldODEStateAndDerivative<T> finalState = getStepStart();
230         setStepStart(null);
231         setStepSize(null);
232         return finalState;
233 
234     }
235 
236 }