1 /*
2 * Licensed to the Hipparchus project 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 Hipparchus project 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 package org.hipparchus.ode;
19
20 import org.hipparchus.complex.Complex;
21
22 /** This class converts {@link ComplexOrdinaryDifferentialEquation complex Ordinary
23 * Differential Equations} into {@link OrdinaryDifferentialEquation real ones}.
24 *
25 * <p>This class is a wrapper around a {@link ComplexOrdinaryDifferentialEquation} which
26 * allow to use a {@link ODEIntegrator} to integrate it.</p>
27 *
28 * <p>The transformation is done by changing the n dimension state
29 * vector to a 2n dimension vector, where the even components are
30 * real parts and odd components are imaginary parts.</p>
31 *
32 * <p>One should be aware that the data is duplicated during the
33 * transformation process and that for each call to {@link
34 * OrdinaryDifferentialEquation#computeDerivatives(double, double[])
35 * computeDerivatives}, this wrapper does copy 4n scalars : 2n before
36 * the call to {@link
37 * OrdinaryDifferentialEquation#computeDerivatives(double, double[])
38 * computeDerivatives} in order to dispatch the y state vector,
39 * and 2n after the call to gather zDot. Since the underlying problem
40 * by itself perhaps also needs to copy data and dispatch the arrays
41 * into domain objects, this has an impact on both memory and CPU usage.
42 * The only way to avoid this duplication is to perform the transformation
43 * at the problem level, i.e. to implement the problem as a first order one
44 * and then avoid using this class.</p>
45 *
46 * <p>
47 * The proper way to use the converter is as follows:
48 * </p>
49 * <pre>
50 * ODEIntegrator integrator = ...build some integrator...;
51 * ComplexOrdinaryDifferentialEquation complexEquations = ...set up the complex problem...;
52 * ComplexODEState initialState = ...set up initial state...;
53 * ComplexODEConverter converter = new ComplexODEConverter();
54 * ComplexODEStateAndDerivative finalstate =
55 * converter.convertStateAndDerivative(integrator.integrate(converter.convertEquations(complexEquations),
56 * converter.convertState(initialState),
57 * t);
58 * </pre>
59 * <p>
60 * If there are {@link ComplexSecondaryODE complex secondary equations}, they must be converted
61 * too and both the converted primary equations and converted secondary equations must be
62 * combined together using {@link ExpandableODE ExpandableODE} as usual for regular real equations.
63 * </p>
64 *
65 * @see ComplexOrdinaryDifferentialEquation
66 * @see OrdinaryDifferentialEquation
67 * @since 1.4
68 */
69
70 public class ComplexODEConverter {
71
72 /** Empty constructor.
73 * <p>
74 * This constructor is not strictly necessary, but it prevents spurious
75 * javadoc warnings with JDK 18 and later.
76 * </p>
77 * @since 3.0
78 */
79 public ComplexODEConverter() { // NOPMD - unnecessary constructor added intentionally to make javadoc happy
80 // nothing to do
81 }
82
83 /** Convert an equations set.
84 * @param equations equations to convert
85 * @return converted equations
86 */
87 public OrdinaryDifferentialEquation convertEquations(final ComplexOrdinaryDifferentialEquation equations) {
88 return new OrdinaryDifferentialEquation() {
89
90 /** {@inheritDoc}
91 * <p>The dimension of the real problem is twice the
92 * dimension of the underlying complex problem.</p>
93 * @return dimension of the problem
94 */
95 @Override
96 public int getDimension() {
97 return 2 * equations.getDimension();
98 }
99
100 /** {@inheritDoc} */
101 @Override
102 public void init(final double t0, final double[] y0, final double finalTime) {
103 equations.init(t0, convert(y0), finalTime);
104 }
105
106 /** {@inheritDoc} */
107 @Override
108 public double[] computeDerivatives(final double t, final double[] y) {
109 return convert(equations.computeDerivatives(t, convert(y)));
110 }
111
112 };
113 }
114
115 /** Convert a secondary equations set.
116 * @param equations equations to convert
117 * @return converted equations
118 */
119 public SecondaryODE convertSecondaryEquations(final ComplexSecondaryODE equations) {
120 return new SecondaryODE() {
121
122 /** {@inheritDoc}
123 * <p>The dimension of the real problem is twice the
124 * dimension of the underlying complex problem.</p>
125 * @return dimension of the problem
126 */
127 @Override
128 public int getDimension() {
129 return 2 * equations.getDimension();
130 }
131
132 /** {@inheritDoc} */
133 @Override
134 public void init(final double t0, final double[] primary0, final double[] secondary0, final double finalTime) {
135 equations.init(t0, convert(primary0), convert(secondary0), finalTime);
136 }
137
138 /** {@inheritDoc} */
139 @Override
140 public double[] computeDerivatives(final double t, final double[] primary, final double[] primaryDot,
141 final double[] secondary) {
142 return convert(equations.computeDerivatives(t, convert(primary), convert(primaryDot), convert(secondary)));
143 }
144
145 };
146 }
147
148 /** Convert a complex state (typically the initial state).
149 * @param state state to convert
150 * @return converted state
151 */
152 public ODEState convertState(final ComplexODEState state) {
153 final double[][] secondary = new double[state.getNumberOfSecondaryStates()][];
154 for (int index = 0; index < secondary.length; ++index) {
155 secondary[index] = convert(state.getSecondaryState(index + 1));
156 }
157 return new ODEState(state.getTime(),
158 convert(state.getPrimaryState()),
159 secondary);
160 }
161
162 /** Convert a real state and derivatives (typically the final state or some intermediate state for
163 * step handling or event handling).
164 * @param state state to convert
165 * @return converted state
166 */
167 public ComplexODEStateAndDerivative convertState(final ODEStateAndDerivative state) {
168 final Complex[][] secondary = new Complex[state.getNumberOfSecondaryStates()][];
169 final Complex[][] secondaryDerivative = new Complex[state.getNumberOfSecondaryStates()][];
170 for (int index = 0; index < secondary.length; ++index) {
171 secondary[index] = convert(state.getSecondaryState(index + 1));
172 secondaryDerivative[index] = convert(state.getSecondaryDerivative(index + 1));
173 }
174 return new ComplexODEStateAndDerivative(state.getTime(),
175 convert(state.getPrimaryState()),
176 convert(state.getPrimaryDerivative()),
177 secondary, secondaryDerivative);
178 }
179
180 /** Convert a real array into a complex array.
181 * @param a array to convert
182 * @return converted array
183 */
184 private Complex[] convert(final double[] a) {
185 final Complex[] converted = new Complex[a.length / 2];
186 for (int i = 0; i < converted.length; ++i) {
187 converted[i] = new Complex(a[2 * i], a[2 * i + 1]);
188 }
189 return converted;
190 }
191
192 /** Convert a complex array into a real array.
193 * @param a array to convert
194 * @return converted array
195 */
196 private double[] convert(final Complex[] a) {
197 final double[] converted = new double[a.length * 2];
198 for (int i = 0; i < a.length; ++i) {
199 converted[2 * i] = a[i].getReal();
200 converted[2 * i + 1] = a[i].getImaginary();
201 }
202 return converted;
203 }
204
205 }