| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF 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 |
| * |
| * http://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.apache.commons.math4.ode; |
| |
| import org.apache.commons.math4.exception.DimensionMismatchException; |
| import org.apache.commons.math4.exception.MaxCountExceededException; |
| import org.apache.commons.math4.exception.NoBracketingException; |
| import org.apache.commons.math4.exception.NumberIsTooSmallException; |
| import org.apache.commons.math4.ode.AbstractIntegrator; |
| import org.apache.commons.math4.ode.AbstractParameterizable; |
| import org.apache.commons.math4.ode.ExpandableStatefulODE; |
| import org.apache.commons.math4.ode.FirstOrderDifferentialEquations; |
| import org.apache.commons.math4.ode.FirstOrderIntegrator; |
| import org.apache.commons.math4.ode.JacobianMatrices; |
| import org.apache.commons.math4.ode.MainStateJacobianProvider; |
| import org.apache.commons.math4.ode.ParameterJacobianProvider; |
| import org.apache.commons.math4.ode.ParameterizedODE; |
| import org.apache.commons.math4.ode.UnknownParameterException; |
| import org.apache.commons.math4.ode.JacobianMatrices.MismatchedEquations; |
| import org.apache.commons.math4.ode.nonstiff.DormandPrince54Integrator; |
| import org.apache.commons.math4.stat.descriptive.SummaryStatistics; |
| import org.apache.commons.math4.util.FastMath; |
| import org.junit.Assert; |
| import org.junit.Test; |
| |
| public class JacobianMatricesTest { |
| |
| @Test |
| public void testLowAccuracyExternalDifferentiation() |
| throws NumberIsTooSmallException, DimensionMismatchException, |
| MaxCountExceededException, NoBracketingException { |
| // this test does not really test JacobianMatrices, |
| // it only shows that WITHOUT this class, attempting to recover |
| // the jacobians from external differentiation on simple integration |
| // results with low accuracy gives very poor results. In fact, |
| // the curves dy/dp = g(b) when b varies from 2.88 to 3.08 are |
| // essentially noise. |
| // This test is taken from Hairer, Norsett and Wanner book |
| // Solving Ordinary Differential Equations I (Nonstiff problems), |
| // the curves dy/dp = g(b) are in figure 6.5 |
| FirstOrderIntegrator integ = |
| new DormandPrince54Integrator(1.0e-8, 100.0, new double[] { 1.0e-4, 1.0e-4 }, new double[] { 1.0e-4, 1.0e-4 }); |
| double hP = 1.0e-12; |
| SummaryStatistics residualsP0 = new SummaryStatistics(); |
| SummaryStatistics residualsP1 = new SummaryStatistics(); |
| for (double b = 2.88; b < 3.08; b += 0.001) { |
| Brusselator brusselator = new Brusselator(b); |
| double[] y = { 1.3, b }; |
| integ.integrate(brusselator, 0, y, 20.0, y); |
| double[] yP = { 1.3, b + hP }; |
| integ.integrate(brusselator, 0, yP, 20.0, yP); |
| residualsP0.addValue((yP[0] - y[0]) / hP - brusselator.dYdP0()); |
| residualsP1.addValue((yP[1] - y[1]) / hP - brusselator.dYdP1()); |
| } |
| Assert.assertTrue((residualsP0.getMax() - residualsP0.getMin()) > 500); |
| Assert.assertTrue(residualsP0.getStandardDeviation() > 30); |
| Assert.assertTrue((residualsP1.getMax() - residualsP1.getMin()) > 700); |
| Assert.assertTrue(residualsP1.getStandardDeviation() > 40); |
| } |
| |
| @Test |
| public void testHighAccuracyExternalDifferentiation() |
| throws NumberIsTooSmallException, DimensionMismatchException, |
| MaxCountExceededException, NoBracketingException, UnknownParameterException { |
| FirstOrderIntegrator integ = |
| new DormandPrince54Integrator(1.0e-8, 100.0, new double[] { 1.0e-10, 1.0e-10 }, new double[] { 1.0e-10, 1.0e-10 }); |
| double hP = 1.0e-12; |
| SummaryStatistics residualsP0 = new SummaryStatistics(); |
| SummaryStatistics residualsP1 = new SummaryStatistics(); |
| for (double b = 2.88; b < 3.08; b += 0.001) { |
| ParamBrusselator brusselator = new ParamBrusselator(b); |
| double[] y = { 1.3, b }; |
| integ.integrate(brusselator, 0, y, 20.0, y); |
| double[] yP = { 1.3, b + hP }; |
| brusselator.setParameter("b", b + hP); |
| integ.integrate(brusselator, 0, yP, 20.0, yP); |
| residualsP0.addValue((yP[0] - y[0]) / hP - brusselator.dYdP0()); |
| residualsP1.addValue((yP[1] - y[1]) / hP - brusselator.dYdP1()); |
| } |
| Assert.assertTrue((residualsP0.getMax() - residualsP0.getMin()) > 0.02); |
| Assert.assertTrue((residualsP0.getMax() - residualsP0.getMin()) < 0.03); |
| Assert.assertTrue(residualsP0.getStandardDeviation() > 0.003); |
| Assert.assertTrue(residualsP0.getStandardDeviation() < 0.004); |
| Assert.assertTrue((residualsP1.getMax() - residualsP1.getMin()) > 0.04); |
| Assert.assertTrue((residualsP1.getMax() - residualsP1.getMin()) < 0.05); |
| Assert.assertTrue(residualsP1.getStandardDeviation() > 0.007); |
| Assert.assertTrue(residualsP1.getStandardDeviation() < 0.008); |
| } |
| |
| @Test |
| public void testWrongParameterName() { |
| final String name = "an-unknown-parameter"; |
| try { |
| ParamBrusselator brusselator = new ParamBrusselator(2.9); |
| brusselator.setParameter(name, 3.0); |
| Assert.fail("an exception should have been thrown"); |
| } catch (UnknownParameterException upe) { |
| Assert.assertTrue(upe.getMessage().contains(name)); |
| Assert.assertEquals(name, upe.getName()); |
| } |
| } |
| |
| @Test |
| public void testInternalDifferentiation() |
| throws NumberIsTooSmallException, DimensionMismatchException, |
| MaxCountExceededException, NoBracketingException, |
| UnknownParameterException, MismatchedEquations { |
| AbstractIntegrator integ = |
| new DormandPrince54Integrator(1.0e-8, 100.0, new double[] { 1.0e-4, 1.0e-4 }, new double[] { 1.0e-4, 1.0e-4 }); |
| double hP = 1.0e-12; |
| double hY = 1.0e-12; |
| SummaryStatistics residualsP0 = new SummaryStatistics(); |
| SummaryStatistics residualsP1 = new SummaryStatistics(); |
| for (double b = 2.88; b < 3.08; b += 0.001) { |
| ParamBrusselator brusselator = new ParamBrusselator(b); |
| brusselator.setParameter(ParamBrusselator.B, b); |
| double[] z = { 1.3, b }; |
| double[][] dZdZ0 = new double[2][2]; |
| double[] dZdP = new double[2]; |
| |
| JacobianMatrices jacob = new JacobianMatrices(brusselator, new double[] { hY, hY }, ParamBrusselator.B); |
| jacob.setParameterizedODE(brusselator); |
| jacob.setParameterStep(ParamBrusselator.B, hP); |
| jacob.setInitialParameterJacobian(ParamBrusselator.B, new double[] { 0.0, 1.0 }); |
| |
| ExpandableStatefulODE efode = new ExpandableStatefulODE(brusselator); |
| efode.setTime(0); |
| efode.setPrimaryState(z); |
| jacob.registerVariationalEquations(efode); |
| |
| integ.setMaxEvaluations(5000); |
| integ.integrate(efode, 20.0); |
| jacob.getCurrentMainSetJacobian(dZdZ0); |
| jacob.getCurrentParameterJacobian(ParamBrusselator.B, dZdP); |
| // Assert.assertEquals(5000, integ.getMaxEvaluations()); |
| // Assert.assertTrue(integ.getEvaluations() > 1500); |
| // Assert.assertTrue(integ.getEvaluations() < 2100); |
| // Assert.assertEquals(4 * integ.getEvaluations(), integ.getEvaluations()); |
| residualsP0.addValue(dZdP[0] - brusselator.dYdP0()); |
| residualsP1.addValue(dZdP[1] - brusselator.dYdP1()); |
| } |
| Assert.assertTrue((residualsP0.getMax() - residualsP0.getMin()) < 0.02); |
| Assert.assertTrue(residualsP0.getStandardDeviation() < 0.003); |
| Assert.assertTrue((residualsP1.getMax() - residualsP1.getMin()) < 0.05); |
| Assert.assertTrue(residualsP1.getStandardDeviation() < 0.01); |
| } |
| |
| @Test |
| public void testAnalyticalDifferentiation() |
| throws MaxCountExceededException, DimensionMismatchException, |
| NumberIsTooSmallException, NoBracketingException, |
| UnknownParameterException, MismatchedEquations { |
| AbstractIntegrator integ = |
| new DormandPrince54Integrator(1.0e-8, 100.0, new double[] { 1.0e-4, 1.0e-4 }, new double[] { 1.0e-4, 1.0e-4 }); |
| SummaryStatistics residualsP0 = new SummaryStatistics(); |
| SummaryStatistics residualsP1 = new SummaryStatistics(); |
| for (double b = 2.88; b < 3.08; b += 0.001) { |
| Brusselator brusselator = new Brusselator(b); |
| double[] z = { 1.3, b }; |
| double[][] dZdZ0 = new double[2][2]; |
| double[] dZdP = new double[2]; |
| |
| JacobianMatrices jacob = new JacobianMatrices(brusselator, Brusselator.B); |
| jacob.addParameterJacobianProvider(brusselator); |
| jacob.setInitialParameterJacobian(Brusselator.B, new double[] { 0.0, 1.0 }); |
| |
| ExpandableStatefulODE efode = new ExpandableStatefulODE(brusselator); |
| efode.setTime(0); |
| efode.setPrimaryState(z); |
| jacob.registerVariationalEquations(efode); |
| |
| integ.setMaxEvaluations(5000); |
| integ.integrate(efode, 20.0); |
| jacob.getCurrentMainSetJacobian(dZdZ0); |
| jacob.getCurrentParameterJacobian(Brusselator.B, dZdP); |
| // Assert.assertEquals(5000, integ.getMaxEvaluations()); |
| // Assert.assertTrue(integ.getEvaluations() > 350); |
| // Assert.assertTrue(integ.getEvaluations() < 510); |
| residualsP0.addValue(dZdP[0] - brusselator.dYdP0()); |
| residualsP1.addValue(dZdP[1] - brusselator.dYdP1()); |
| } |
| Assert.assertTrue((residualsP0.getMax() - residualsP0.getMin()) < 0.014); |
| Assert.assertTrue(residualsP0.getStandardDeviation() < 0.003); |
| Assert.assertTrue((residualsP1.getMax() - residualsP1.getMin()) < 0.05); |
| Assert.assertTrue(residualsP1.getStandardDeviation() < 0.01); |
| } |
| |
| @Test |
| public void testFinalResult() |
| throws MaxCountExceededException, DimensionMismatchException, |
| NumberIsTooSmallException, NoBracketingException, |
| UnknownParameterException, MismatchedEquations { |
| |
| AbstractIntegrator integ = |
| new DormandPrince54Integrator(1.0e-8, 100.0, new double[] { 1.0e-10, 1.0e-10 }, new double[] { 1.0e-10, 1.0e-10 }); |
| double[] y = new double[] { 0.0, 1.0 }; |
| Circle circle = new Circle(y, 1.0, 1.0, 0.1); |
| |
| JacobianMatrices jacob = new JacobianMatrices(circle, Circle.CX, Circle.CY, Circle.OMEGA); |
| jacob.addParameterJacobianProvider(circle); |
| jacob.setInitialMainStateJacobian(circle.exactDyDy0(0)); |
| jacob.setInitialParameterJacobian(Circle.CX, circle.exactDyDcx(0)); |
| jacob.setInitialParameterJacobian(Circle.CY, circle.exactDyDcy(0)); |
| jacob.setInitialParameterJacobian(Circle.OMEGA, circle.exactDyDom(0)); |
| |
| ExpandableStatefulODE efode = new ExpandableStatefulODE(circle); |
| efode.setTime(0); |
| efode.setPrimaryState(y); |
| jacob.registerVariationalEquations(efode); |
| |
| integ.setMaxEvaluations(5000); |
| |
| double t = 18 * FastMath.PI; |
| integ.integrate(efode, t); |
| y = efode.getPrimaryState(); |
| for (int i = 0; i < y.length; ++i) { |
| Assert.assertEquals(circle.exactY(t)[i], y[i], 1.0e-9); |
| } |
| |
| double[][] dydy0 = new double[2][2]; |
| jacob.getCurrentMainSetJacobian(dydy0); |
| for (int i = 0; i < dydy0.length; ++i) { |
| for (int j = 0; j < dydy0[i].length; ++j) { |
| Assert.assertEquals(circle.exactDyDy0(t)[i][j], dydy0[i][j], 1.0e-9); |
| } |
| } |
| double[] dydcx = new double[2]; |
| jacob.getCurrentParameterJacobian(Circle.CX, dydcx); |
| for (int i = 0; i < dydcx.length; ++i) { |
| Assert.assertEquals(circle.exactDyDcx(t)[i], dydcx[i], 1.0e-7); |
| } |
| double[] dydcy = new double[2]; |
| jacob.getCurrentParameterJacobian(Circle.CY, dydcy); |
| for (int i = 0; i < dydcy.length; ++i) { |
| Assert.assertEquals(circle.exactDyDcy(t)[i], dydcy[i], 1.0e-7); |
| } |
| double[] dydom = new double[2]; |
| jacob.getCurrentParameterJacobian(Circle.OMEGA, dydom); |
| for (int i = 0; i < dydom.length; ++i) { |
| Assert.assertEquals(circle.exactDyDom(t)[i], dydom[i], 1.0e-7); |
| } |
| } |
| |
| @Test |
| public void testParameterizable() |
| throws MaxCountExceededException, DimensionMismatchException, |
| NumberIsTooSmallException, NoBracketingException, |
| UnknownParameterException, MismatchedEquations { |
| |
| AbstractIntegrator integ = |
| new DormandPrince54Integrator(1.0e-8, 100.0, new double[] { 1.0e-10, 1.0e-10 }, new double[] { 1.0e-10, 1.0e-10 }); |
| double[] y = new double[] { 0.0, 1.0 }; |
| ParameterizedCircle pcircle = new ParameterizedCircle(y, 1.0, 1.0, 0.1); |
| |
| double hP = 1.0e-12; |
| double hY = 1.0e-12; |
| |
| JacobianMatrices jacob = new JacobianMatrices(pcircle, new double[] { hY, hY }, |
| ParameterizedCircle.CX, ParameterizedCircle.CY, |
| ParameterizedCircle.OMEGA); |
| jacob.setParameterizedODE(pcircle); |
| jacob.setParameterStep(ParameterizedCircle.CX, hP); |
| jacob.setParameterStep(ParameterizedCircle.CY, hP); |
| jacob.setParameterStep(ParameterizedCircle.OMEGA, hP); |
| jacob.setInitialMainStateJacobian(pcircle.exactDyDy0(0)); |
| jacob.setInitialParameterJacobian(ParameterizedCircle.CX, pcircle.exactDyDcx(0)); |
| jacob.setInitialParameterJacobian(ParameterizedCircle.CY, pcircle.exactDyDcy(0)); |
| jacob.setInitialParameterJacobian(ParameterizedCircle.OMEGA, pcircle.exactDyDom(0)); |
| |
| ExpandableStatefulODE efode = new ExpandableStatefulODE(pcircle); |
| efode.setTime(0); |
| efode.setPrimaryState(y); |
| jacob.registerVariationalEquations(efode); |
| |
| integ.setMaxEvaluations(50000); |
| |
| double t = 18 * FastMath.PI; |
| integ.integrate(efode, t); |
| y = efode.getPrimaryState(); |
| for (int i = 0; i < y.length; ++i) { |
| Assert.assertEquals(pcircle.exactY(t)[i], y[i], 1.0e-9); |
| } |
| |
| double[][] dydy0 = new double[2][2]; |
| jacob.getCurrentMainSetJacobian(dydy0); |
| for (int i = 0; i < dydy0.length; ++i) { |
| for (int j = 0; j < dydy0[i].length; ++j) { |
| Assert.assertEquals(pcircle.exactDyDy0(t)[i][j], dydy0[i][j], 5.0e-4); |
| } |
| } |
| |
| double[] dydp0 = new double[2]; |
| jacob.getCurrentParameterJacobian(ParameterizedCircle.CX, dydp0); |
| for (int i = 0; i < dydp0.length; ++i) { |
| Assert.assertEquals(pcircle.exactDyDcx(t)[i], dydp0[i], 5.0e-4); |
| } |
| |
| double[] dydp1 = new double[2]; |
| jacob.getCurrentParameterJacobian(ParameterizedCircle.OMEGA, dydp1); |
| for (int i = 0; i < dydp1.length; ++i) { |
| Assert.assertEquals(pcircle.exactDyDom(t)[i], dydp1[i], 1.0e-2); |
| } |
| } |
| |
| private static class Brusselator extends AbstractParameterizable |
| implements MainStateJacobianProvider, ParameterJacobianProvider { |
| |
| public static final String B = "b"; |
| |
| private double b; |
| |
| public Brusselator(double b) { |
| super(B); |
| this.b = b; |
| } |
| |
| @Override |
| public int getDimension() { |
| return 2; |
| } |
| |
| @Override |
| public void computeDerivatives(double t, double[] y, double[] yDot) { |
| double prod = y[0] * y[0] * y[1]; |
| yDot[0] = 1 + prod - (b + 1) * y[0]; |
| yDot[1] = b * y[0] - prod; |
| } |
| |
| @Override |
| public void computeMainStateJacobian(double t, double[] y, double[] yDot, |
| double[][] dFdY) { |
| double p = 2 * y[0] * y[1]; |
| double y02 = y[0] * y[0]; |
| dFdY[0][0] = p - (1 + b); |
| dFdY[0][1] = y02; |
| dFdY[1][0] = b - p; |
| dFdY[1][1] = -y02; |
| } |
| |
| @Override |
| public void computeParameterJacobian(double t, double[] y, double[] yDot, |
| String paramName, double[] dFdP) { |
| if (isSupported(paramName)) { |
| dFdP[0] = -y[0]; |
| dFdP[1] = y[0]; |
| } else { |
| dFdP[0] = 0; |
| dFdP[1] = 0; |
| } |
| } |
| |
| public double dYdP0() { |
| return -1088.232716447743 + (1050.775747149553 + (-339.012934631828 + 36.52917025056327 * b) * b) * b; |
| } |
| |
| public double dYdP1() { |
| return 1502.824469929139 + (-1438.6974831849952 + (460.959476642384 - 49.43847385647082 * b) * b) * b; |
| } |
| |
| } |
| |
| private static class ParamBrusselator extends AbstractParameterizable |
| implements FirstOrderDifferentialEquations, ParameterizedODE { |
| |
| public static final String B = "b"; |
| |
| private double b; |
| |
| public ParamBrusselator(double b) { |
| super(B); |
| this.b = b; |
| } |
| |
| @Override |
| public int getDimension() { |
| return 2; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public double getParameter(final String name) |
| throws UnknownParameterException { |
| complainIfNotSupported(name); |
| return b; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public void setParameter(final String name, final double value) |
| throws UnknownParameterException { |
| complainIfNotSupported(name); |
| b = value; |
| } |
| |
| @Override |
| public void computeDerivatives(double t, double[] y, double[] yDot) { |
| double prod = y[0] * y[0] * y[1]; |
| yDot[0] = 1 + prod - (b + 1) * y[0]; |
| yDot[1] = b * y[0] - prod; |
| } |
| |
| public double dYdP0() { |
| return -1088.232716447743 + (1050.775747149553 + (-339.012934631828 + 36.52917025056327 * b) * b) * b; |
| } |
| |
| public double dYdP1() { |
| return 1502.824469929139 + (-1438.6974831849952 + (460.959476642384 - 49.43847385647082 * b) * b) * b; |
| } |
| |
| } |
| |
| /** ODE representing a point moving on a circle with provided center and angular rate. */ |
| private static class Circle extends AbstractParameterizable |
| implements MainStateJacobianProvider, ParameterJacobianProvider { |
| |
| public static final String CX = "cx"; |
| public static final String CY = "cy"; |
| public static final String OMEGA = "omega"; |
| |
| private final double[] y0; |
| private double cx; |
| private double cy; |
| private double omega; |
| |
| public Circle(double[] y0, double cx, double cy, double omega) { |
| super(CX,CY,OMEGA); |
| this.y0 = y0.clone(); |
| this.cx = cx; |
| this.cy = cy; |
| this.omega = omega; |
| } |
| |
| @Override |
| public int getDimension() { |
| return 2; |
| } |
| |
| @Override |
| public void computeDerivatives(double t, double[] y, double[] yDot) { |
| yDot[0] = omega * (cy - y[1]); |
| yDot[1] = omega * (y[0] - cx); |
| } |
| |
| @Override |
| public void computeMainStateJacobian(double t, double[] y, |
| double[] yDot, double[][] dFdY) { |
| dFdY[0][0] = 0; |
| dFdY[0][1] = -omega; |
| dFdY[1][0] = omega; |
| dFdY[1][1] = 0; |
| } |
| |
| @Override |
| public void computeParameterJacobian(double t, double[] y, double[] yDot, |
| String paramName, double[] dFdP) |
| throws UnknownParameterException { |
| complainIfNotSupported(paramName); |
| if (paramName.equals(CX)) { |
| dFdP[0] = 0; |
| dFdP[1] = -omega; |
| } else if (paramName.equals(CY)) { |
| dFdP[0] = omega; |
| dFdP[1] = 0; |
| } else { |
| dFdP[0] = cy - y[1]; |
| dFdP[1] = y[0] - cx; |
| } |
| } |
| |
| public double[] exactY(double t) { |
| double cos = FastMath.cos(omega * t); |
| double sin = FastMath.sin(omega * t); |
| double dx0 = y0[0] - cx; |
| double dy0 = y0[1] - cy; |
| return new double[] { |
| cx + cos * dx0 - sin * dy0, |
| cy + sin * dx0 + cos * dy0 |
| }; |
| } |
| |
| public double[][] exactDyDy0(double t) { |
| double cos = FastMath.cos(omega * t); |
| double sin = FastMath.sin(omega * t); |
| return new double[][] { |
| { cos, -sin }, |
| { sin, cos } |
| }; |
| } |
| |
| public double[] exactDyDcx(double t) { |
| double cos = FastMath.cos(omega * t); |
| double sin = FastMath.sin(omega * t); |
| return new double[] {1 - cos, -sin}; |
| } |
| |
| public double[] exactDyDcy(double t) { |
| double cos = FastMath.cos(omega * t); |
| double sin = FastMath.sin(omega * t); |
| return new double[] {sin, 1 - cos}; |
| } |
| |
| public double[] exactDyDom(double t) { |
| double cos = FastMath.cos(omega * t); |
| double sin = FastMath.sin(omega * t); |
| double dx0 = y0[0] - cx; |
| double dy0 = y0[1] - cy; |
| return new double[] { -t * (sin * dx0 + cos * dy0) , t * (cos * dx0 - sin * dy0) }; |
| } |
| |
| } |
| |
| /** ODE representing a point moving on a circle with provided center and angular rate. */ |
| private static class ParameterizedCircle extends AbstractParameterizable |
| implements FirstOrderDifferentialEquations, ParameterizedODE { |
| |
| public static final String CX = "cx"; |
| public static final String CY = "cy"; |
| public static final String OMEGA = "omega"; |
| |
| private final double[] y0; |
| private double cx; |
| private double cy; |
| private double omega; |
| |
| public ParameterizedCircle(double[] y0, double cx, double cy, double omega) { |
| super(CX,CY,OMEGA); |
| this.y0 = y0.clone(); |
| this.cx = cx; |
| this.cy = cy; |
| this.omega = omega; |
| } |
| |
| @Override |
| public int getDimension() { |
| return 2; |
| } |
| |
| @Override |
| public void computeDerivatives(double t, double[] y, double[] yDot) { |
| yDot[0] = omega * (cy - y[1]); |
| yDot[1] = omega * (y[0] - cx); |
| } |
| |
| @Override |
| public double getParameter(final String name) |
| throws UnknownParameterException { |
| if (name.equals(CX)) { |
| return cx; |
| } else if (name.equals(CY)) { |
| return cy; |
| } else if (name.equals(OMEGA)) { |
| return omega; |
| } else { |
| throw new UnknownParameterException(name); |
| } |
| } |
| |
| @Override |
| public void setParameter(final String name, final double value) |
| throws UnknownParameterException { |
| if (name.equals(CX)) { |
| cx = value; |
| } else if (name.equals(CY)) { |
| cy = value; |
| } else if (name.equals(OMEGA)) { |
| omega = value; |
| } else { |
| throw new UnknownParameterException(name); |
| } |
| } |
| |
| public double[] exactY(double t) { |
| double cos = FastMath.cos(omega * t); |
| double sin = FastMath.sin(omega * t); |
| double dx0 = y0[0] - cx; |
| double dy0 = y0[1] - cy; |
| return new double[] { |
| cx + cos * dx0 - sin * dy0, |
| cy + sin * dx0 + cos * dy0 |
| }; |
| } |
| |
| public double[][] exactDyDy0(double t) { |
| double cos = FastMath.cos(omega * t); |
| double sin = FastMath.sin(omega * t); |
| return new double[][] { |
| { cos, -sin }, |
| { sin, cos } |
| }; |
| } |
| |
| public double[] exactDyDcx(double t) { |
| double cos = FastMath.cos(omega * t); |
| double sin = FastMath.sin(omega * t); |
| return new double[] {1 - cos, -sin}; |
| } |
| |
| public double[] exactDyDcy(double t) { |
| double cos = FastMath.cos(omega * t); |
| double sin = FastMath.sin(omega * t); |
| return new double[] {sin, 1 - cos}; |
| } |
| |
| public double[] exactDyDom(double t) { |
| double cos = FastMath.cos(omega * t); |
| double sin = FastMath.sin(omega * t); |
| double dx0 = y0[0] - cx; |
| double dy0 = y0[1] - cy; |
| return new double[] { -t * (sin * dx0 + cos * dy0) , t * (cos * dx0 - sin * dy0) }; |
| } |
| |
| } |
| |
| } |