#lang racket
(require scheme/unsafe/ops)
(require "control.rkt")
(define-values (struct:standard-control-state
standard-control-state-constructor
standard-control-state?
standard-control-state-field-ref
set-standard-control-state-field!)
(make-struct-type 'standard-control-state #f 4 0))
(define (make-standard-control-state)
(standard-control-state-constructor 0.0 0.0 0.0 0.0))
(define standard-control-state-eps-abs
(make-struct-field-accessor standard-control-state-field-ref 0 'eps-abs))
(define set-standard-control-state-eps-abs!
(make-struct-field-mutator set-standard-control-state-field! 0 'eps-abs))
(define standard-control-state-eps-rel
(make-struct-field-accessor standard-control-state-field-ref 1 'eps-rel))
(define set-standard-control-state-eps-rel!
(make-struct-field-mutator set-standard-control-state-field! 1 'eps-rel))
(define standard-control-state-a_y
(make-struct-field-accessor standard-control-state-field-ref 2 'a_y))
(define set-standard-control-state-a_y!
(make-struct-field-mutator set-standard-control-state-field! 2 'a_y))
(define standard-control-state-a_dydt
(make-struct-field-accessor standard-control-state-field-ref 3 'a_dydt))
(define set-standard-control-state-a_dydt!
(make-struct-field-mutator set-standard-control-state-field! 3 'a_dydt))
(define (standard-control-init standard-control-state
eps-abs eps-rel a_y a_dydt)
(when (< eps-abs 0.0)
(error 'standard-control-init "eps-abs is negative"))
(when (< eps-rel 0.0)
(error 'standard-control-init "eps-rel is negative"))
(when (< a_y 0.0)
(error 'standard-control-init "a_y is negative"))
(when (< a_dydt 0.0)
(error 'standard-control-init "a_dydt is negative"))
(set-standard-control-state-eps-abs! standard-control-state eps-abs)
(set-standard-control-state-eps-rel! standard-control-state eps-rel)
(set-standard-control-state-a_y! standard-control-state a_y)
(set-standard-control-state-a_dydt! standard-control-state a_dydt))
(define (standard-control-h-adjust state dim ord y y-err yp h)
(let ((eps-abs (standard-control-state-eps-abs state))
(eps-rel (standard-control-state-eps-rel state))
(a_y (standard-control-state-a_y state))
(a_dydt (standard-control-state-a_dydt state))
(s 0.9)
(h-old (unbox h))
(rmax -inf.0))
(for ((i (in-range dim)))
(let* ((D0 (unsafe-fl+
(unsafe-fl* eps-rel
(unsafe-fl+
(unsafe-fl* a_y (abs (unsafe-vector-ref y i)))
(unsafe-fl* a_dydt (abs (unsafe-fl* h-old
(unsafe-vector-ref yp i))))))
eps-abs))
(r (unsafe-fl/ (abs (unsafe-vector-ref y-err i)) (abs D0))))
(set! rmax (max r rmax))))
(let/ec exit
(cond ((unsafe-fl> rmax 1.1)
(let ((r (unsafe-fl/ s (expt rmax (unsafe-fl/ 1.0 (exact->inexact ord))))))
(when (unsafe-fl< r 0.2)
(set! r 0.2))
(set-box! h (unsafe-fl* r h-old))
(exit -1)))
((unsafe-fl< rmax 0.5)
(let ((r (unsafe-fl/ s (expt rmax (unsafe-fl/ 1.0 (exact->inexact (unsafe-fx+ ord 1)))))))
(when (unsafe-fl> r 5.0)
(set! r 5.0))
(when (unsafe-fl< r 1.0) (set! r 1.0))
(set-box! h (unsafe-fl* r h-old))
(exit 1)))
(else
0)))))
(define standard-control-type
(make-ode-control-type
"standard"
make-standard-control-state
standard-control-init
standard-control-h-adjust))
(define (standard-control-new eps-abs eps-rel a_y a_dydt)
(let ((control (make-ode-control standard-control-type)))
(ode-control-init control eps-abs eps-rel a_y a_dydt)
control))
(define (control-y-new eps-abs eps-rel)
(standard-control-new eps-abs eps-rel 1.0 0.0))
(define (control-yp-new eps-abs eps-rel)
(standard-control-new eps-abs eps-rel 0.0 1.0))
(provide (all-defined-out))