Skip to content

Commit

Permalink
Merge branch 'main' into cm/yet-another-docs-refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
jpfairbanks authored Jan 12, 2025
2 parents 6c14a51 + 15ed096 commit 1c01b94
Show file tree
Hide file tree
Showing 7 changed files with 616 additions and 181 deletions.
1 change: 1 addition & 0 deletions docs/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ DiagrammaticEquations = "6f00c28b-6bed-4403-80fa-30e0dc12f317"
Distributed = "8ba89e20-285c-5b6f-9357-94700520ee1b"
Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f"
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
DocumenterCitations = "daee34ce-89f3-4625-b898-19384cb65244"
Downloads = "f43a241f-c20a-4ad4-852c-f6b1247861c6"
FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549"
GeometryBasics = "5c1252a2-5f33-56bf-86c9-59e7332b4326"
Expand Down
11 changes: 10 additions & 1 deletion docs/make.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
@info "Loading Documenter"
using Documenter
using DocumenterCitations
using Literate
using Distributed
using ProgressMeter
Expand Down Expand Up @@ -33,6 +34,11 @@ for (root, dirs, files) in walkdir(literate_dir)
end
end

bib = CitationBibliography(
joinpath(@__DIR__, "src", "decapodes_documenter.bib");
style=:numeric
)

@info "Building Documenter.jl docs"
makedocs(
modules = [Decapodes],
Expand All @@ -57,6 +63,7 @@ makedocs(
"Vortices" => "navier_stokes/ns.md",
"Harmonics" => "harmonics/harmonics.md",
"Cahn-Hilliard" => "ch/cahn-hilliard.md",
"Brusselator" => "brussel/brussel.md",
"Klausmeier" => "klausmeier/klausmeier.md",
"CISM v2.1" => "cism/cism.md",
"Grigoriev Ice Cap" => "grigoriev/grigoriev.md", # Requires ice_dynamics
Expand All @@ -71,10 +78,12 @@ makedocs(
"MHD" => "examples/mhd.md" # TODO convert original file to a docs page
],
"Misc Features" => "bc/bc_debug.md", # Requires overview
"FAQ" => "faq/faq.md",
"ASCII Operators" => "ascii.md",
"Canonical Models" => "canon.md",
"Library Reference" => "api.md"
]
],
plugins=[bib]
)

@info "Deploying docs"
Expand Down
213 changes: 213 additions & 0 deletions docs/src/brussel/brussel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
# Brusselator

This Brusselator example is adapted from [MethodOfLines.jl](https://docs.sciml.ai/MethodOfLines/stable/tutorials/brusselator/#brusselator)'s page on the same topic. The [Brusselator](https://en.wikipedia.org/wiki/Brusselator) is a autocatalytic chemical reaction that takes place between two reactants `U` and `V`.

```@setup INFO
include(joinpath(Base.@__DIR__, ".." , "..", "docinfo.jl"))
info = DocInfo.Info()
```
## Dependencies
```@example DEC
using CairoMakie
import CairoMakie: wireframe, mesh, Figure, Axis
using Catlab
using CombinatorialSpaces
using ComponentArrays
using DiagrammaticEquations
using Decapodes
using LinearAlgebra
using MLStyle
using OrdinaryDiffEq
using GeometryBasics: Point2, Point3
Point2D = Point2{Float64}
Point3D = Point3{Float64}
nothing # hide
```

## The Model

We establish the model for the Brusselator, with the two reactants `U` and `V`, modeled as residing on the vertices of the mesh. The equations encode a reaction that occurs independently at each point coupled with a diffusion term as well as a source term `F` in the case of `U`. Here `α` denotes the rate of diffusion for both reactants.
```@example DEC
BrusselatorDynamics = @decapode begin
(U, V)::Form0
U2V::Form0
(U̇, V̇)::Form0
(α)::Constant
F::Parameter
U2V == (U .* U) .* V
U̇ == 1 + U2V - (4.4 * U) + (α * Δ(U)) + F
V̇ == (3.4 * U) - U2V + (α * Δ(V))
∂ₜ(U) == U̇
∂ₜ(V) == V̇
end
nothing # hide
```

## Boundary Conditions

We now establish the Dirichlet boundary conditions for our model. Here we intend to set some portion of the `U` variable to be a fixed value on some portion of the mesh. At this point the boundary conditions are only set symbolically and their actual implementation can change. Note that these values are set at the beginning of execution, as shown by the computation graph.
```@example DEC
BrusselatorBoundaries = @decapode begin
B::Constant
end
BrusselatorMorphism = @relation () begin
rlb(C, Cb)
end
Brusselator = collate(
BrusselatorDynamics,
BrusselatorBoundaries,
BrusselatorMorphism,
Dict(
:C => :U,
:Cb => :B))
to_graphviz(Brusselator)
```

## The Mesh

We load our triangulated mesh with horizontal and vertical resolution being `h=0.01`. `Point3D` is being used for the primal mesh `s` for ease of visualization while `Point2D` is used for the dual mesh `sd` for better memory usage. Since this conversion only drops the final z-coordinate, no information is lost.
```@example DEC
h = 0.01
s = triangulated_grid(1,1,h,h,Point3D);
sd = EmbeddedDeltaDualComplex2D{Bool,Float64,Point2D}(s);
subdivide_duals!(sd, Circumcenter());
fig = Figure() # hide
ax = CairoMakie.Axis(fig[1,1], aspect=1) # hide
wf = wireframe!(ax, s; linewidth=1) # hide
save("Brusselator_rect.png", fig) # hide
nothing # hide
```

!["BrusselatorRect"](Brusselator_rect.png)

## Initial data
We assign the initial values of `U` and `V` according to a continuous function. Since they both live on the vertices of our mesh, we can simply iterate over all point coordinates, extract the coordinate values (for either x or y) and compute the desired value. `F` has some logic attached to it encoding that it will "activate" only once the simulation has reached time `t = 1.1`.

Here we also decide to set our boundary conditions to be `1.0` along the left and right sides of our mesh.
```@example DEC
U = map(sd[:point]) do (_,y)
22 * (y *(1-y))^(3/2)
end
V = map(sd[:point]) do (x,_)
27 * (x *(1-x))^(3/2)
end
fig = Figure() # hide
ax = CairoMakie.Axis(fig[1,1], aspect=1, title = "Initial value of U") # hide
msh = CairoMakie.mesh!(ax, s, color=U, colormap=:jet, colorrange=extrema(U)) # hide
Colorbar(fig[1,2], msh) # hide
save("initial_U.png", fig) # hide
fig = Figure() # hide
ax = CairoMakie.Axis(fig[1,1], aspect=1, title = "Initial value of V") # hide
msh = CairoMakie.mesh!(ax, s, color=V, colormap=:jet, colorrange=extrema(V)) # hide
Colorbar(fig[1,2], msh) # hide
save("initial_V.png", fig) # hide
F₁ = map(sd[:point]) do (x,y)
(x-0.3)^2 + (y-0.6)^2 ≤ (0.1)^2 ? 5.0 : 0.0
end
F₂ = zeros(nv(sd))
constants_and_parameters = (
α = 0.001,
B = 1.0, # Boundary value
F = t -> t ≥ 1.1 ? F₁ : F₂)
nothing # hide
```

![Initial U Conditions](initial_U.png)
![Initial V Conditions](initial_V.png)

```@example DEC
# Find left and right vertices of mesh
min_x = minimum(x -> x[1], s[:point])
max_x = maximum(x -> x[1], s[:point])
left_wall_idxs = findall(x -> x[1] == min_x, s[:point])
right_wall_idxs = findall(x -> x[1] == max_x, s[:point])
wall_idxs = vcat(left_wall_idxs, right_wall_idxs)
function generate(sd, my_symbol; hodge=GeometricHodge())
op = @match my_symbol begin
:rlb => (x,y) -> begin
x[wall_idxs] .= y
x
end
_ => error("Unmatched operator $my_symbol")
end
end
fig = Figure() # hide
ax = CairoMakie.Axis(fig[1,1], aspect=1, title = "Highlighted Boundary") # hide
value = zeros(nv(sd)) # hide
value[wall_idxs] .= 1.0 # hide
msh = CairoMakie.mesh!(ax, s, color=value, colormap=:jet, colorrange=(0,2)) # hide
Colorbar(fig[1,2], msh) # hide
save("boundary.png", fig) # hide
nothing # hide
```

![Boundary Visualized](boundary.png)


## Generate the Simulation

We generate our simulation code and store the function in `fₘ` and then run our simulation for `t=11.5` simulated time units.
```@example DEC
sim = evalsim(Brusselator)
fₘ = sim(sd, generate, DiagonalHodge())
u₀ = ComponentArray(U=U, V=V)
tₑ = 11.5
prob = ODEProblem(fₘ, u₀, (0, tₑ), constants_and_parameters)
soln = solve(prob, Tsit5())
soln.retcode
```

## Visualize

We can then use Makie to visualize the evolution of our Brusselator model.
```@example DEC
function save_dynamics(save_file_name)
time = Observable(0.0)
u = @lift(soln($time).U)
v = @lift(soln($time).V)
f = Figure(; size = (500, 850))
ax_U = CairoMakie.Axis(f[1,1], title = @lift("Concentration of U at Time $($time)"))
ax_V = CairoMakie.Axis(f[2,1], title = @lift("Concentration of V at Time $($time)"))
msh_U = mesh!(ax_U, s, color=u, colormap=:jet, colorrange=extrema(soln(tₑ).U))
Colorbar(f[1,2], msh_U)
msh_V = mesh!(ax_V, s, color=v, colormap=:jet, colorrange=extrema(soln(tₑ).V))
Colorbar(f[2,2], msh_V)
timestamps = range(0, tₑ, step=1e-1)
record(f, save_file_name, timestamps; framerate = 15) do t
time[] = t
end
end
save_dynamics("brusselator.gif")
nothing # hide
```

![Brusselator_results_flat](brusselator.gif)

```@example INFO
DocInfo.get_report(info) # hide
```

24 changes: 13 additions & 11 deletions docs/src/cism/cism.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ The Decapodes framework takes high-level representations of physics equations an

We do so by translating equations from vector calculus notation to the "discrete exterior calculus" (DEC). This process is roughly about recognizing whether physical quantities represent scalar or vector quantities, and recognizing whether differential operators represent gradient, divergence, and so on.

In this benchmark, we will implement the "small slope approximation" of glacial dynamics used by P. Halfar in his 1981 work ["On the dynamics of the ice sheets"](https://agupubs.onlinelibrary.wiley.com/doi/abs/10.1029/JC086iC11p11065) by taking his original formulation, translating it into the DEC, then providing a mesh and initial conditions.
In this benchmark, we will implement the "small slope approximation" of glacial dynamics used by P. Halfar in his 1981 work "On the dynamics of the ice sheets"[halfar_dynamics_1981](@cite) by taking his original formulation, translating it into the DEC, then providing a mesh and initial conditions.

The initial conditions used here are exactly those considered by W. H. Lipscomb et al. in ["Description And Evaluation of the Community Ice Sheet Model (CISM) v2.1" (2019)](https://gmd.copernicus.org/articles/12/387/2019/).
The initial conditions used here are exactly those considered by W. H. Lipscomb et al. in "Description And Evaluation of the Community Ice Sheet Model (CISM) v2.1"[lipscomb_description_2019](@cite).

```@example DEC
# AlgebraicJulia Dependencies
Expand Down Expand Up @@ -65,7 +65,7 @@ to_graphviz(halfar_eq2)

!["Glen's Law"](glens_law.png)

Here, we recognize that Gamma is in fact what glaciologists call "Glen's Flow Law." It states that the strain rate of a sheet of ice can be related to applied stress via a power law. Below, we encode the formulation as it is usually given in the literature, depending explicitly on the gravitational constant, g.
Here, we recognize that Gamma is in fact what glaciologists call "Glen's Flow Law"[glen_flow_1958](@cite). It states that the strain rate of a sheet of ice can be related to applied stress via a power law. Below, we encode the formulation as it is usually given in the literature, depending explicitly on the gravitational constant, g.

```@example DEC
# Equation 1 from Glen, J. W. THE FLOW LAW OF ICE: A discussion of the
Expand Down Expand Up @@ -194,11 +194,8 @@ constants_and_parameters = (

We provide here the mapping from symbols to differential operators. As more of the differential operators of the DEC are implemented, they are upstreamed to the Decapodes and CombinatorialSpaces libraries. Of course, users can also provide their own implementations of these operators and others as they see fit.

## Setting up the Simulation
```@example DEC
#############################################
# Define how symbols map to Julia functions #
#############################################
function generate(sd, my_symbol; hodge=GeometricHodge())
# We pre-allocate matrices that encode differential operators.
op = @match my_symbol begin
Expand All @@ -216,16 +213,14 @@ end
The `gensim` function takes our high-level representation of the physics equations and produces compiled simulation code. It performs optimizations such as allocating memory for intermediate variables, and so on.

```@example DEC
#######################
# Generate simulation #
#######################
sim = eval(gensim(ice_dynamics2D))
fₘ = sim(sd, generate)
```

Julia is a "Just-In-Time" compiled language. That means that functions are compiled the first time they are called, and later calls to those functions skip this step. To get a feel for just how fast this simulation is, we will run the dynamics twice, once for a very short timespan to trigger pre-compilation, and then again for the actual dynamics.

## Running the Simulation

```@example DEC
# Pre-compile simulation
Expand Down Expand Up @@ -264,6 +259,8 @@ Here we save the solution information to a [file](ice_dynamics2D.jld2).
@save "ice_dynamics2D.jld2" soln
```

## Result Comparison and Analysis

We recall that these dynamics are of the "shallow slope" and "shallow ice" approximations. So, at the edge of our parabolic dome of ice, we expect increased error as the slope increases. On the interior of the dome, we expect the dynamics to match more closely that given by the analytic model. We will see that the CISM results likewise accumulate error in the same neighborhood.

!["Halfar Small Ice Approximation Quote"](halfar_quote.png)
Expand Down Expand Up @@ -380,6 +377,11 @@ Since Decapodes targets high-level representations of physics, it is uniquely su

Further improvements to the Decapodes library are made continuously. We are creating implementations of DEC operators that are constructed and execute faster. And we are in the beginning stages of 3D simulations using the DEC.

```@bibliography
Pages = ["cism.md"]
Canonical = false
```

```@example INFO
DocInfo.get_report(info) # hide
```
Loading

0 comments on commit 1c01b94

Please sign in to comment.