ComplexODEConverter.java
/*
* Licensed to the Hipparchus project under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The Hipparchus project licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.hipparchus.ode;
import org.hipparchus.complex.Complex;
/** This class converts {@link ComplexOrdinaryDifferentialEquation complex Ordinary
* Differential Equations} into {@link OrdinaryDifferentialEquation real ones}.
*
* <p>This class is a wrapper around a {@link ComplexOrdinaryDifferentialEquation} which
* allow to use a {@link ODEIntegrator} to integrate it.</p>
*
* <p>The transformation is done by changing the n dimension state
* vector to a 2n dimension vector, where the even components are
* real parts and odd components are imaginary parts.</p>
*
* <p>One should be aware that the data is duplicated during the
* transformation process and that for each call to {@link
* OrdinaryDifferentialEquation#computeDerivatives(double, double[])
* computeDerivatives}, this wrapper does copy 4n scalars : 2n before
* the call to {@link
* OrdinaryDifferentialEquation#computeDerivatives(double, double[])
* computeDerivatives} in order to dispatch the y state vector,
* and 2n after the call to gather zDot. Since the underlying problem
* by itself perhaps also needs to copy data and dispatch the arrays
* into domain objects, this has an impact on both memory and CPU usage.
* The only way to avoid this duplication is to perform the transformation
* at the problem level, i.e. to implement the problem as a first order one
* and then avoid using this class.</p>
*
* <p>
* The proper way to use the converter is as follows:
* </p>
* <pre>
* ODEIntegrator integrator = ...build some integrator...;
* ComplexOrdinaryDifferentialEquation complexEquations = ...set up the complex problem...;
* ComplexODEState initialState = ...set up initial state...;
* ComplexODEConverter converter = new ComplexODEConverter();
* ComplexODEStateAndDerivative finalstate =
* converter.convertStateAndDerivative(integrator.integrate(converter.convertEquations(complexEquations),
* converter.convertState(initialState),
* t);
* </pre>
* <p>
* If there are {@link ComplexSecondaryODE complex secondary equations}, they must be converted
* too and both the converted primary equations and converted secondary equations must be
* combined together using {@link ExpandableODE ExpandableODE} as usual for regular real equations.
* </p>
*
* @see ComplexOrdinaryDifferentialEquation
* @see OrdinaryDifferentialEquation
* @since 1.4
*/
public class ComplexODEConverter {
/** Empty constructor.
* <p>
* This constructor is not strictly necessary, but it prevents spurious
* javadoc warnings with JDK 18 and later.
* </p>
* @since 3.0
*/
public ComplexODEConverter() { // NOPMD - unnecessary constructor added intentionally to make javadoc happy
// nothing to do
}
/** Convert an equations set.
* @param equations equations to convert
* @return converted equations
*/
public OrdinaryDifferentialEquation convertEquations(final ComplexOrdinaryDifferentialEquation equations) {
return new OrdinaryDifferentialEquation() {
/** {@inheritDoc}
* <p>The dimension of the real problem is twice the
* dimension of the underlying complex problem.</p>
* @return dimension of the problem
*/
@Override
public int getDimension() {
return 2 * equations.getDimension();
}
/** {@inheritDoc} */
@Override
public void init(final double t0, final double[] y0, final double finalTime) {
equations.init(t0, convert(y0), finalTime);
}
/** {@inheritDoc} */
@Override
public double[] computeDerivatives(final double t, final double[] y) {
return convert(equations.computeDerivatives(t, convert(y)));
}
};
}
/** Convert a secondary equations set.
* @param equations equations to convert
* @return converted equations
*/
public SecondaryODE convertSecondaryEquations(final ComplexSecondaryODE equations) {
return new SecondaryODE() {
/** {@inheritDoc}
* <p>The dimension of the real problem is twice the
* dimension of the underlying complex problem.</p>
* @return dimension of the problem
*/
@Override
public int getDimension() {
return 2 * equations.getDimension();
}
/** {@inheritDoc} */
@Override
public void init(final double t0, final double[] primary0, final double[] secondary0, final double finalTime) {
equations.init(t0, convert(primary0), convert(secondary0), finalTime);
}
/** {@inheritDoc} */
@Override
public double[] computeDerivatives(final double t, final double[] primary, final double[] primaryDot,
final double[] secondary) {
return convert(equations.computeDerivatives(t, convert(primary), convert(primaryDot), convert(secondary)));
}
};
}
/** Convert a complex state (typically the initial state).
* @param state state to convert
* @return converted state
*/
public ODEState convertState(final ComplexODEState state) {
final double[][] secondary = new double[state.getNumberOfSecondaryStates()][];
for (int index = 0; index < secondary.length; ++index) {
secondary[index] = convert(state.getSecondaryState(index + 1));
}
return new ODEState(state.getTime(),
convert(state.getPrimaryState()),
secondary);
}
/** Convert a real state and derivatives (typically the final state or some intermediate state for
* step handling or event handling).
* @param state state to convert
* @return converted state
*/
public ComplexODEStateAndDerivative convertState(final ODEStateAndDerivative state) {
final Complex[][] secondary = new Complex[state.getNumberOfSecondaryStates()][];
final Complex[][] secondaryDerivative = new Complex[state.getNumberOfSecondaryStates()][];
for (int index = 0; index < secondary.length; ++index) {
secondary[index] = convert(state.getSecondaryState(index + 1));
secondaryDerivative[index] = convert(state.getSecondaryDerivative(index + 1));
}
return new ComplexODEStateAndDerivative(state.getTime(),
convert(state.getPrimaryState()),
convert(state.getPrimaryDerivative()),
secondary, secondaryDerivative);
}
/** Convert a real array into a complex array.
* @param a array to convert
* @return converted array
*/
private Complex[] convert(final double[] a) {
final Complex[] converted = new Complex[a.length / 2];
for (int i = 0; i < converted.length; ++i) {
converted[i] = new Complex(a[2 * i], a[2 * i + 1]);
}
return converted;
}
/** Convert a complex array into a real array.
* @param a array to convert
* @return converted array
*/
private double[] convert(final Complex[] a) {
final double[] converted = new double[a.length * 2];
for (int i = 0; i < a.length; ++i) {
converted[2 * i] = a[i].getReal();
converted[2 * i + 1] = a[i].getImaginary();
}
return converted;
}
}