#lang scheme
(require "simulation-environment.ss"
"event.ss"
"process.ss"
"set.ss"
"variable.ss")
(require scheme/mpair)
(require (planet williams/science/ode-initval))
(provide (all-defined-out))
(define-syntax schedule
(syntax-rules (now at in when)
((schedule now (function . arguments) #:priority priority)
(schedule '#:now (function . arguments) priority))
((schedule now (function . arguments))
(schedule '#:now (function . arguments)))
((schedule (at time) (function . arguments) #:priority priority)
(schedule time (function . arguments) priority))
((schedule (at time) (function . arguments))
(schedule time (function . arguments)))
((schedule (in delay) (function . arguments) #:priority priority)
(schedule (+ delay (current-simulation-time))
(function . arguments) priority))
((schedule (in delay) (function . arguments))
(schedule (+ delay (current-simulation-time))
(function . arguments)))
((schedule (when event) (function . arguments) #:priority priority)
(schedule event (function . arguments) priority))
((schedule (when event) (function . arguments))
(schedule event (function . arguments)))
((schedule time (function . arguments))
(schedule time (function . arguments) 0))
((schedule time (function . arguments) priority)
(if (process-def? function)
(let ((process (make-process function (list . arguments))))
(set-event-priority! (process-event process) priority)
(schedule-event (process-event process) time (current-simulation-environment))
process)
(let ((event (make-event time priority #f function (list . arguments))))
(schedule-event event time (current-simulation-environment))
event)))))
(define schedule-event
(case-lambda
((event time environment)
(cond ((real? time)
(set-event-time! event time)
(event-list-add! (simulation-environment-future-event-list
environment)
event))
((eq? time '#:now)
(set-event-time! event (simulation-environment-time
environment))
(event-list-add! (simulation-environment-now-event-list
environment)
event))
((eq? time '#:continuous)
(set-event-time! event (simulation-environment-time
environment))
(event-list-add! (simulation-environment-continuous-event-list
environment)
event))
((event? time)
(when (not (event-linked-event-list time))
(set-event-linked-event-list! time (make-event-list)))
(set-event-time! event (event-time time))
(event-list-add! (event-linked-event-list time) event))
((process? time)
(schedule-event event (process-event time) environment))
(else
(error 'schedule-event
"Illegal time specification ~a" time))))
((event time)
(schedule-event event time (current-simulation-environment)))
((event)
(event-list-add! (current-simulation-future-event-list) event))))
(define (make-and-schedule-event time priority process function arguments)
(let ((event (make-event +inf.0 priority process function arguments)))
(schedule-event event time)
event))
(define (start-simulation)
(let ((current-environment (current-simulation-environment)))
(let/ec exit
(set-simulation-environment-loop-exit!
current-environment exit)
(let main-loop ()
(let/cc next
(set-simulation-environment-loop-next!
current-environment next)
(cond ((not (event-list-empty?
(current-simulation-now-event-list)))
(let ((event (event-list-pop!
(simulation-environment-now-event-list
current-environment))))
(execute-discrete-event event current-environment)))
((not (event-list-empty?
(simulation-environment-future-event-list
current-environment)))
(let ((event (mcar (event-list-events
(simulation-environment-future-event-list
current-environment)))))
(if (and (not (event-list-empty?
(simulation-environment-continuous-event-list
current-environment)))
(< (simulation-environment-time
current-environment)
(event-time event)))
(execute-continuous-events
(event-time event) current-environment)
(begin
(set! event
(event-list-pop!
(simulation-environment-future-event-list
current-environment)))
(when (= (event-time event) +inf.0)
(exit))
(when (simulation-environment-monitor
current-environment)
((simulation-environment-monitor
current-environment)))
(set-simulation-environment-time!
current-environment (event-time event))
(execute-discrete-event event current-environment)))))
((not (event-list-empty?
(simulation-environment-continuous-event-list
current-environment)))
(execute-continuous-events +inf.0 current-environment))
(else
(exit))))
(let ((process (simulation-environment-process
current-environment)))
(when process
(let ((monitor (process-monitor process)))
(when monitor
(monitor process)))))
(main-loop)))
(set-simulation-environment-event!
current-environment #f)
(set-simulation-environment-process!
current-environment #f)
(set-simulation-environment-loop-exit!
current-environment #f)
(set-simulation-environment-loop-next!
current-environment #f)))
(define (execute-discrete-event event current-environment)
(let ((process (event-process event)))
(when (event-linked-event-list event)
(for-each
(lambda (linked-event)
(schedule-event linked-event #:now current-environment))
(event-list-events (event-linked-event-list event)))
(set-event-list-events! (event-linked-event-list event) '()))
(set-simulation-environment-event!
current-environment event)
(set-simulation-environment-process!
current-environment process)
(when process
(set-process-state! process PROCESS-ACTIVE))
(apply (event-function event)
(event-arguments event))
(when process
(set-process-state! process PROCESS-TERMINATED))))
(define (execute-continuous-events time-end current-environment)
(when (current-simulation-state-changed?)
(continuous-initialize current-environment))
(let* ((step (simulation-environment-step
current-environment))
(control (simulation-environment-control
current-environment))
(evolve (simulation-environment-evolve
current-environment))
(system (simulation-environment-system
current-environment))
(t (box (simulation-environment-time
current-environment)))
(h (box (simulation-environment-step-size
current-environment)))
(y (make-vector
(simulation-environment-dimension
current-environment)
0.0))
(save-step-size (simulation-environment-step-size
current-environment)))
(set-state-vector-values! y current-environment)
(ode-evolve-reset evolve)
(let loop ()
(when (< (unbox t) time-end)
(let ((terminating-events '())
(save-y (simulation-environment-y
current-environment)))
(set-simulation-environment-y!
current-environment y)
(mfor-each
(lambda (event)
(let ((process (event-process event)))
(when (and (process-terminating-condition process)
((process-terminating-condition process)))
(set! terminating-events
(cons event terminating-events)))))
(event-list-events
(simulation-environment-continuous-event-list
current-environment)))
(set-simulation-environment-y!
current-environment save-y)
(if (not (null? terminating-events))
(for-each
(lambda (event)
(let ((process (event-process event)))
(event-list-remove!
(simulation-environment-continuous-event-list
current-environment)
event)
(for-each
(lambda (variable)
(set-variable-state-index! variable -1))
(process-continuous-variables process))
(schedule-event event '#:now current-environment)
(set-simulation-environment-state-changed?!
current-environment #t)))
terminating-events)
(begin
(when (simulation-environment-monitor
current-environment)
((simulation-environment-monitor
current-environment)))
(ode-evolve-apply
evolve control step system
t time-end h y)
(set-box!
h (min (unbox h)
(simulation-environment-max-step-size
current-environment)))
(set-simulation-environment-time!
current-environment (unbox t))
(when (simulation-environment-control
current-environment)
(set-simulation-environment-step-size!
current-environment (unbox h)))
(set-variable-values! y current-environment)
(mfor-each
(lambda (event)
(let* ((process (event-process event))
(monitor (process-monitor process)))
(when monitor
(monitor process))))
(event-list-events
(simulation-environment-continuous-event-list
current-environment)))
(loop))))))
(set-simulation-environment-step-size!
current-environment save-step-size)))
(define (continuous-eval t y dydt params)
(let* ((current-environment (car params))
(save-time (simulation-environment-time
current-environment))
(save-y (simulation-environment-y
current-environment))
(save-dydt (simulation-environment-dydt
current-environment)))
(set-simulation-environment-time!
current-environment t)
(set-simulation-environment-y!
current-environment y)
(set-simulation-environment-dydt!
current-environment dydt)
(mfor-each
(lambda (event)
(let ((process (event-process event)))
((process-differentiation-function process))))
(event-list-events (simulation-environment-continuous-event-list
current-environment)))
(set-simulation-environment-time!
current-environment save-time)
(set-simulation-environment-y!
current-environment save-y)
(set-simulation-environment-dydt!
current-environment save-dydt)))
(define (continuous-initialize current-environment)
(let ((dimension 0))
(mfor-each
(lambda (event)
(for-each
(lambda (variable)
(let ((index dimension))
(set-variable-state-index! variable index)
(set! dimension (+ 1 dimension))))
(process-continuous-variables (event-process event))))
(event-list-events
(simulation-environment-continuous-event-list
current-environment)))
(set-simulation-environment-dimension!
current-environment dimension)
(set-simulation-environment-system!
current-environment
(make-ode-system continuous-eval #f dimension (list current-environment)))
(set-simulation-environment-step!
current-environment
(make-ode-step (simulation-environment-step-type
current-environment)
dimension))
(set-simulation-environment-evolve!
current-environment
(make-ode-evolve dimension))
(set-simulation-environment-state-changed?!
current-environment #f)))
(define (set-variable-values! state-vector current-environment)
(mfor-each
(lambda (event)
(for-each
(lambda (variable)
(set-variable-value!
variable (vector-ref state-vector
(variable-state-index variable))))
(process-continuous-variables (event-process event))))
(event-list-events
(simulation-environment-continuous-event-list
current-environment))))
(define (set-state-vector-values! state-vector current-environment)
(mfor-each
(lambda (event)
(for-each
(lambda (variable)
(vector-set! state-vector
(variable-state-index variable)
(variable-field-ref variable 1)))
(process-continuous-variables (event-process event))))
(event-list-events
(simulation-environment-continuous-event-list
current-environment))))
(define stop-simulation
(case-lambda
((return-value)
((current-simulation-loop-exit) return-value))
(()
((current-simulation-loop-exit)))))
(define (wait/work delay)
(let/cc continue
(let ((event (current-simulation-event)))
(set-event-time! event (+ (current-simulation-time) delay))
(set-event-function! event continue)
(set-event-arguments! event '())
(when (event-process event)
(set-process-state! (event-process event) PROCESS-WAITING/WORKING))
(schedule-event event))
(current-simulation-event #f)
((current-simulation-loop-next))))
(define wait wait/work)
(define work wait/work)
(define-syntax work/continuously
(syntax-rules (until)
((work/continuously
until condition
body ...)
(let/cc continue
(let ((process (current-simulation-process))
(event (current-simulation-event)))
(set-process-terminating-condition!
process
(lambda () condition))
(set-process-differentiation-function!
process
(lambda () body ...))
(set-process-state! process PROCESS-WORKING-CONTINUOUSLY)
(set-event-function! event continue)
(set-event-arguments! event '())
(schedule-event event '#:continuous))
(current-simulation-event #f)
(current-simulation-state-changed? #t)
((current-simulation-loop-next))))
((work/continuously
body ...)
(work/continuously
until #f
body ...))))
(define (suspend-process)
(let/cc continue
(let ((process (current-simulation-process))
(event (current-simulation-event)))
(set-event-time! event 0.0)
(set-event-function! event continue)
(set-event-arguments! event '())
(set-process-state! process PROCESS-SUSPENDED))
(current-simulation-event #f)
((current-simulation-loop-next))))
(define (interrupt-process process)
(let ((event (process-event process)))
(event-list-remove! event)
(set-event-time! event (- (event-time event) (current-simulation-time)))
(set-process-state! process PROCESS-INTERRUPTED)))
(define (resume-process process)
(let ((event (process-event process)))
(schedule-event event (+ (current-simulation-time)
(event-time event)))))