Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

geodesics and more physics support #69

Merged
merged 13 commits into from
Aug 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,19 @@

## [unreleased]

- #69:

- Adds
`emmy.mathbox.physics.{lagrangian-curve,hamiltonian-curve,routhian-curve,geodesic}`
to the existing `ode-curve` function.

- Adds
`emmy.viewer.physics.{evolve-lagrangian,evolve-hamiltonian,evolve-routhian}`
to the existing `evolve` function.

- Updates the `emmy.viewer.components.physics/Evolve` component to use a
pre-allocated JS output array instead of forcing an allocation on each tick.

## [0.2.0]

- #57:
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ Use `plot/scene` to render multiple plots onto a 3D canvas:

![MathBox plot example](https://github.com/mentat-collective/emmy-viewers/assets/69635/3349cff0-744c-40fa-b996-1d324162b3d9)

> See the [MathBox Guide](https://emmy-viewers.mentat.org/dev/examples/mafs) for 3D plotting examples.
> See the [MathBox Guide](https://emmy-viewers.mentat.org/dev/examples/mathbox) for 3D plotting examples.

Next, try clicking the 'show code' link for the examples at the top of the
page and pasting their source into your namespace. Then visit the ["Specific
Expand Down
2 changes: 1 addition & 1 deletion dev/emmy_viewers/notebook.clj
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@
:t [-10 10]
:color :green}))

;; > See the [MathBox Guide](/dev/examples/mafs) for 3D plotting examples.
;; > See the [MathBox Guide](/dev/examples/mathbox) for 3D plotting examples.
;;
;; Next, try clicking the 'show code' link for the examples at the top of the
;; page and pasting their source into your namespace. Then visit the ["Specific
Expand Down
71 changes: 49 additions & 22 deletions dev/examples/manifold/klein.clj
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
:exclude [+ - * / zero? compare divide numerator denominator
infinite? abs ref partial =])
(:require [emmy.clerk :as ec]
[emmy.mathbox.physics :as ph]
[emmy.env :as e :refer :all]
[emmy.leva :as leva]
[emmy.mathbox.plot :as p]
Expand All @@ -17,17 +18,10 @@

;; # Fun with Klein Bottles

(defn slider-surface [name {:keys [u v] :as opts}]
(defn slider-surface [name {:keys [u v] :as opts} & children]
(ev/with-let [!opts {:u (peek u) :v (peek v)}]
(p/scene
{:axes
{:x {:divisions 10
:label {:position 3}}
:y {:divisions 10
:ticks {:labels? false}}
:z {:divisions 10
:label-ticks? false}}
:grids []}
(apply
p/scene
(leva/controls
{:folder {:name name}
:schema
Expand All @@ -37,7 +31,8 @@
(p/parametric-surface
(assoc opts
:u [(first u) (ev/get !opts :u)]
:v [(first v) (ev/get !opts :v)])))))
:v [(first v) (ev/get !opts :v)]))
children)))

;; ## Mobius Strip

Expand Down Expand Up @@ -67,18 +62,31 @@

;; Can you find the Emmy logo hidden inside?

(ev/with-let [!r {:r 2}]
(ev/with-let [!r {:r 2 :theta_0 1 :alpha_0 0 :steps 20}]
[:<>
(leva/controls {:atom !r
:folder {:name "Klein Bagel"}
:schema {:r {:min 2 :max 6 :step 0.01}}})
(leva/controls
{:atom !r
:folder {:name "Klein Bagel"}
:schema {:r {:min 2 :max 6 :step 0.01}
:theta_0 {:label (->infix 'theta_0) :min 0 :max Math/PI :step 0.02}
:alpha_0 {:label (->infix 'alpha_0) :min 0 :max Math/PI :step 0.02}
:steps {:min 500 :max 9000 :step 50}}})
(slider-surface
"Klein Bagel"
{:f (ev/with-params {:atom !r :params [:r]}
klein-bagel)
:opacity 0.75
:u [0 (* 2 Math/PI)]
:v [0 (* 2 Math/PI)]})])
:v [0 (* 2 Math/PI)]}

(ph/geodesic
{:x0 [(ev/get !r :theta_0) 0]
:v0 [(list 'Math/cos (ev/get !r :alpha_0))
(list 'Math/sin (ev/get !r :alpha_0))]
:xform (ev/with-params {:atom !r :params [:r]}
klein-bagel)
:steps (ev/get !r :steps)
:width 2
:end? true}))])

;; ## Klein bottle:

Expand All @@ -102,11 +110,30 @@
(* 2/15 (sin v)
(+ 3 (* 5 (cos u) (sin u))))])

(slider-surface
"Klein"
{:f klein-bottle
:u [0 Math/PI]
:v [0 (* 2 Math/PI)]})
(ev/with-let [!r {:theta_0 1 :alpha_0 0 :steps 20}]
[:<>
(leva/controls
{:atom !r
:folder {:name "Klein"}
:schema {:theta_0 {:label (->infix 'theta_0) :min 0 :max Math/PI :step 0.02}
:alpha_0 {:label (->infix 'alpha_0) :min 0 :max Math/PI :step 0.02}
:steps {:min 10 :max 9000 :step 50}}})

(slider-surface
"Klein"
{:f klein-bottle
:opacity 0.2
:u [0 Math/PI]
:v [0 (* 2 Math/PI)]}

(ph/geodesic
{:x0 [(ev/get !r :theta_0) 0]
:v0 [(list 'Math/cos (ev/get !r :alpha_0))
(list 'Math/sin (ev/get !r :alpha_0))]
:xform klein-bottle
:steps (ev/get !r :steps)
:width 2
:end? true}))])

;; ## Plucker's Conoid

Expand Down
7 changes: 3 additions & 4 deletions dev/examples/simulation/double_ellipsoid.clj
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,11 @@
{:threestrap {:plugins ["core" "controls" "cursor" "stats"]}}
(leva/controls {:atom !opts})

(emmy.viewer.physics/evolve
(emmy.viewer.physics/evolve-lagrangian
{:atom !state
:initial-state initial-state
:f' (ev/with-params {:atom !opts :params [:m :k :x0 :a :b :c]}
(comp Lagrangian->state-derivative
L-central-triaxial))})
:L (ev/with-params {:atom !opts :params [:m :k :x0 :a :b :c]}
L-central-triaxial)})

(emmy.mathbox.physics/comet
{:length 16
Expand Down
7 changes: 3 additions & 4 deletions dev/examples/simulation/ellipsoid.clj
Original file line number Diff line number Diff line change
Expand Up @@ -117,12 +117,11 @@
:b {:min 1 :max 5 :step 0.01}
:c {:min 1 :max 5 :step 0.01}}})

(emmy.viewer.physics/evolve
(emmy.viewer.physics/evolve-lagrangian
{:atom !state
:initial-state initial-state
:f' (ev/with-params {:atom !opts :params [:m :g :a :b :c]}
(comp Lagrangian->state-derivative
L-central-triaxial))})
:L (ev/with-params {:atom !opts :params [:m :g :a :b :c]}
L-central-triaxial)})

(plot/parametric-surface
{:opacity 0.2
Expand Down
8 changes: 4 additions & 4 deletions dev/examples/simulation/oscillator.clj
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,14 @@
(plot/scene
(leva/controls {:atom !opts})

(emmy.viewer.physics/evolve
(emmy.viewer.physics/evolve-lagrangian
{:atom !state
:initial-state initial-state
:f' (ev/with-params {:atom !opts :params [:g :m :k]}
(comp e/Lagrangian->state-derivative L-harmonic))})
:L (ev/with-params {:atom !opts :params [:g :m :k]}
L-harmonic)})

(emmy.mathbox.physics/comet
{:length 1
{:length 10
:state->xyz coordinate
:initial-state initial-state
:atom !state}))))
Expand Down
8 changes: 4 additions & 4 deletions dev/examples/simulation/toroid.clj
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@
(clerk/with-viewer ec/multiviewer
(let [L (L-toroidal 'R 'r)
theta (literal-function 'theta)
phi (literal-function 'phi)]
phi (literal-function 'phi)]
(((Lagrange-equations L) (up theta phi))
't)))

Expand All @@ -113,14 +113,14 @@
:alpha_0 {:label (->infix 'alpha_0) :min 0 :max Math/PI :step 0.02}
:steps {:min 500 :max 9000 :step 50}}})

(ph/ode-curve
(ph/lagrangian-curve
{:initial-state
[0
[(ev/get !params :theta_0) 0]
[(list 'Math/cos (ev/get !params :alpha_0))
(list 'Math/sin (ev/get !params :alpha_0))]]
:f' (ev/with-params {:atom !params :params [:R :r]}
(compose e/Lagrangian->state-derivative L-toroidal))
:L (ev/with-params {:atom !params :params [:R :r]}
L-toroidal)
:state->xyz (ev/with-params {:atom !params :params [:R :r]}
t->r)
:steps (ev/get !params :steps)
Expand Down
2 changes: 1 addition & 1 deletion dev/user.clj
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
:browse? true
:watch-paths ["src" "dev"]
;; Enable this when working on new components.
;; :cljs-namespaces '[emmy-viewers.sci-extensions]
:cljs-namespaces '[emmy-viewers.sci-extensions]
})

(def static-defaults
Expand Down
2 changes: 1 addition & 1 deletion resources/emmy_viewers/clerk/template.edn
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{:description "TODO replace me with a description of your project!"
:clerk-port 7777
:emmy-viewers-sha "ea84aef12dc2e37729b1de4263aa5554d886dccc"
:emmy-viewers-sha "218a5a27fc7ea01895cd33f888ca9d9061011b85"
:clerk-sha "d80187013d7b7b96db3d8b114b8d99f687170668"
:clerk-utils-sha "b98b74fa025b5543318a6b5beb1622fd35b4906e"
:clj-version "1.11.1"
Expand Down
109 changes: 109 additions & 0 deletions src/emmy/mathbox/physics.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
namespace."
(:refer-clojure :exclude [vector])
(:require [emmy.mathbox.plot :as plot]
[emmy.mechanics.lagrange :as l]
[emmy.mechanics.hamilton :as h]
[emmy.mechanics.routhian :as r]
[emmy.viewer :as ev]
[emmy.viewer.compile :as vc]
[emmy.viewer.physics :as ph]))
Expand All @@ -26,6 +29,9 @@
responsible for transforming each integrated state into a 3D point. Defaults
to `identity`.

- `:simplify?` if `true`, the compiler will attempt to simplify the body of
`:state->xyz` and `:f'`. `false` by default.

- `:steps`: the number of `:dt`-spaced steps for the integrator to take.
Defaults to 1000.

Expand Down Expand Up @@ -63,6 +69,109 @@
['emmy.mathbox.components.physics/ODECurve opts])
(ev/fragment plot/scene))))

(defn lagrangian-curve
"Returns a fragment that plots a curve by integrating a system of ordinary
differential equations generated from the Lagrangian passed via `:L` forward
from the initial input state `initial-state` for `steps` steps of `dt` each.

Required arguments:

- `:L'`: function of the form `(fn [[t q qdot]] <energy>)`

- `:initial-state`: the initial input vector to `:L`.

See [[ode-curve]] for a description of all supported optional options."
[{:keys [L] :as opts}]
(let [f' (if (ev/param-f? L)
(update L :f #(comp l/Lagrangian->state-derivative %))
(l/Lagrangian->state-derivative L))]
(ode-curve
(-> (dissoc opts :L)
(assoc :f' f')))))

(defn hamiltonian-curve
"Returns a fragment that plots a curve by integrating a system of ordinary
differential equations generated from the Hamiltonian passed via `:H` forward
from the initial input state `initial-state` for `steps` steps of `dt` each.

Required arguments:

- `:H`: function of the form `(fn [[t q p]] <energy>)`

- `:initial-state`: the initial input vector to `:H`.

See [[ode-curve]] for a description of all supported optional options."
[{:keys [H] :as opts}]
(let [f' (if (ev/param-f? H)
(update H :f #(comp h/Hamiltonian->state-derivative %))
(h/Hamiltonian->state-derivative H))]
(ode-curve
(-> (dissoc opts :H)
(assoc :f' f')))))

(defn routhian-curve
"Returns a fragment that plots a curve by integrating a system of ordinary
differential equations generated from the Routhian passed via `:R` forward
from the initial input state `initial-state` for `steps` steps of `dt` each.

Required arguments:

- `:R`: function of the form `(fn [[t q qdot-or-p]] <energy>)`

- `:initial-state`: the initial input vector to `:R`.

See [[ode-curve]] for a description of all supported optional options."
[{:keys [R] :as opts}]
(let [f' (if (ev/param-f? R)
(update R :f #(comp r/Routhian->state-derivative %))
(r/Routhian->state-derivative R))]
(ode-curve
(-> (dissoc opts :R)
(assoc :f' f')))))

(defn- L-params [xform]
(fn [& params]
(comp (l/L-free-particle 1)
(l/F->C
(comp (apply xform params)
l/coordinate)))))

(defn- L-bare [xform]
((L-params (fn [] xform))))

(defn geodesic
"Returns a fragment that plots a geodesic curve over the parametric surface
defined by `:xform`, starting from initial position `:x0` with initial
velocity `:v0`. The geodesic curve is generated by integrating a system of
ordinary differential equations forward for `steps` steps of `dt` each.

Required arguments:

- `:xform`: function of the form `(fn [[u v]] [<x> <y> <z>])`

- `:x0`: starting position of the geodesic in `[u v]` coordinates.

- `:v0`: starting velocity of the geodesic in `[udot vdot]`.

See [[ode-curve]] for a description of all supported optional options."
[{:keys [xform x0 v0] :as opts}]
(let [L (if (ev/param-f? xform)
(update xform :f L-params)
(L-bare xform))
state->xyz (if (ev/param-f? xform)
(update xform :f (fn [f]
(fn [& params]
(comp (apply f params)
l/coordinate))))
(comp xform l/coordinate))]
(lagrangian-curve
(-> (dissoc opts :xform :x0 :v0)
(assoc :L L
:initial-state [0 x0 v0]
:state->xyz state->xyz)))))

;; ## Visual Elements

(defn comet
"Returns a fragment that renders a point that trails its historical positions
out behind it in a glowing tail.
Expand Down
16 changes: 8 additions & 8 deletions src/emmy/viewer/components/physics.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -136,13 +136,13 @@
:as opts}]
;; TODO make this in a hook and call the no-arity function on the hook.
;; exit.
(let [update (monotonic-integrator
f'
(:state (.-state !state))
(select-keys opts [:epsilon :max-steps]))]
(let [initial (->arr (:state (.-state !state)))
update (monotonic-integrator
f'
initial
(select-keys opts [:epsilon :max-steps]))]
[sw/Stopwatch
{:onTick
(fn [t]
;; TODO can we keep the output here mutable and provide an out to
;; update?
(swap! !state assoc :state (update t)))}]))
(let [out (js/Array. (count initial))]
(fn [t]
(swap! !state assoc :state (update t out))))}]))
Loading
Loading