package umontreal.iro.lecuyer.simevents;

import java.util.List;
import java.util.Collections;
import java.util.ArrayList;


/**
 * Represents the portion of the simulator's state associated with
 * continuous-time simulation.
 * Any simulator, including the default static one, can have an associate continuous state
 * which is obtained using the continuousState() method
 * of the {@link Simulator} class.
 * This state includes all active integration variables as well as the current integration method.
 * 
 * 

* One of the methods {@link #selectEuler selectEuler}, {@link #selectRungeKutta2 selectRungeKutta2} or * {@link #selectRungeKutta4 selectRungeKutta4} must be called before starting * any integration. * These methods permit one to select the numerical integration method * and the step size h (in time units) that will be used * for all continuous-time variables linked to the simulator. * For all the methods, an integration step at time t changes * the values of the variables from their old values at time t - h to their * new values at time t. * *

* Each integration step is scheduled as an event and added to the event list. * */ public class ContinuousState { // Integration methods public enum IntegMethod{ EULER, // Euler integration method RUNGEKUTTA2, // Runge-Kutta integration method of order 2 RUNGEKUTTA4 // Runge-Kutta integration method of order 4 } private double stepSize; // Integration step size. private IntegMethod integMethod; // Integration method in use. private int order; // Order of the method in use. private double[] A = new double[4]; private double[] B = new double[4]; private double[] C = new double[4]; // The event that actually executes integration steps. private StepEvent stepEv = null; // Class of event that executes an integration step. private class StepEvent extends Event { public StepEvent(Simulator sim) { super(sim); } public void actions() { switch (integMethod) { case EULER: oneStepEuler(); break; case RUNGEKUTTA2: oneStepRK(); break; case RUNGEKUTTA4: oneStepRK(); break; default: throw new IllegalArgumentException ("Integration step with undefined method"); } this.schedule (stepSize); // if (afterInteg != null) afterInteg.actions(); } public String toString() { return "Integration step for continuous variable "; } } private List list; private Simulator sim; /** * Creates a new {@link ContinuousState} object linked to the given simulator. * Usually, the user should not call this constructor directly since a new object * is created automatically by the continuousState() method of * class {@link Simulator}. * */ protected ContinuousState (Simulator sim) { this.list = new ArrayList(); this.sim = sim; assert sim != null; } /** * Returns the list of continuous-time variables currently * integrated by the simulator. * The returned list is updated automatically as variables are added or removed, but it * cannot be modified directly. One must instead use * startInteg or stopInteg in class {@link Continuous} to add * or remove variables. * * */ public List getContinuousVariables() { return Collections.unmodifiableList (list); } /** * Starts the integration process that will change the state of * {@link Continuous} variable at each integration step. * */ protected void startInteg(Continuous c) { // The following is done only the first time this method is called. if (stepEv == null) stepEv = new StepEvent(sim); c.active = true; // Inserts this in list of active variables. if (list.isEmpty()) { stepEv.schedule (stepSize); } // There was no active variable. list.add(c); } protected void stopInteg(Continuous c) { c.active = false; list.remove(c); if (list.isEmpty()) stepEv.cancel(); } /** * Return an integer that represent the integration method in use. * * @return Interger that represent the integration method in use. * */ public IntegMethod integMethod () { return integMethod; } /** * Selects the Euler method as the integration method, * with the integration step size h, in time units. * * @param h integration step, in simulation time units * * */ public void selectEuler (double h) { integMethod = IntegMethod.EULER; stepSize = h; } /** * Selects a Runge-Kutta method of order 2 as the integration * method to be used, with step size h. * * @param h integration step, in simulation time units * * */ public void selectRungeKutta2 (double h) { integMethod = IntegMethod.RUNGEKUTTA2; stepSize = h; order = 2; A[0] = 1.0; A[1] = 0.0; B[0] = 0.5; B[1] = 0.5; C[0] = 0.0; C[1] = 1.0; } /** * Selects a Runge-Kutta method of order 4 as the integration * method to be used, with step size h. * * @param h integration step, in simulation time units * * */ public void selectRungeKutta4 (double h) { integMethod = IntegMethod.RUNGEKUTTA4; stepSize = h; order = 4; A[0] = 0.5; A[1] = 0.5; A[2] = 1.0; A[3] = 0.0; B[0] = 1.0/6.0; B[1] = 1.0/3.0; B[2] = 1.0/3.0; B[3] = 1.0/6.0; C[0] = 0.0; C[1] = 0.5; C[2] = 0.5; C[3] = 1.0; } private void oneStepEuler() { Continuous v; double t = sim.time() - stepSize; int current; current = list.size(); while (current > 0) { v = list.get(--current); v.phi = v.value + stepSize * v.derivative (t); } current = list.size(); while (current > 0) { v = list.get(--current); v.value = v.phi; if (v.ev != null) v.ev.scheduleNext(); v.afterEachStep(); } } private void oneStepRK() { Continuous v; double t = sim.time() - stepSize; int current = list.size(); while (current > 0) { v = list.get(--current); v.buffer = v.value; v.sum = 0.0; v.pi = 0.0; } for (int i = 1; i <= order-1; i++) { current = list.size(); while (current > 0) { v = list.get(--current); v.pi = v.derivative (t + stepSize * C[i-1]); v.sum = v.sum + v.pi * B[i-1]; v.phi = v.buffer + stepSize * v.pi * A[i-1]; } current = list.size(); while (current > 0) { v = list.get(--current); v.value = v.phi; } } current = list.size(); while (current > 0) { v = list.get(--current); v.pi = v.derivative (t + stepSize * C[order-1]); v.value = v.buffer + stepSize * (v.sum + v.pi * B[order-1]); if (v.ev != null) v.ev.scheduleNext(); v.afterEachStep(); } } }

Ads help maintain this website.