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 }