001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.math4.legacy.ode; 018 019import java.util.ArrayList; 020import java.util.List; 021 022import org.apache.commons.math4.legacy.exception.DimensionMismatchException; 023import org.apache.commons.math4.legacy.exception.MaxCountExceededException; 024 025 026/** 027 * This class represents a combined set of first order differential equations, 028 * with at least a primary set of equations expandable by some sets of secondary 029 * equations. 030 * <p> 031 * One typical use case is the computation of the Jacobian matrix for some ODE. 032 * In this case, the primary set of equations corresponds to the raw ODE, and we 033 * add to this set another bunch of secondary equations which represent the Jacobian 034 * matrix of the primary set. 035 * </p> 036 * <p> 037 * We want the integrator to use <em>only</em> the primary set to estimate the 038 * errors and hence the step sizes. It should <em>not</em> use the secondary 039 * equations in this computation. The {@link AbstractIntegrator integrator} will 040 * be able to know where the primary set ends and so where the secondary sets begin. 041 * </p> 042 * 043 * @see FirstOrderDifferentialEquations 044 * @see JacobianMatrices 045 * 046 * @since 3.0 047 */ 048 049public class ExpandableStatefulODE { 050 051 /** Primary differential equation. */ 052 private final FirstOrderDifferentialEquations primary; 053 054 /** Mapper for primary equation. */ 055 private final EquationsMapper primaryMapper; 056 057 /** Time. */ 058 private double time; 059 060 /** State. */ 061 private final double[] primaryState; 062 063 /** State derivative. */ 064 private final double[] primaryStateDot; 065 066 /** Components of the expandable ODE. */ 067 private List<SecondaryComponent> components; 068 069 /** Build an expandable set from its primary ODE set. 070 * @param primary the primary set of differential equations to be integrated. 071 */ 072 public ExpandableStatefulODE(final FirstOrderDifferentialEquations primary) { 073 final int n = primary.getDimension(); 074 this.primary = primary; 075 this.primaryMapper = new EquationsMapper(0, n); 076 this.time = Double.NaN; 077 this.primaryState = new double[n]; 078 this.primaryStateDot = new double[n]; 079 this.components = new ArrayList<>(); 080 } 081 082 /** Get the primary set of differential equations. 083 * @return primary set of differential equations 084 */ 085 public FirstOrderDifferentialEquations getPrimary() { 086 return primary; 087 } 088 089 /** Return the dimension of the complete set of equations. 090 * <p> 091 * The complete set of equations correspond to the primary set plus all secondary sets. 092 * </p> 093 * @return dimension of the complete set of equations 094 */ 095 public int getTotalDimension() { 096 if (components.isEmpty()) { 097 // there are no secondary equations, the complete set is limited to the primary set 098 return primaryMapper.getDimension(); 099 } else { 100 // there are secondary equations, the complete set ends after the last set 101 final EquationsMapper lastMapper = components.get(components.size() - 1).mapper; 102 return lastMapper.getFirstIndex() + lastMapper.getDimension(); 103 } 104 } 105 106 /** Get the current time derivative of the complete state vector. 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 */ 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(t, primaryState, primaryStateDot, 124 component.state, component.stateDot); 125 component.mapper.insertEquationData(component.stateDot, yDot); 126 } 127 128 primaryMapper.insertEquationData(primaryStateDot, yDot); 129 } 130 131 /** Add a set of secondary equations to be integrated along with the primary set. 132 * @param secondary secondary equations set 133 * @return index of the secondary equation in the expanded state 134 */ 135 public int addSecondaryEquations(final SecondaryEquations secondary) { 136 137 final int firstIndex; 138 if (components.isEmpty()) { 139 // lazy creation of the components list 140 components = new ArrayList<>(); 141 firstIndex = primary.getDimension(); 142 } else { 143 final SecondaryComponent last = components.get(components.size() - 1); 144 firstIndex = last.mapper.getFirstIndex() + last.mapper.getDimension(); 145 } 146 147 components.add(new SecondaryComponent(secondary, firstIndex)); 148 149 return components.size() - 1; 150 } 151 152 /** Get an equations mapper for the primary equations set. 153 * @return mapper for the primary set 154 * @see #getSecondaryMappers() 155 */ 156 public EquationsMapper getPrimaryMapper() { 157 return primaryMapper; 158 } 159 160 /** Get the equations mappers for the secondary equations sets. 161 * @return equations mappers for the secondary equations sets 162 * @see #getPrimaryMapper() 163 */ 164 public EquationsMapper[] getSecondaryMappers() { 165 final EquationsMapper[] mappers = new EquationsMapper[components.size()]; 166 for (int i = 0; i < mappers.length; ++i) { 167 mappers[i] = components.get(i).mapper; 168 } 169 return mappers; 170 } 171 172 /** Set current time. 173 * @param time current time 174 */ 175 public void setTime(final double time) { 176 this.time = time; 177 } 178 179 /** Get current time. 180 * @return current time 181 */ 182 public double getTime() { 183 return time; 184 } 185 186 /** Set primary part of the current state. 187 * @param primaryState primary part of the current state 188 * @throws DimensionMismatchException if the dimension of the array does not 189 * match the primary set 190 */ 191 public void setPrimaryState(final double[] primaryState) throws DimensionMismatchException { 192 193 // safety checks 194 if (primaryState.length != this.primaryState.length) { 195 throw new DimensionMismatchException(primaryState.length, this.primaryState.length); 196 } 197 198 // set the data 199 System.arraycopy(primaryState, 0, this.primaryState, 0, primaryState.length); 200 } 201 202 /** Get primary part of the current state. 203 * @return primary part of the current state 204 */ 205 public double[] getPrimaryState() { 206 return primaryState.clone(); 207 } 208 209 /** Get primary part of the current state derivative. 210 * @return primary part of the current state derivative 211 */ 212 public double[] getPrimaryStateDot() { 213 return primaryStateDot.clone(); 214 } 215 216 /** Set secondary part of the current state. 217 * @param index index of the part to set as returned by {@link 218 * #addSecondaryEquations(SecondaryEquations)} 219 * @param secondaryState secondary part of the current state 220 * @throws DimensionMismatchException if the dimension of the partial state does not 221 * match the selected equations set dimension 222 */ 223 public void setSecondaryState(final int index, final double[] secondaryState) 224 throws DimensionMismatchException { 225 226 // get either the secondary state 227 double[] localArray = components.get(index).state; 228 229 // safety checks 230 if (secondaryState.length != localArray.length) { 231 throw new DimensionMismatchException(secondaryState.length, localArray.length); 232 } 233 234 // set the data 235 System.arraycopy(secondaryState, 0, localArray, 0, secondaryState.length); 236 } 237 238 /** Get secondary part of the current state. 239 * @param index index of the part to set as returned by {@link 240 * #addSecondaryEquations(SecondaryEquations)} 241 * @return secondary part of the current state 242 */ 243 public double[] getSecondaryState(final int index) { 244 return components.get(index).state.clone(); 245 } 246 247 /** Get secondary part of the current state derivative. 248 * @param index index of the part to set as returned by {@link 249 * #addSecondaryEquations(SecondaryEquations)} 250 * @return secondary part of the current state derivative 251 */ 252 public double[] getSecondaryStateDot(final int index) { 253 return components.get(index).stateDot.clone(); 254 } 255 256 /** Set the complete current state. 257 * @param completeState complete current state to copy data from 258 * @throws DimensionMismatchException if the dimension of the complete state does not 259 * match the complete equations sets dimension 260 */ 261 public void setCompleteState(final double[] completeState) 262 throws DimensionMismatchException { 263 264 // safety checks 265 if (completeState.length != getTotalDimension()) { 266 throw new DimensionMismatchException(completeState.length, getTotalDimension()); 267 } 268 269 // set the data 270 primaryMapper.extractEquationData(completeState, primaryState); 271 for (final SecondaryComponent component : components) { 272 component.mapper.extractEquationData(completeState, component.state); 273 } 274 } 275 276 /** Get the complete current state. 277 * @return complete current state 278 * @throws DimensionMismatchException if the dimension of the complete state does not 279 * match the complete equations sets dimension 280 */ 281 public double[] getCompleteState() throws DimensionMismatchException { 282 283 // allocate complete array 284 double[] completeState = new double[getTotalDimension()]; 285 286 // set the data 287 primaryMapper.insertEquationData(primaryState, completeState); 288 for (final SecondaryComponent component : components) { 289 component.mapper.insertEquationData(component.state, completeState); 290 } 291 292 return completeState; 293 } 294 295 /** Components of the compound stateful ODE. */ 296 private static class SecondaryComponent { 297 298 /** Secondary differential equation. */ 299 private final SecondaryEquations equation; 300 301 /** Mapper between local and complete arrays. */ 302 private final EquationsMapper mapper; 303 304 /** State. */ 305 private final double[] state; 306 307 /** State derivative. */ 308 private final double[] stateDot; 309 310 /** Simple constructor. 311 * @param equation secondary differential equation 312 * @param firstIndex index to use for the first element in the complete arrays 313 */ 314 SecondaryComponent(final SecondaryEquations equation, final int firstIndex) { 315 final int n = equation.getDimension(); 316 this.equation = equation; 317 mapper = new EquationsMapper(firstIndex, n); 318 state = new double[n]; 319 stateDot = new double[n]; 320 } 321 } 322}