mned

package module
v0.0.0-...-d3410a1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 25, 2023 License: LGPL-3.0 Imports: 2 Imported by: 0

README

Solvned: A numerical ODE framework for Go

Solvned is a framework for solving ordinary differential equations in Go. It provides a variety of methods to solve these equations, from the simple Euler method to the sophisticated Runge-Kutta-Fehlberg method, as well as the means to define new methods and operate with them in a general way, with interpolation and an event mechanism for iteration until a given point.

Check out the visualization examples at the examples directory and the pkg.go.dev documentation.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func AdaptiveUpdateStep

func AdaptiveUpdateStep(
	step float64, tol float64, err float64, max float64, order uint8,
) float64

Return the new step that should be taken by an AdaptiveStepMethod of the given order and with a given tolerance took a step of a given size yielding the given amount of error, with max as the maximum size of a step.

func StepUntil

func StepUntil(
	s Stepper,
	init Point,
	interp Interpolator,
	callback func(*Point),
	evs ...Event,
) (index int, ok bool)

Starting in the point init, step until the domain of the function ends or one of the events says that the stepping should stop, calculating the intermediante points with the given interpolator and, if `callback` is not nil, calling the given callback with each new point. If ok is false, the iteration ended because the stepper returned false; otherwise index contains the index of the event that made the iteration stop.

Types

type AdaptiveStepMethod

type AdaptiveStepMethod struct {
	Step      float64
	Hmin      float64
	Hmax      float64
	Tolerance float64
	Method    func(*IVP, float64) bool
	Order     uint8
}

An AdaptiveStepMethod is a kind of adaptive method (that is, one that changes the step over time for greater precision and performance) that can be adapted from a fixed-step method.

Step, Hmin, and Hmax are the initial, minimum, and maximum step. If the step must go lower than Hmin to satisfy the tolerance, the stepper ends; if it could get greater than Hmax, it saturates to Hmax. The Tolerance is such that the Euclidean norm of the estimated error of any step of size `h` must be lower than `h*Tolerance`.

Method works the same way as in `FixedStepMethod` except that its error for a given interval of time must be on the order of `O(h^Order)`, with the variable `h` being the step. Order must be positive.

The algorithm used is the following, where `(t,x(t))` are the initial values, `y(h,t,x(t))` is the result of applying the inner method with step `h` from `(t,x(t))`, `k` is the order of the method and `h` is the step.

  1. First, `F:=y(h,t,x(t))` and `H:=y(h/2,t+h/2,y(h/2,t,x(t)))` are obtained, and the error is estimated as `e:=|(1+1/(2^k-1))*(H-F)|`.
  2. If that error is lower than `|h|*Tolerance`, the step is accepted. Otherwise we set `q:=(|h|*Tolerance)/(2*e)`, saturate so that `q` is in `[0.1, 4]`, set `h:=q*h`, check bounds on `h` and return to the start.
  3. The next value is taken to be `(t+h, (2^k*H - H)/(2^k - 1))`, and the next step size is calculated by changing `h` as in step 2.

The steppers are *not* `ConfigurableStepper`s.

func (*AdaptiveStepMethod) Backward

func (m *AdaptiveStepMethod) Backward(ivp *IVP) Stepper

func (*AdaptiveStepMethod) Forward

func (m *AdaptiveStepMethod) Forward(ivp *IVP) Stepper

type CacheSolver

type CacheSolver struct {
	// contains filtered or unexported fields
}

A CacheSolver is like a dynamic version of a DenseSolution. It stores points of the solution of a problem in a conceptually maximal interval that is evaluated lazily; that is, it starts with the solutions in a range that only includes the initial point and, when a point is asked for a Time value outside the range, the range is expanded to cover that point.

func CacheSolve

func CacheSolve(
	m Method, ivp *IVP, interp Interpolator, evs ...Event,
) CacheSolver

Create a CacheSolver to solve the given IVP with the given solving Method, with the given interpolator to calculate points between steps and taking into account the events provided.

func (*CacheSolver) BackwardPoints

func (c *CacheSolver) BackwardPoints() []Point

Get the points calculated from the initial values to earlier times. The points **MUST NOT** be modified.

func (*CacheSolver) End

func (c *CacheSolver) End() float64

Get the time of the stored solution point with the greatest time.

func (*CacheSolver) ForwardPoints

func (c *CacheSolver) ForwardPoints() []Point

Get the points calculated from the initial values to later times. The points **MUST NOT** be modified.

func (*CacheSolver) Get

func (c *CacheSolver) Get(t float64) (x []float64, ok bool)

Get the value of the solution for a given time.

If the value is outside of the bounds of what's stored, values are added until reaching the given time. If ok is false, the value is out of bounds for the problem or some event and points have only been added as far as it was possible.

func (*CacheSolver) PointCoords

func (c *CacheSolver) PointCoords() [][]float64

Rearrange the stored solution points in a result such that the i-th point has time `result[0][i]` and value `(result[1][i],...,result[size][i])`, where `size` is the number of dimensions of a value. This is useful for plotting.

func (*CacheSolver) Start

func (c *CacheSolver) Start() float64

Get the time of the stored solution point with the lowest time.

func (*CacheSolver) StepBackward

func (c *CacheSolver) StepBackward(h float64) ([]float64, bool)

Get the value at `c.Start() - h` as in `c.Get` except that, if the underlying backward Stepper is a ConfigurableStepper, its method `NextStep(-h)` is used. The result value should *NOT* be mutated.

This is most useful with fixed step methods to specify the step.

func (*CacheSolver) StepForward

func (c *CacheSolver) StepForward(h float64) ([]float64, bool)

Get the value at `c.End() + h` as in `c.Get` except that, if the underlying forward Stepper is a ConfigurableStepper, its method `NextStep(h)` is used. The result value should *NOT* be mutated.

This is most useful with fixed step methods to specify the step.

func (*CacheSolver) StepToBeginning

func (c *CacheSolver) StepToBeginning()

Force the generation of values to the left until the domain ends or an event tells the stepping to stop.

func (*CacheSolver) StepToEnd

func (c *CacheSolver) StepToEnd()

Force the generation of values to the right until the domain ends or an event tells the stepping to stop.

type ConfigurableStepper

type ConfigurableStepper interface {
	Stepper
	// Like Stepper.Next but the time of the next point is specified to be
	// the time of the latest point plus the value given.
	NextStep(float64) (*Point, bool)
}

A ConfigurableStepper is a Stepper which allows specifying the delta of the next step; that is, the difference between the time of a point and that of the previous point.

type DenseSolution

type DenseSolution struct {
	// contains filtered or unexported fields
}

A DenseSolution is a structure with precalculated points of a solution within a given interval. It allows getting the value of any point within that interval via interpolation. The zero value is meaningless.

func DenseSolve

func DenseSolve(
	method Method,
	ivp *IVP,
	start float64,
	end float64,
	interp Interpolator,
) (DenseSolution, bool)

Solve an initial value problem in an interval `[start, end]` with the given method. The points are calculated eagerly and, when a point is requested not given by the method, interpolation is used with the given interpolator.

If the second return value is false, the method didn't get to generate any point and the first return value is meaningless. If it's true, the first return value contains a solution for an interval that contains at most two explicitly-stored points outside the interval requested (the two extremes) but which might not contain the whole interval requested if the method stopped.

func (*DenseSolution) End

func (d *DenseSolution) End() float64

Get the latest time of the points in the solution.

func (*DenseSolution) Get

func (d *DenseSolution) Get(time float64) ([]float64, bool)

Get the value for a point in the solution with a given time between `d.Start()` and `d.End()`. If the second return value is false, the point was not in the interval and the first return value is meaningless.

func (*DenseSolution) InnerPoints

func (d *DenseSolution) InnerPoints() []Point

Return the list of all explicitly-calculated points. This slice *MUST NOT* be modified, nor any of the points it contains.

func (*DenseSolution) PointCoords

func (d *DenseSolution) PointCoords() [][]float64

Like CacheSolver.PointCoords but for a DenseSolution.

func (*DenseSolution) Start

func (d *DenseSolution) Start() float64

Get the earliest time of the points in the solution.

type Event

type Event struct {
	Cross     func(*Point) float64
	Tolerance float64
	Action    func(*Point) bool
}

An Event is a condition that can happen in a point in the solution of an initial value problem and an associated action to take. The event happens at the point of the solution where a given function changes its sign.

The Cross function is a continuous function whose zeroes are the points where the event happens. The Tolerance is the margin of error allowed; the maximum absolute value of `Cross(p)` such that `p` is considered to be close enough to an event to be passed to Action. The Action is to be called when an occurrence of the event is found; it can use the point in some way and it returns a boolean indicating whether the calculation should continue.

func (*Event) FindPoint

func (e *Event) FindPoint(i Interpolator, p1 *Point, p2 *Point) Point

If e.Cross has a zero between p1 and p2, find such a zero or a point `p` close enough to the zero that `|e.Cross(p)| < e.Tolerance`. To get the intermediante points, the given interpolator is used.

type FixedStepMethod

type FixedStepMethod struct {
	Step   float64
	Method func(*IVP, float64) bool
}

Create a fixed step method, one where all steps increment or decrement the time by the same amount, with the added condition that it shouldn't depend on extra state. The Step is the fixed increment/decrement of time, which must be positive. The steppers returned by this method are `ConfigurableStepper`s.

The Method is a function that takes an IVP and a step and stores in IVP.Start.Value the value for IVP.Start.Time+step, while leaving IVP.Start.Time untouched. The return value of `method` is true if the operation completed successfully or false if there was a problem, such as going out of the domain of the IVP.Derivative, in which case the resulting state of the IVP is unspecified.

func (FixedStepMethod) Backward

func (m FixedStepMethod) Backward(ivp *IVP) Stepper

func (FixedStepMethod) Forward

func (m FixedStepMethod) Forward(ivp *IVP) Stepper

type HermiteInterpolator

type HermiteInterpolator struct {
	F func(Point) ([]float64, bool) // The derivative function.
}

A HermiteInterpolator is an interpolator that takes into account the derivative of the solution function in the end points.

func HermiteForIVP

func HermiteForIVP(ivp *IVP) HermiteInterpolator

Create a Hermite interpolator suitable for the given IVP.

func (HermiteInterpolator) FindValue

func (h HermiteInterpolator) FindValue(
	p1 *Point, p2 *Point, t float64,
) []float64

type IVP

type IVP struct {
	Derivative func(Point) ([]float64, bool)
	Start      Point
}

An IVP is an initial value problem given by a first-order ODE. It's given by the Derivative function, which is a pure function, and the initial values.

The second return value of Derivative indicates if the Point was in the domain of the function. If it wasn't the first return value should not be used. Ideally, the domain should be restricted to the connected component of the initial value, as otherwise a solving method could "jump" to another component and the results from there on would be invalid.

func (*IVP) Clone

func (ivp *IVP) Clone() IVP

type Interpolator

type Interpolator interface {
	// Approximate the value of a function in a point `t` given two
	// **different** points `p1` and `p2` of the function such that `t`
	// is between `p1.Time` and `p2.Time`.
	FindValue(p1 *Point, p2 *Point, t float64) []float64
}

An Interpolator tries to approximate a function based on a finite set of known points in it. We'll focus just on interpolation with two points since this is precise enough for our purposes, although the interpolator might be constructed with some knowledge about the underlying function.

type LinearInterpolator

type LinearInterpolator struct{}

A LinearInterpolator is an interpolator that assumes that there's a straight line between the two points given. It can be constructed with `new`.

func (LinearInterpolator) FindValue

func (li LinearInterpolator) FindValue(
	p1 *Point, p2 *Point, t float64,
) []float64

type Method

type Method interface {
	// Make a stepper over the solution of the given IVP that starts at the
	// initial values of the problem and goes to ever increasing values for
	// the independent variable.
	Forward(*IVP) Stepper
	// Make a stepper over the solution of the given IVP that starts at the
	// initial values of the problem and goes to ever decreasing values for
	// the independent variable.
	Backward(*IVP) Stepper
}

A Method is a way of approximating points of the solution to an IVP. It works as a factory of Steppers that step forward and backward from the initial values of the problem.

type Point

type Point struct {
	Time  float64   // The independent variable.
	Value []float64 // The dependent variable.
}

A Point is an element of the solution, given by the values of the independent and dependent variables.

func (*Point) Clone

func (p *Point) Clone() Point

Deep-copy a point.

type Stepper

type Stepper interface {
	// Go to the next point and return a pointer to it. If the second value
	// is false, there are no more points in the direction the Stepper is
	// following and the value of Point should not be trusted.
	//
	// The point returned might change after the next call to next() on the
	// same Stepper.
	Next() (*Point, bool)
}

A Stepper is an iterator over points of the solution of an IVP. ODE solver methods generally produce steppers that go from the initial values of the problems to lower or greater values.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL