►V Reference
On this page:
58.16.1 Deterministic ODE Flow and Streamlines
58.16.1.1 Solver State Spaces
ode-state-space
real-ode-state-space
vec2-ode-state-space
vec3-ode-state-space
numeric-vector-ode-state-space
58.16.1.2 Direct Fixed-Step Integration
ode-flow-position
58.16.1.3 Adaptive Configuration and Events
adaptive-rk45
adaptive-rk45?
ode-event
ode-event?
58.16.1.4 Prepared Trajectories
ode-trajectory?
prepare-ode-trajectory
ode-trajectory-time-range
ode-trajectory-step-size
ode-trajectory-checkpoint-every
ode-trajectory-solver
ode-trajectory-diagnostics
ode-trajectory-diagnostics?
ode-trajectory-position
58.16.1.5 Streamlines and Flow Particles
streamline-points
streamline
streamlines
flow-particle
9.3

58.16 ODE Trajectories and Streamlines🔗ℹ

Use ode-flow-position for a direct fixed-step calculation, prepare-ode-trajectory for repeated time lookup, and streamline or flow-particle for scene content. Fixed RK4 checkpoints and adaptive RK45 dense output have different preparation and lookup behavior, described below. Ordinary function and data plots are in Function, Parametric, and Data Plots.

See also Function, Parametric, and Data Plots, Relations and Live Layout.

    58.16.1 Deterministic ODE Flow and Streamlines

      58.16.1.1 Solver State Spaces

      58.16.1.2 Direct Fixed-Step Integration

      58.16.1.3 Adaptive Configuration and Events

      58.16.1.4 Prepared Trajectories

      58.16.1.5 Streamlines and Flow Particles

58.16.1 Deterministic ODE Flow and Streamlines🔗ℹ

The ODE operations turn a two-dimensional vector field into reproducible integral-curve geometry. A two-argument field is autonomous; a three-argument field receives time first. Fixed-step fourth-order Runge–Kutta (RK4) is the default, with the caller selecting the step size and number of streamline steps. Neither the direct numerical solver nor a prepared trajectory depends on a prior rendered frame. prepare-ode-trajectory stores immutable canonical RK4 checkpoints, so an animated particle does not recompute the complete seed-to-time prefix for every frame. The renderer batches selected frame times by checkpoint interval, sharing each interval’s full RK4 suffix steps.

An optional deterministic adaptive Dormand–Prince 5(4) backend is also available. It stores accepted endpoint derivatives for cubic Hermite dense lookup, accepts time-dependent fields, can stop at one scalar sign-crossing event, and records immutable diagnostics. Once an adaptive trajectory is prepared, dense lookup and frame rendering never invoke the author field.

Both trajectory families express their numerical operations through an immutable ode-state-space. The built-in real-ode-state-space, vec2-ode-state-space, and vec3-ode-state-space values make the shared RK4 and RK45 algorithms explicit; (numeric-vector-ode-state-space n) creates a fixed-length immutable numeric-vector state space. These values are useful when building a numerical extension, while the ordinary two-dimensional and spatial trajectory constructors remain the author-facing API.

58.16.1.1 Solver State Spaces🔗ℹ

struct

(struct ode-state-space (dimension
    add
    subtract
    scale
    norm
    interpolate
    finite?)
    #:transparent)
  dimension : exact-positive-integer?
  add : procedure?
  subtract : procedure?
  scale : procedure?
  norm : procedure?
  interpolate : procedure?
  finite? : procedure?
Describes finite vector-space operations for the shared solver kernel. Its procedures must consistently operate on the declared dimension; finite? recognizes the immutable state representation. Prepared numeric-vector states are immutable to prevent a retained mutable input from changing a trajectory.
The one-dimensional real state space.
The two-dimensional vec2 state space.
The three-dimensional vec3 state space.

procedure

(numeric-vector-ode-state-space dimension) → ode-state-space?

  dimension : exact-positive-integer?
Creates a space whose states are immutable vectors of exactly dimension finite reals.

58.16.1.2 Direct Fixed-Step Integration🔗ℹ

procedure

(ode-flow-position field    
  seed    
  time    
  [#:step-size step-size]) → vec2?
  field : 
(or/c (procedure-arity-includes/c 2)
      (procedure-arity-includes/c 3))
  seed : vec2?
  time : finite-real?
  step-size : (and/c finite-real? positive?) = 1/20
Returns the RK4 solution beginning at coordinate-space seed at time zero and integrated through signed time. A two-argument field receives numeric coordinate x and y values; a three-argument field receives time, x, and y. It must return one finite vec2 derivative. A positive time advances the field; a negative time integrates it backwards.

The final partial step is included, so the requested time is reached exactly in ordinary arithmetic rather than rounded to a step-grid endpoint. This is a fixed-step solver, not an adaptive tolerance-controlled integrator.

A constant field gives uniform motion from the seed.

Examples:
> (define position
    (ode-flow-position (lambda (x y) (vec2 1 0))
                       origin 1 #:step-size 1/4))
> (= (vec2-x position) 1)

#t

> (= (vec2-y position) 0)

#t

58.16.1.3 Adaptive Configuration and Events🔗ℹ

procedure

(adaptive-rk45 [#:relative-tolerance relative-tolerance 
  #:absolute-tolerance absolute-tolerance 
  #:initial-step initial-step 
  #:minimum-step minimum-step 
  #:maximum-step maximum-step 
  #:maximum-steps maximum-steps]) 
 → adaptive-rk45?
  relative-tolerance : (and/c finite-real? positive?) = 1e-6
  absolute-tolerance : (and/c finite-real? positive?) = 1e-8
  initial-step : (and/c finite-real? positive?) = 1/10
  minimum-step : (and/c finite-real? positive?) = 1e-8
  maximum-step : (and/c finite-real? positive?) = 1
  maximum-steps : exact-positive-integer? = 100000
Creates immutable configuration for the deterministic Dormand–Prince embedded 5(4) adaptive solver. The step bounds must satisfy minimum-step <= initial-step <= maximum-step. The relative and absolute tolerances form the usual componentwise scale atol + rtol * max (abs (previous) ,abs (candidate)).

procedure

(adaptive-rk45? value) → boolean?

  value : any/c
Recognizes immutable adaptive Dormand–Prince solver configuration.

procedure

(ode-event function    
  [#:direction direction    
  #:name name]) → ode-event?
  function : 
(or/c (procedure-arity-includes/c 2)
      (procedure-arity-includes/c 3))
  direction : (or/c 'any 'increasing 'decreasing) = 'any
  name : symbol? = 'event
Creates one terminal scalar event for adaptive preparation. Like a field, function accepts either (x y) or (time x y) and must return one finite real. A sign crossing ends the trajectory; increasing and decreasing select the crossing orientation. The root is located by deterministic bisection over the accepted step’s cubic dense output.

procedure

(ode-event? value) → boolean?

  value : any/c
Recognizes an adaptive terminal event declaration.

58.16.1.4 Prepared Trajectories🔗ℹ

procedure

(ode-trajectory? value) → boolean?

  value : any/c
Returns #t for an immutable prepared fixed-RK4 or adaptive-RK45 trajectory.

procedure

(prepare-ode-trajectory field 
  seed 
  #:time-range time-range 
  [#:step-size step-size 
  #:checkpoint-every checkpoint-every 
  #:solver solver 
  #:event event]) 
 → ode-trajectory?
  field : 
(or/c (procedure-arity-includes/c 2)
      (procedure-arity-includes/c 3))
  seed : vec2?
  time-range : (cons/c finite-real? finite-real?)
  step-size : (and/c finite-real? positive?) = 1/20
  checkpoint-every : exact-positive-integer? = 16
  solver : (or/c false/c adaptive-rk45?) = #f
  event : (or/c false/c ode-event?) = #f
Prepares a closed time range expressed as (cons start-time end-time), where start-time is at most end-time. The trajectory stores immutable states at canonical multiples of step-size, spaced by checkpoint-every full steps in both the positive and negative directions from the seed at time zero.

For a lookup, the trajectory begins at the preceding checkpoint between zero and the requested time, takes fewer than checkpoint-every full steps, and then takes the usual final remainder step. It therefore preserves the fixed-RK4 numerical meaning of ode-flow-position without repeating a long prefix for each frame.

The field must be pure and stable for the lifetime of the prepared value. The library cannot determine whether an arbitrary Racket procedure’s captured state has changed.

When solver is #f, this is the established fixed-RK4 checkpoint trajectory. event is then rejected. With an adaptive-rk45? value, accepted Dormand–Prince endpoint positions and derivatives are stored instead. event, when supplied, truncates the actual supported range at its dense scalar root.

procedure

(ode-trajectory-time-range trajectory)

 → (cons/c finite-real? finite-real?)
  trajectory : ode-trajectory?
Returns the supported closed time range. For a trajectory stopped by an adaptive event, the relevant endpoint is the detected dense root rather than the original requested boundary.

procedure

(ode-trajectory-step-size trajectory)

 → (or/c (and/c finite-real? positive?) false/c)
  trajectory : ode-trajectory?
Returns the fixed RK4 step size, or #f for an adaptive trajectory.

procedure

(ode-trajectory-checkpoint-every trajectory)

 → (or/c exact-positive-integer? false/c)
  trajectory : ode-trajectory?
Returns the number of full RK4 steps between stored canonical checkpoints, or #f for an adaptive trajectory.

procedure

(ode-trajectory-solver trajectory)

 → (or/c 'fixed-rk4 adaptive-rk45?)
  trajectory : ode-trajectory?
Returns 'fixed-rk4 for the established checkpoint backend or the immutable adaptive-rk45? value that prepared an adaptive trajectory.

procedure

(ode-trajectory-diagnostics trajectory)

 → (or/c false/c ode-trajectory-diagnostics?)
  trajectory : ode-trajectory?
Returns #f for fixed RK4. An adaptive trajectory returns transparent diagnostics containing solver name, accepted and rejected step counts, termination time/reason, and the maximum componentwise scaled embedded error. The error is a local control diagnostic, not a global proof of solution error.

procedure

(ode-trajectory-diagnostics? value) → boolean?

  value : any/c
Recognizes the immutable diagnostics returned for a prepared adaptive ODE trajectory.

procedure

(ode-trajectory-position trajectory time) → vec2?

  trajectory : ode-trajectory?
  time : finite-real?
Returns the prepared position at time. Fixed trajectories reproduce the existing checkpoint/remainder RK4 path. Adaptive trajectories use stored cubic Hermite dense output and do not call the field. The time must lie inside the actual supported range, which may end early at an event root.

58.16.1.5 Streamlines and Flow Particles🔗ℹ

procedure

(streamline-points field    
  seed    
  [#:direction direction    
  #:step-size step-size    
  #:steps steps]) → (listof vec2?)
  field : 
(or/c (procedure-arity-includes/c 2)
      (procedure-arity-includes/c 3))
  seed : vec2?
  direction : (or/c 'forward 'backward 'both) = 'both
  step-size : (and/c finite-real? positive?) = 1/20
  steps : exact-positive-integer? = 120
Returns the coordinate-space RK4 samples for one streamline. 'forward goes from time zero through positive time; 'backward returns points in increasing geometric order from negative time to the seed; 'both combines both directions without duplicating the seed.

procedure

(streamline axes    
  field    
  seed    
  #:id id    
  [#:direction direction    
  #:step-size step-size    
  #:steps steps    
  #:opacity opacity    
  #:stroke stroke    
  #:stroke-width stroke-width]) → path-visual?
  axes : axes-visual?
  field : 
(or/c (procedure-arity-includes/c 2)
      (procedure-arity-includes/c 3))
  seed : vec2?
  id : symbol?
  direction : (or/c 'forward 'backward 'both) = 'both
  step-size : (and/c finite-real? positive?) = 1/20
  steps : exact-positive-integer? = 120
  opacity : opacity? = 1
  stroke : any/c = "royalblue"
  stroke-width : (and/c finite-real? (>=/c 0)) = 2
Converts streamline-points through axes into one ordinary world-space path Visual. The axes conversion is captured when the streamline is constructed. It does not clip, stop at axes bounds, or retain the field procedure after construction.

procedure

(streamlines axes    
  field    
  seeds    
  #:id id    
  [#:direction direction    
  #:step-size step-size    
  #:steps steps    
  #:opacity opacity    
  #:stroke stroke    
  #:stroke-width stroke-width]) → group-visual?
  axes : axes-visual?
  field : 
(or/c (procedure-arity-includes/c 2)
      (procedure-arity-includes/c 3))
  seeds : (listof vec2?)
  id : symbol?
  direction : (or/c 'forward 'backward 'both) = 'both
  step-size : (and/c finite-real? positive?) = 1/20
  steps : exact-positive-integer? = 120
  opacity : opacity? = 1
  stroke : any/c = "royalblue"
  stroke-width : (and/c finite-real? (>=/c 0)) = 2
Builds an ordinary group of streamline paths. Seed number n becomes the direct child named by id-n; for example, the first child of 'flow is reachable at '(flow flow-0).

procedure

(flow-particle axes    
  trajectory    
  phase    
  #:id id    
  [#:shape shape    
  #:size size    
  #:fill fill    
  #:stroke stroke    
  #:stroke-width stroke-width    
  #:opacity opacity]) → derived-visual?
  axes : axes-visual?
  trajectory : ode-trajectory?
  phase : (or/c symbol? scene-parameter?)
  id : symbol?
  shape : point-marker-shape? = 'circle
  size : (and/c finite-real? positive?) = 1/5
  fill : any/c = "crimson"
  stroke : any/c = "black"
  stroke-width : (and/c finite-real? (>=/c 0)) = 1
  opacity : opacity? = 1
Creates a parameter-driven point marker. At every scene sample, phase’s finite real value selects a position from trajectory, which is then converted through axes. The phase must remain within the trajectory’s declared range.

Before render-frames! creates frame workers, it samples all requested phase values and freezes the corresponding particle coordinates in an immutable table. The preparation pass walks each used checkpoint interval once, sharing full RK4 suffix steps among its selected times. Workers only read those coordinates; they never call the author field. Direct arbitrary-time scene sampling remains deterministic through ode-trajectory-position.

For an adaptive trajectory, the preparation pass instead reads its stored dense output directly; no numerical integration or field call occurs after the trajectory has been prepared.