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 * http://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 package org.apache.commons.math3.ode; 18 19 import org.apache.commons.math3.exception.DimensionMismatchException; 20 import org.apache.commons.math3.exception.MaxCountExceededException; 21 22 import java.util.ArrayList; 23 import java.util.List; 24 25 /** 26 * This class represents a combined set of first order differential equations, with at least a 27 * primary set of equations expandable by some sets of secondary equations. 28 * 29 * <p>One typical use case is the computation of the Jacobian matrix for some ODE. In this case, the 30 * primary set of equations corresponds to the raw ODE, and we add to this set another bunch of 31 * secondary equations which represent the Jacobian matrix of the primary set. 32 * 33 * <p>We want the integrator to use <em>only</em> the primary set to estimate the errors and hence 34 * the step sizes. It should <em>not</em> use the secondary equations in this computation. The 35 * {@link AbstractIntegrator integrator} will be able to know where the primary set ends and so 36 * where the secondary sets begin. 37 * 38 * @see FirstOrderDifferentialEquations 39 * @see JacobianMatrices 40 * @since 3.0 41 */ 42 public class ExpandableStatefulODE { 43 44 /** Primary differential equation. */ 45 private final FirstOrderDifferentialEquations primary; 46 47 /** Mapper for primary equation. */ 48 private final EquationsMapper primaryMapper; 49 50 /** Time. */ 51 private double time; 52 53 /** State. */ 54 private final double[] primaryState; 55 56 /** State derivative. */ 57 private final double[] primaryStateDot; 58 59 /** Components of the expandable ODE. */ 60 private List<SecondaryComponent> components; 61 62 /** 63 * Build an expandable set from its primary ODE set. 64 * 65 * @param primary the primary set of differential equations to be integrated. 66 */ ExpandableStatefulODE(final FirstOrderDifferentialEquations primary)67 public ExpandableStatefulODE(final FirstOrderDifferentialEquations primary) { 68 final int n = primary.getDimension(); 69 this.primary = primary; 70 this.primaryMapper = new EquationsMapper(0, n); 71 this.time = Double.NaN; 72 this.primaryState = new double[n]; 73 this.primaryStateDot = new double[n]; 74 this.components = new ArrayList<ExpandableStatefulODE.SecondaryComponent>(); 75 } 76 77 /** 78 * Get the primary set of differential equations. 79 * 80 * @return primary set of differential equations 81 */ getPrimary()82 public FirstOrderDifferentialEquations getPrimary() { 83 return primary; 84 } 85 86 /** 87 * Return the dimension of the complete set of equations. 88 * 89 * <p>The complete set of equations correspond to the primary set plus all secondary sets. 90 * 91 * @return dimension of the complete set of equations 92 */ getTotalDimension()93 public int getTotalDimension() { 94 if (components.isEmpty()) { 95 // there are no secondary equations, the complete set is limited to the primary set 96 return primaryMapper.getDimension(); 97 } else { 98 // there are secondary equations, the complete set ends after the last set 99 final EquationsMapper lastMapper = components.get(components.size() - 1).mapper; 100 return lastMapper.getFirstIndex() + lastMapper.getDimension(); 101 } 102 } 103 104 /** 105 * Get the current time derivative of the complete state vector. 106 * 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 */ computeDerivatives(final double t, final double[] y, final double[] yDot)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( 124 t, primaryState, primaryStateDot, component.state, component.stateDot); 125 component.mapper.insertEquationData(component.stateDot, yDot); 126 } 127 128 primaryMapper.insertEquationData(primaryStateDot, yDot); 129 } 130 131 /** 132 * Add a set of secondary equations to be integrated along with the primary set. 133 * 134 * @param secondary secondary equations set 135 * @return index of the secondary equation in the expanded state 136 */ addSecondaryEquations(final SecondaryEquations secondary)137 public int addSecondaryEquations(final SecondaryEquations secondary) { 138 139 final int firstIndex; 140 if (components.isEmpty()) { 141 // lazy creation of the components list 142 components = new ArrayList<ExpandableStatefulODE.SecondaryComponent>(); 143 firstIndex = primary.getDimension(); 144 } else { 145 final SecondaryComponent last = components.get(components.size() - 1); 146 firstIndex = last.mapper.getFirstIndex() + last.mapper.getDimension(); 147 } 148 149 components.add(new SecondaryComponent(secondary, firstIndex)); 150 151 return components.size() - 1; 152 } 153 154 /** 155 * Get an equations mapper for the primary equations set. 156 * 157 * @return mapper for the primary set 158 * @see #getSecondaryMappers() 159 */ getPrimaryMapper()160 public EquationsMapper getPrimaryMapper() { 161 return primaryMapper; 162 } 163 164 /** 165 * Get the equations mappers for the secondary equations sets. 166 * 167 * @return equations mappers for the secondary equations sets 168 * @see #getPrimaryMapper() 169 */ getSecondaryMappers()170 public EquationsMapper[] getSecondaryMappers() { 171 final EquationsMapper[] mappers = new EquationsMapper[components.size()]; 172 for (int i = 0; i < mappers.length; ++i) { 173 mappers[i] = components.get(i).mapper; 174 } 175 return mappers; 176 } 177 178 /** 179 * Set current time. 180 * 181 * @param time current time 182 */ setTime(final double time)183 public void setTime(final double time) { 184 this.time = time; 185 } 186 187 /** 188 * Get current time. 189 * 190 * @return current time 191 */ getTime()192 public double getTime() { 193 return time; 194 } 195 196 /** 197 * Set primary part of the current state. 198 * 199 * @param primaryState primary part of the current state 200 * @throws DimensionMismatchException if the dimension of the array does not match the primary 201 * set 202 */ setPrimaryState(final double[] primaryState)203 public void setPrimaryState(final double[] primaryState) throws DimensionMismatchException { 204 205 // safety checks 206 if (primaryState.length != this.primaryState.length) { 207 throw new DimensionMismatchException(primaryState.length, this.primaryState.length); 208 } 209 210 // set the data 211 System.arraycopy(primaryState, 0, this.primaryState, 0, primaryState.length); 212 } 213 214 /** 215 * Get primary part of the current state. 216 * 217 * @return primary part of the current state 218 */ getPrimaryState()219 public double[] getPrimaryState() { 220 return primaryState.clone(); 221 } 222 223 /** 224 * Get primary part of the current state derivative. 225 * 226 * @return primary part of the current state derivative 227 */ getPrimaryStateDot()228 public double[] getPrimaryStateDot() { 229 return primaryStateDot.clone(); 230 } 231 232 /** 233 * Set secondary part of the current state. 234 * 235 * @param index index of the part to set as returned by {@link 236 * #addSecondaryEquations(SecondaryEquations)} 237 * @param secondaryState secondary part of the current state 238 * @throws DimensionMismatchException if the dimension of the partial state does not match the 239 * selected equations set dimension 240 */ setSecondaryState(final int index, final double[] secondaryState)241 public void setSecondaryState(final int index, final double[] secondaryState) 242 throws DimensionMismatchException { 243 244 // get either the secondary state 245 double[] localArray = components.get(index).state; 246 247 // safety checks 248 if (secondaryState.length != localArray.length) { 249 throw new DimensionMismatchException(secondaryState.length, localArray.length); 250 } 251 252 // set the data 253 System.arraycopy(secondaryState, 0, localArray, 0, secondaryState.length); 254 } 255 256 /** 257 * Get secondary part of the current state. 258 * 259 * @param index index of the part to set as returned by {@link 260 * #addSecondaryEquations(SecondaryEquations)} 261 * @return secondary part of the current state 262 */ getSecondaryState(final int index)263 public double[] getSecondaryState(final int index) { 264 return components.get(index).state.clone(); 265 } 266 267 /** 268 * Get secondary part of the current state derivative. 269 * 270 * @param index index of the part to set as returned by {@link 271 * #addSecondaryEquations(SecondaryEquations)} 272 * @return secondary part of the current state derivative 273 */ getSecondaryStateDot(final int index)274 public double[] getSecondaryStateDot(final int index) { 275 return components.get(index).stateDot.clone(); 276 } 277 278 /** 279 * Set the complete current state. 280 * 281 * @param completeState complete current state to copy data from 282 * @throws DimensionMismatchException if the dimension of the complete state does not match the 283 * complete equations sets dimension 284 */ setCompleteState(final double[] completeState)285 public void setCompleteState(final double[] completeState) throws DimensionMismatchException { 286 287 // safety checks 288 if (completeState.length != getTotalDimension()) { 289 throw new DimensionMismatchException(completeState.length, getTotalDimension()); 290 } 291 292 // set the data 293 primaryMapper.extractEquationData(completeState, primaryState); 294 for (final SecondaryComponent component : components) { 295 component.mapper.extractEquationData(completeState, component.state); 296 } 297 } 298 299 /** 300 * Get the complete current state. 301 * 302 * @return complete current state 303 * @throws DimensionMismatchException if the dimension of the complete state does not match the 304 * complete equations sets dimension 305 */ getCompleteState()306 public double[] getCompleteState() throws DimensionMismatchException { 307 308 // allocate complete array 309 double[] completeState = new double[getTotalDimension()]; 310 311 // set the data 312 primaryMapper.insertEquationData(primaryState, completeState); 313 for (final SecondaryComponent component : components) { 314 component.mapper.insertEquationData(component.state, completeState); 315 } 316 317 return completeState; 318 } 319 320 /** Components of the compound stateful ODE. */ 321 private static class SecondaryComponent { 322 323 /** Secondary differential equation. */ 324 private final SecondaryEquations equation; 325 326 /** Mapper between local and complete arrays. */ 327 private final EquationsMapper mapper; 328 329 /** State. */ 330 private final double[] state; 331 332 /** State derivative. */ 333 private final double[] stateDot; 334 335 /** 336 * Simple constructor. 337 * 338 * @param equation secondary differential equation 339 * @param firstIndex index to use for the first element in the complete arrays 340 */ SecondaryComponent(final SecondaryEquations equation, final int firstIndex)341 SecondaryComponent(final SecondaryEquations equation, final int firstIndex) { 342 final int n = equation.getDimension(); 343 this.equation = equation; 344 mapper = new EquationsMapper(firstIndex, n); 345 state = new double[n]; 346 stateDot = new double[n]; 347 } 348 } 349 } 350