package org.geogebra.common.kernel.cas;
import java.util.ArrayList;
import org.apache.commons.math3.ode.FirstOrderDifferentialEquations;
import org.apache.commons.math3.ode.FirstOrderIntegrator;
import org.apache.commons.math3.ode.nonstiff.ClassicalRungeKuttaIntegrator;
import org.apache.commons.math3.ode.sampling.StepHandler;
import org.apache.commons.math3.ode.sampling.StepInterpolator;
import org.geogebra.common.kernel.Construction;
import org.geogebra.common.kernel.Kernel;
import org.geogebra.common.kernel.MyPoint;
import org.geogebra.common.kernel.SegmentType;
import org.geogebra.common.kernel.algos.AlgoElement;
import org.geogebra.common.kernel.commands.Commands;
import org.geogebra.common.kernel.geos.GeoElement;
import org.geogebra.common.kernel.geos.GeoFunction;
import org.geogebra.common.kernel.geos.GeoFunctionable;
import org.geogebra.common.kernel.geos.GeoLocus;
import org.geogebra.common.kernel.geos.GeoNumeric;
import org.geogebra.common.util.debug.Log;
/**
* Algorithm for second order ODEs
*/
public class AlgoSolveODE2 extends AlgoElement {
private GeoFunction b, c, f; // input
private GeoNumeric x, y, yDot, end, step; // input
// private GeoList g; // output
private GeoLocus locus; // output
/** points of the locus */
ArrayList<MyPoint> al;
/**
* SolveODE[ <b(x)>, <c(x)>, <f(x)>, <Start x>, <Start y>, <Start y'>, <End
* x>, <Step>]
*
* @param cons
* construction
* @param label
* label for output
* @param b
* b
* @param c
* c
* @param f
* function
* @param x
* start x
* @param y
* start y
* @param yDot
* start y'
* @param end
* end parameter
* @param step
* step
*/
public AlgoSolveODE2(Construction cons, String label, GeoFunctionable b,
GeoFunctionable c, GeoFunctionable f, GeoNumeric x, GeoNumeric y,
GeoNumeric yDot, GeoNumeric end, GeoNumeric step) {
super(cons);
this.b = b.getGeoFunction();
this.c = c.getGeoFunction();
this.f = f.getGeoFunction();
this.x = x;
this.y = y;
this.yDot = yDot;
this.end = end;
this.step = step;
// g = new GeoList(cons);
locus = new GeoLocus(cons);
setInputOutput(); // for AlgoElement
compute();
// g.setLabel(label);
locus.setLabel(label);
}
@Override
public Commands getClassName() {
return Commands.SolveODE;
}
// for AlgoElement
@Override
protected void setInputOutput() {
input = new GeoElement[8];
input[0] = b;
input[1] = c;
input[2] = f;
input[3] = x;
input[4] = y;
input[5] = yDot;
input[6] = end;
input[7] = step;
super.setOutputLength(1);
super.setOutput(0, locus);
setDependencies(); // done by AlgoElement
}
/**
* @return resulting locus
*/
public GeoLocus getResult() {
return locus;
}
@Override
public final void compute() {
if (!b.isDefined() || !c.isDefined() || !f.isDefined() || !x.isDefined()
|| !y.isDefined() || !yDot.isDefined() || !step.isDefined()
|| !end.isDefined() || Kernel.isZero(step.getDouble())) {
locus.setUndefined();
return;
}
// g.clear();
if (al == null) {
al = new ArrayList<MyPoint>();
} else {
al.clear();
}
FirstOrderIntegrator integrator = new ClassicalRungeKuttaIntegrator(
step.getDouble());
FirstOrderDifferentialEquations ode;
ode = new ODE2(b, c, f);
integrator.addStepHandler(stepHandler);
// boolean oldState = cons.isSuppressLabelsActive();
// cons.setSuppressLabelCreation(true);
// g.add(new GeoPoint(cons, null, x.getDouble(), y.getDouble(), 1.0));
al.add(new MyPoint(x.getDouble(), y.getDouble(), SegmentType.MOVE_TO));
// cons.setSuppressLabelCreation(oldState);
double[] yy2 = new double[] { y.getDouble(), yDot.getDouble() }; // initial
// state
try {
integrator.integrate(ode, x.getDouble(), yy2, end.getDouble(), yy2);
} catch (RuntimeException e) {
// catches ArithmeticException, IllegalStateException and
// ArithmeticException
e.printStackTrace();
}
locus.setPoints(al);
locus.setDefined(true);
}
private StepHandler stepHandler = new StepHandler() {
private Construction cons1 = kernel.getConstruction();
public void init(double t0, double[] y0, double t) {
Log.error("unimplemented");
}
@Override
public void handleStep(StepInterpolator interpolator, boolean isLast) {
double t = interpolator.getCurrentTime();
double[] y1 = interpolator.getInterpolatedState();
// System.out.println(t + " " + y[0]+ " "+y[1]);
boolean oldState = cons1.isSuppressLabelsActive();
cons1.setSuppressLabelCreation(true);
// g.add(new GeoPoint(cons, null, t, y[0], 1.0));
al.add(new MyPoint(t, y1[0], SegmentType.LINE_TO));
cons1.setSuppressLabelCreation(oldState);
}
};
// integrator.addStepHandler(stepHandler);
private static class ODE2 implements FirstOrderDifferentialEquations {
GeoFunction b, c, f;
public ODE2(GeoFunction b, GeoFunction c, GeoFunction f) {
this.b = b;
this.c = c;
this.f = f;
}
@Override
public int getDimension() {
return 2;
}
/*
* Transform 2nd order into 2 linked first order y0'' + b y0' + c y0 =
* f(x) substitute y0' = y1 (1) y1' + b y1 + c y = f(x) y1' = f(x) - b
* y1 - c y0 (2)
*/
@Override
public void computeDerivatives(double t, double[] y, double[] yDot) {
yDot[0] = y[1]; // (1)
yDot[1] = f.value(t) - b.value(t) * y[1] - c.value(t) * y[0]; // (2)
}
}
}