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

Add ASCII support for Unicode ops #142

Merged
merged 6 commits into from
Oct 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
1 change: 1 addition & 0 deletions docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ makedocs(
"Decapodes.jl" => "index.md",
"Overview" => "overview.md",
"Equations" => "equations.md",
"ASCII Operators" => "ascii.md",
"Misc Features" => "bc_debug.md",
"Pipe Flow" => "poiseuille.md",
"Glacial Flow" => "ice_dynamics.md",
Expand Down
27 changes: 27 additions & 0 deletions docs/src/ascii.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# ASCII and Vector Calculus Operators

Some users may have trouble entering unicode characters like ⋆ or ∂ in their development environment. So, we offer the following ASCII equivalents. Further, some users may like to use vector calculus symbols instead of exterior calculus symbols where possible. We offer support for such symbols as well.

## ASCII Equivalents

| Unicode | ASCII | Meaning |
| ------- | ----- | ------- |
| ∂ₜ | dt | derivative w.r.t. time |
| ⋆ | star | Hodge star, generalizing transpose |
| Δ | lapl | laplacian |
| ∧ | wedge | wedge product, generalizing the cross product |
| ⋆⁻¹ | star\_inv | Hodge star, generalizing transpose |
| ∘(⋆,d,⋆) | div | divergence, a.k.a. ∇⋅ |

## Vector Calculus Equivalents

| Vec | DEC | How to enter |
| -------- | ---------------- | -------------------------- |
| grad | d | grad |
| div | ∘(⋆,d,⋆) | div |
| curl | ∘(d,⋆) | curl |
| ∇ | d | \nabla |
| ∇ᵈ | ∘(⋆,d,⋆) | \nabla \<tab\> \\^d \<tab\> |
| ∇x | ∘(d,⋆) | \nabla \<tab\> x |
| adv(X,Y) | ∘(⋆,d,⋆)(X∧Y) | adv |

2 changes: 1 addition & 1 deletion docs/src/equations.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,4 @@ infer_types!(oscillator)
to_graphviz(oscillator)
```

Often you will have a linear material where you are scaling by a constant, and a nonlinear version of that material where that scaling is replaced by a generic nonlinear function. This is why we allow Decapodes to represent both of these types of equations.
Often you will have a linear material where you are scaling by a constant, and a nonlinear version of that material where that scaling is replaced by a generic nonlinear function. This is why we allow Decapodes to represent both of these types of equations.
2 changes: 1 addition & 1 deletion src/Decapodes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ using Base.Iterators

import Unicode

export normalize_unicode, DerivOp, append_dot,
export normalize_unicode, DerivOp, append_dot, unicode!, vec_to_dec!,
SchDecapode, SchNamedDecapode, AbstractDecapode, AbstractNamedDecapode, Decapode, NamedDecapode, SummationDecapode, fill_names!, dot_rename!, expand_operators, add_constant!, add_parameter, infer_types!, resolve_overloads!, op1_inf_rules_1D, op2_inf_rules_1D, op1_inf_rules_2D, op2_inf_rules_2D, op1_res_rules_1D, op2_res_rules_1D, op1_res_rules_2D, op2_res_rules_2D,
Term, Var, Judgement, Eq, AppCirc1, AppCirc2, App1, App2, Plus, Tan, term, parse_decapode,
VectorForm, PhysicsState, findname, findnode,
Expand Down
151 changes: 120 additions & 31 deletions src/decapodeacset.jl
Original file line number Diff line number Diff line change
Expand Up @@ -285,35 +285,35 @@ These are the default rules used to do type inference in the 1D exterior calculu
"""
op1_inf_rules_1D = [
# Rules for ∂ₜ
(src_type = :Form0, tgt_type = :Form0, op_names = [:∂ₜ]),
(src_type = :Form1, tgt_type = :Form1, op_names = [:∂ₜ]),
(src_type = :Form0, tgt_type = :Form0, op_names = [:∂ₜ,:dt]),
(src_type = :Form1, tgt_type = :Form1, op_names = [:∂ₜ,:dt]),

# Rules for d
(src_type = :Form0, tgt_type = :Form1, op_names = [:d, :d₀]),
(src_type = :DualForm0, tgt_type = :DualForm1, op_names = [:d, :dual_d₀, :d̃₀]),

# Rules for ⋆
(src_type = :Form0, tgt_type = :DualForm1, op_names = [:⋆, :⋆₀]),
(src_type = :Form1, tgt_type = :DualForm0, op_names = [:⋆, :⋆₁]),
(src_type = :DualForm1, tgt_type = :Form0, op_names = [:⋆, :⋆₀⁻¹]),
(src_type = :DualForm0, tgt_type = :Form1, op_names = [:⋆, :⋆₁⁻¹]),
(src_type = :Form0, tgt_type = :DualForm1, op_names = [:⋆, :⋆₀, :star]),
(src_type = :Form1, tgt_type = :DualForm0, op_names = [:⋆, :⋆₁, :star]),
(src_type = :DualForm1, tgt_type = :Form0, op_names = [:⋆, :⋆₀⁻¹, :star_inv]),
(src_type = :DualForm0, tgt_type = :Form1, op_names = [:⋆, :⋆₁⁻¹, :star_inv]),

# Rules for Δ
(src_type = :Form0, tgt_type = :Form0, op_names = [:Δ, :Δ₀]),
(src_type = :Form1, tgt_type = :Form1, op_names = [:Δ, :Δ₁]),
(src_type = :Form0, tgt_type = :Form0, op_names = [:Δ, :Δ₀, :lapl]),
(src_type = :Form1, tgt_type = :Form1, op_names = [:Δ, :Δ₁, :lapl]),

# Rules for δ
(src_type = :Form1, tgt_type = :Form0, op_names = [:δ, :δ₁]),
(src_type = :Form1, tgt_type = :Form0, op_names = [:δ, :δ₁, :codif]),

# Rules for negation
(src_type = :Form0, tgt_type = :Form0, op_names = [:neg, :(-)]),
(src_type = :Form1, tgt_type = :Form1, op_names = [:neg, :(-)])]

op2_inf_rules_1D = [
# Rules for ∧₀₀, ∧₁₀, ∧₀₁
(proj1_type = :Form0, proj2_type = :Form0, res_type = :Form0, op_names = [:∧, :∧₀₀]),
(proj1_type = :Form1, proj2_type = :Form0, res_type = :Form1, op_names = [:∧, :∧₁₀]),
(proj1_type = :Form0, proj2_type = :Form1, res_type = :Form1, op_names = [:∧, :∧₀₁]),
(proj1_type = :Form0, proj2_type = :Form0, res_type = :Form0, op_names = [:∧, :∧₀₀, :wedge]),
(proj1_type = :Form1, proj2_type = :Form0, res_type = :Form1, op_names = [:∧, :∧₁₀, :wedge]),
(proj1_type = :Form0, proj2_type = :Form1, res_type = :Form1, op_names = [:∧, :∧₀₁, :wedge]),

# Rules for L₀, L₁
(proj1_type = :Form1, proj2_type = :Form0, res_type = :Form0, op_names = [:L, :L₀]),
Expand Down Expand Up @@ -356,9 +356,9 @@ These are the default rules used to do type inference in the 2D exterior calculu
"""
op1_inf_rules_2D = [
# Rules for ∂ₜ
(src_type = :Form0, tgt_type = :Form0, op_names = [:∂ₜ]),
(src_type = :Form1, tgt_type = :Form1, op_names = [:∂ₜ]),
(src_type = :Form2, tgt_type = :Form2, op_names = [:∂ₜ]),
(src_type = :Form0, tgt_type = :Form0, op_names = [:∂ₜ, :dt]),
(src_type = :Form1, tgt_type = :Form1, op_names = [:∂ₜ, :dt]),
(src_type = :Form2, tgt_type = :Form2, op_names = [:∂ₜ, :dt]),

# Rules for d
(src_type = :Form0, tgt_type = :Form1, op_names = [:d, :d₀]),
Expand All @@ -367,22 +367,22 @@ op1_inf_rules_2D = [
(src_type = :DualForm1, tgt_type = :DualForm2, op_names = [:d, :dual_d₁, :d̃₁]),

# Rules for ⋆
(src_type = :Form0, tgt_type = :DualForm2, op_names = [:⋆, :⋆₀]),
(src_type = :Form1, tgt_type = :DualForm1, op_names = [:⋆, :⋆₁]),
(src_type = :Form2, tgt_type = :DualForm0, op_names = [:⋆, :⋆₂]),
(src_type = :Form0, tgt_type = :DualForm2, op_names = [:⋆, :⋆₀, :star]),
(src_type = :Form1, tgt_type = :DualForm1, op_names = [:⋆, :⋆₁, :star]),
(src_type = :Form2, tgt_type = :DualForm0, op_names = [:⋆, :⋆₂, :star]),

(src_type = :DualForm2, tgt_type = :Form0, op_names = [:⋆, :⋆₀⁻¹]),
(src_type = :DualForm1, tgt_type = :Form1, op_names = [:⋆, :⋆₁⁻¹]),
(src_type = :DualForm0, tgt_type = :Form2, op_names = [:⋆, :⋆₂⁻¹]),
(src_type = :DualForm2, tgt_type = :Form0, op_names = [:⋆, :⋆₀⁻¹, :star_inv]),
(src_type = :DualForm1, tgt_type = :Form1, op_names = [:⋆, :⋆₁⁻¹, :star_inv]),
(src_type = :DualForm0, tgt_type = :Form2, op_names = [:⋆, :⋆₂⁻¹, :star_inv]),

# Rules for Δ
(src_type = :Form0, tgt_type = :Form0, op_names = [:Δ, :Δ₀]),
(src_type = :Form1, tgt_type = :Form1, op_names = [:Δ, :Δ₁]),
(src_type = :Form2, tgt_type = :Form2, op_names = [:Δ, :Δ₂]),
(src_type = :Form0, tgt_type = :Form0, op_names = [:Δ, :Δ₀, :lapl]),
(src_type = :Form1, tgt_type = :Form1, op_names = [:Δ, :Δ₁, :lapl]),
(src_type = :Form2, tgt_type = :Form2, op_names = [:Δ, :Δ₂, :lapl]),

# Rules for δ
(src_type = :Form1, tgt_type = :Form0, op_names = [:δ, :δ₁]),
(src_type = :Form2, tgt_type = :Form1, op_names = [:δ, :δ₂]),
(src_type = :Form1, tgt_type = :Form0, op_names = [:δ, :δ₁, :codif]),
(src_type = :Form2, tgt_type = :Form1, op_names = [:δ, :δ₂, :codif]),

# Rules for negation
(src_type = :Form0, tgt_type = :Form0, op_names = [:neg, :(-)]),
Expand All @@ -391,9 +391,9 @@ op1_inf_rules_2D = [

op2_inf_rules_2D = vcat(op2_inf_rules_1D, [
# Rules for ∧₁₁, ∧₂₀, ∧₀₂
(proj1_type = :Form1, proj2_type = :Form1, res_type = :Form2, op_names = [:∧, :∧₁₁]),
(proj1_type = :Form2, proj2_type = :Form0, res_type = :Form2, op_names = [:∧, :∧₂₀]),
(proj1_type = :Form0, proj2_type = :Form2, res_type = :Form2, op_names = [:∧, :∧₀₂]),
(proj1_type = :Form1, proj2_type = :Form1, res_type = :Form2, op_names = [:∧, :∧₁₁, :wedge]),
(proj1_type = :Form2, proj2_type = :Form0, res_type = :Form2, op_names = [:∧, :∧₂₀, :wedge]),
(proj1_type = :Form0, proj2_type = :Form2, res_type = :Form2, op_names = [:∧, :∧₀₂, :wedge]),

(proj1_type = :Form1, proj2_type = :DualForm2, res_type = :DualForm2, op_names = [:L]),

Expand Down Expand Up @@ -566,8 +566,13 @@ op1_res_rules_1D = [
(src_type = :Form1, tgt_type = :DualForm0, resolved_name = :⋆₁, op = :⋆),
(src_type = :DualForm1, tgt_type = :Form0, resolved_name = :⋆₀⁻¹, op = :⋆),
(src_type = :DualForm0, tgt_type = :Form1, resolved_name = :⋆₁⁻¹, op = :⋆),
(src_type = :Form0, tgt_type = :DualForm1, resolved_name = :⋆₀, op = :star),
(src_type = :Form1, tgt_type = :DualForm0, resolved_name = :⋆₁, op = :star),
(src_type = :DualForm1, tgt_type = :Form0, resolved_name = :⋆₀⁻¹, op = :star),
(src_type = :DualForm0, tgt_type = :Form1, resolved_name = :⋆₁⁻¹, op = :star),
# Rules for δ.
(src_type = :Form1, tgt_type = :Form0, resolved_name = :δ₁, op = :δ)]
(src_type = :Form1, tgt_type = :Form0, resolved_name = :δ₁, op = :δ),
(src_type = :Form1, tgt_type = :Form0, resolved_name = :δ₁, op = :codif)]

# We merge 1D and 2D rules since it seems op2 rules are metric-free. If
# this assumption is false, this needs to change.
Expand All @@ -576,6 +581,9 @@ op2_res_rules_1D = [
(proj1_type = :Form0, proj2_type = :Form0, res_type = :Form0, resolved_name = :∧₀₀, op = :∧),
(proj1_type = :Form1, proj2_type = :Form0, res_type = :Form1, resolved_name = :∧₁₀, op = :∧),
(proj1_type = :Form0, proj2_type = :Form1, res_type = :Form1, resolved_name = :∧₀₁, op = :∧),
(proj1_type = :Form0, proj2_type = :Form0, res_type = :Form0, resolved_name = :∧₀₀, op = :wedge),
(proj1_type = :Form1, proj2_type = :Form0, res_type = :Form1, resolved_name = :∧₁₀, op = :wedge),
(proj1_type = :Form0, proj2_type = :Form1, res_type = :Form1, resolved_name = :∧₀₁, op = :wedge),
# Rules for L.
(proj1_type = :Form1, proj2_type = :Form0, res_type = :Form0, resolved_name = :L₀, op = :L),
(proj1_type = :Form1, proj2_type = :Form1, res_type = :Form1, resolved_name = :L₁, op = :L),
Expand All @@ -599,17 +607,29 @@ op1_res_rules_2D = [
(src_type = :DualForm2, tgt_type = :Form0, resolved_name = :⋆₀⁻¹, op = :⋆),
(src_type = :DualForm1, tgt_type = :Form1, resolved_name = :⋆₁⁻¹, op = :⋆),
(src_type = :DualForm0, tgt_type = :Form2, resolved_name = :⋆₂⁻¹, op = :⋆),
(src_type = :Form0, tgt_type = :DualForm2, resolved_name = :⋆₀, op = :star),
(src_type = :Form1, tgt_type = :DualForm1, resolved_name = :⋆₁, op = :star),
(src_type = :Form2, tgt_type = :DualForm0, resolved_name = :⋆₂, op = :star),
(src_type = :DualForm2, tgt_type = :Form0, resolved_name = :⋆₀⁻¹, op = :star),
(src_type = :DualForm1, tgt_type = :Form1, resolved_name = :⋆₁⁻¹, op = :star),
(src_type = :DualForm0, tgt_type = :Form2, resolved_name = :⋆₂⁻¹, op = :star),
# Rules for δ.
(src_type = :Form2, tgt_type = :Form1, resolved_name = :δ₂, op = :δ),
(src_type = :Form1, tgt_type = :Form0, resolved_name = :δ₁, op = :δ),
(src_type = :Form2, tgt_type = :Form1, resolved_name = :δ₂, op = :codif),
(src_type = :Form1, tgt_type = :Form0, resolved_name = :δ₁, op = :codif),
# Rules for ∇².
# TODO: Call this :nabla2 in ASCII?
(src_type = :Form0, tgt_type = :Form0, resolved_name = :∇²₀, op = :∇²),
(src_type = :Form1, tgt_type = :Form1, resolved_name = :∇²₁, op = :∇²),
(src_type = :Form2, tgt_type = :Form2, resolved_name = :∇²₂, op = :∇²),
# Rules for Δ.
(src_type = :Form0, tgt_type = :Form0, resolved_name = :Δ₀, op = :Δ),
(src_type = :Form1, tgt_type = :Form1, resolved_name = :Δ₁, op = :Δ),
(src_type = :Form1, tgt_type = :Form1, resolved_name = :Δ₂, op = :Δ)]
(src_type = :Form1, tgt_type = :Form1, resolved_name = :Δ₂, op = :Δ),
(src_type = :Form0, tgt_type = :Form0, resolved_name = :Δ₀, op = :lapl),
(src_type = :Form1, tgt_type = :Form1, resolved_name = :Δ₁, op = :lapl),
(src_type = :Form1, tgt_type = :Form1, resolved_name = :Δ₂, op = :lapl)]

# We merge 1D and 2D rules directly here since it seems op2 rules
# are metric-free. If this assumption is false, this needs to change.
Expand All @@ -618,6 +638,9 @@ op2_res_rules_2D = vcat(op2_res_rules_1D, [
(proj1_type = :Form1, proj2_type = :Form1, res_type = :Form2, resolved_name = :∧₁₁, op = :∧),
(proj1_type = :Form2, proj2_type = :Form0, res_type = :Form2, resolved_name = :∧₂₀, op = :∧),
(proj1_type = :Form0, proj2_type = :Form2, res_type = :Form2, resolved_name = :∧₀₂, op = :∧),
(proj1_type = :Form1, proj2_type = :Form1, res_type = :Form2, resolved_name = :∧₁₁, op = :wedge),
(proj1_type = :Form2, proj2_type = :Form0, res_type = :Form2, resolved_name = :∧₂₀, op = :wedge),
(proj1_type = :Form0, proj2_type = :Form2, res_type = :Form2, resolved_name = :∧₀₂, op = :wedge),
# Rules for L.
(proj1_type = :Form1, proj2_type = :Form2, res_type = :Form2, resolved_name = :L₂, op = :L),
(proj1_type = :Form1, proj2_type = :DualForm2, res_type = :DualForm2, resolved_name = :L₂ᵈ, op = :L),
Expand Down Expand Up @@ -663,3 +686,69 @@ Resolve function overloads based on types of src and tgt.
resolve_overloads!(d::SummationDecapode) =
resolve_overloads!(d, op1_res_rules_2D, op2_res_rules_2D)

function replace_names!(d::SummationDecapode, op1_repls::Vector{Pair{Symbol, Any}}, op2_repls::Vector{Pair{Symbol, Symbol}})
for (orig,repl) in op1_repls
for i in collect(incident(d, orig, :op1))
d[i, :op1] = repl
end
end
for (orig,repl) in op2_repls
for i in collect(incident(d, orig, :op2))
d[i, :op2] = repl
end
end
d
end

ascii_to_unicode_op1 = Pair{Symbol, Any}[
(:dt => :∂ₜ),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might want to save dt for the differential in the t direction like dx, dy, dz. What do you think about ddt or partial_t?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't seen enough d/dts in practice being used that way. With the major exception being t as a parameter of a curve. Excluding capital T as well.

Saying that though, I am sure there are some major exceptions I haven’t thought of where this might occur. Maybe in some GCM?

I will keep an eye open where there is a differential equation in time, defined along a local parameterization t. Surely there is some 1D model that does this, and I will see what notation they use.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't been able to think up any examples, so I am going to put a pin in this and merge now.

(:star => :⋆),
(:lapl => :Δ),
(:codif => :δ),
(:star_inv => :⋆⁻¹)]

ascii_to_unicode_op2 = [
(:wedge => :∧)]

""" function unicode!(d::SummationDecapode)

Replace ASCII operators with their Unicode equivalents.
"""
unicode!(d::SummationDecapode) = replace_names!(d, ascii_to_unicode_op1, ascii_to_unicode_op2)

vec_to_dec_op1 = [
(:grad => :d),
(:div => [:⋆,:d,:⋆]),
(:curl => [:d,:⋆]),
(:∇ => :d),
(Symbol("∇ᵈ") => [:⋆,:d,:⋆]),
# Note: This is x, not \times.
(Symbol("∇x") => [:d,:⋆])]

vec_to_dec_op2 = Pair{Symbol, Symbol}[]

""" function vec_to_dec!(d::SummationDecapode)

Replace Vector Calculus operators with Discrete Exterior Calculus equivalents.
"""
function vec_to_dec!(d::SummationDecapode)
# Perform simple substitutions.
replace_names!(d, vec_to_dec_op1, vec_to_dec_op2)

# Replace `adv` with divergence of ∧.
advs = incident(d, :adv, :op2)
adv_tgts = d[advs, :res]

# Intermediate wedges.
wedge_tgts = add_parts!(d, :Var, length(adv_tgts), name=map(i -> Symbol("•_adv_$i"), eachindex(advs)), type=:infer)
# Divergences.
add_parts!(d, :Op1, length(adv_tgts), src=wedge_tgts, tgt=adv_tgts, op1=fill([:⋆,:d,:⋆],length(advs)))
# Point advs to the intermediates.
d[collect(advs), :res] = wedge_tgts

# Replace adv with ∧.
d[collect(advs), :op2] = fill(:∧,length(advs))

d
end

1 change: 1 addition & 0 deletions src/language.jl
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ term(expr::Expr) = begin
@match expr begin
#TODO: Would we want ∂ₜ to be used with general expressions or just Vars?
Expr(:call, :∂ₜ, b) => Tan(Var(b))
Expr(:call, :dt, b) => Tan(Var(b))

Expr(:call, Expr(:call, :∘, a...), b) => AppCirc1(a, term(b))
Expr(:call, a, b) => App1(a, term(b))
Expand Down
Loading