Skip to content

Commit 3214246

Browse files
Edited transient tutorial and added nonlinear case
1 parent 2e29dd1 commit 3214246

9 files changed

Lines changed: 271 additions & 123 deletions

File tree

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ jobs:
124124
${{ runner.os }}-test-
125125
${{ runner.os }}-
126126
- uses: julia-actions/julia-buildpkg@v1
127-
- run: cd test/; julia --project=.. --color=yes --check-bounds=yes runtests.jl isotropic_damage.jl validation_DrWatson.jl interpolation_fe.jl poisson_transient.jl TopOptEMFocus.jl
127+
- run: cd test/; julia --project=.. --color=yes --check-bounds=yes runtests.jl isotropic_damage.jl validation_DrWatson.jl interpolation_fe.jl transient_linear.jl transient_nonlinear.jl TopOptEMFocus.jl
128128
tutorials_mpi:
129129
name: TutorialsMPI ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ github.event_name }}
130130
runs-on: ${{ matrix.os }}

Project.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b"
3333
Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f"
3434

3535
[compat]
36-
Gridap = "=0.17.19"
37-
GridapDistributed = "=0.3"
36+
Gridap = "=0.17.19, 0.18"
37+
GridapDistributed = "=0.3, 0.4"
3838
GridapGmsh = "=0.7"
3939
GridapP4est = "=0.3"
4040
GridapPETSc = "=0.5"
-2.51 MB
Binary file not shown.

assets/transient_linear/result.gif

4.51 MB
Loading
3.94 MB
Loading

deps/build.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ files = [
2121
"On using DrWatson.jl"=>"validation_DrWatson.jl",
2222
"Interpolation of CellFields"=>"interpolation_fe.jl",
2323
"Poisson equation on parallel distributed-memory computers"=>"poisson_distributed.jl",
24-
"Transient Poisson equation"=>"poisson_transient.jl",
24+
"Transient Poisson equation"=>"transient_linear.jl",
25+
"Transient nonlinear equation"=>"transient_nonlinear.jl",
2526
"Topology optimization"=>"TopOptEMFocus.jl",
2627
"Unfitted Poisson"=>"unfitted_poisson.jl"]
2728

src/poisson_transient.jl

Lines changed: 0 additions & 119 deletions
This file was deleted.

src/transient_linear.jl

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
# ## Introduction
2+
3+
# In this tutorial, we will learn
4+
# * How to solve a simple time-dependent PDE in Gridap
5+
# * How to build a transient FE space
6+
# * How to define a transient weak form
7+
# * How to set up a time-marching scheme for a linear ODE
8+
# * How to visualise transient results
9+
10+
# We split the presentation of the ODE module of Gridap in two parts:
11+
# * In this tutorial we focus on the differences between the steady and time-dependent cases, on a simple linear time-dependent PDE.
12+
# * [Tutorial 18](https://gridap.github.io/Tutorials/stable/pages/t0018_transient_nonlinear/) will introduce more advanced features of the ODE module of Gridap, applied to a nonlinear time-dependent PDE.
13+
14+
# The [documentation of the ODE module of Gridap](https://gridap.github.io/Gridap.jl/dev/ODEs/) contains a detailed description of the framework for transient problems implemented in Gridap, including a [classification of transient problem](https://gridap.github.io/Gridap.jl/dev/ODEs/#Classification-of-ODEs), a [classification of numerical schemes](https://gridap.github.io/Gridap.jl/dev/ODEs/#Classification-of-numerical-schemes), an overview of the [high-level API](https://gridap.github.io/Gridap.jl/dev/ODEs/#High-level-API-in-Gridap) which this tutorial illustrates and of the [internals of the ODE module](https://gridap.github.io/Gridap.jl/dev/ODEs/#Low-level-implementation), as well as some [notes on and analysis of the numerical schemes implemented in Gridap](https://gridap.github.io/Gridap.jl/dev/ODEs/#Numerical-schemes-formulation-and-implementation).
15+
16+
# ## Problem statement
17+
18+
# We solve the heat equation in the two-dimensional domain $\Omega = [-1, +1]^{2}$. Let $k$ denote the thermal conductivity of the material, $c$ its specific heat capacity, $\rho$ its density and $q$ a rate of external heat generation. Let $g$ denote the temperature on the boundary of the domain. Let $t_{0}$ be the initial time, and $u_{0}$ be the initial temperature. The strong form of the heat equation reads: find $u(t): \Omega \to \mathbb{R}$ such that
19+
# ```math
20+
# \left\lbrace
21+
# \begin{aligned}
22+
# \rho(t, x) c(t, x) \partial_{t} u(t, x) - \nabla \cdot (k(t, x) \nabla u(t, x)) &= q(t, x) & \text{ in } \Omega, \\
23+
# u(t, x) &= g(t, x) & \text{ on } \partial \Omega, \\
24+
# u(t_{0}, x) &= u_{0}(x) & \text{ in } \Omega \\
25+
# \end{aligned}
26+
# \right.
27+
# ```
28+
# We assume that the data ($k$, $c$, $\rho$ and $q$) is continuous in time. Let $\alpha = k / (\rho c)$ denote the thermal diffusivity and $f = q / (\rho c)$ be the rate of external temperature generation. The weak form of the problem reads: find $u(t) \in U_{g}(t)$ such that $b(t, u, v) = \ell(t, v)$ for all $t \geq t_{0}$ and $v \in V_{0}$, where the time-dependent bilinear and linear forms $b(t, \cdot, \cdot)$ and $\ell(t, \cdot)$ are defined as
29+
# ```math
30+
# \begin{aligned}
31+
# b(t, u, v) &= m(t, u, v) + a(t, u, v), \\
32+
# m(t, u, v) &= \int_{\Omega} v \partial_{t} u(t) \ {\rm d} \Omega, \\
33+
# a(t, u, v) &= \int_{\Omega} \alpha(t) \nabla v \cdot \nabla u(t) \ {\rm d} \Omega, \\
34+
# \ell(t, v) &= \int_{\Omega} v f(t) \ {\rm d} \Omega,
35+
# \end{aligned}
36+
# ```
37+
# and the the functional spaces are $U_{g}(t) = H^{1}_{g(t)}(\Omega)$ and $V_{0} = H^{1}_{0}(\Omega)$. In particular, the trial space $U_{g}$ is a transient functional space, in the sense that its Dirichlet trace $g$ depends on time. However, the test space $V_{0}$ is time-independent (it has a constant, zero Dirichlet trace). For all $t \geq t_{0}$, we assume that $\alpha(t) \in L^{\infty}(\Omega)$ is uniformly positive in $\Omega$, $f(t) \in H^{-1}(\Omega)$, $g(t) \in H^{1/2}(\Omega)$, and finally $u_{0} \in L^{2}(\Omega)$.
38+
39+
# ## Discrete model
40+
41+
# We start with the discretization of the computational domain. In our case, we consider a $20 \times 20$ Cartesian mesh of the square $[-1, +1]^{2}$.
42+
43+
using Gridap
44+
domain = (-1, +1, -1, +1)
45+
partition = (20, 20)
46+
model = CartesianDiscreteModel(domain, partition)
47+
48+
# ## FE spaces
49+
50+
# In this tutorial we use a linear Lagrangian FE space.
51+
52+
order = 1
53+
reffe = ReferenceFE(lagrangian, Float64, order)
54+
55+
# The test space is defined as for steady problems
56+
57+
V0 = TestFESpace(model, reffe, dirichlet_tags="boundary")
58+
59+
# The trial space is now a `TransientTrialFESpace`, which is constructed from a `TestFESpace` and a function (or vector of functions if several Dirichlet tags are provided) for the Dirichlet boundary conditions. The Dirichlet trace has to be prescribed as a function of time and then space as follows
60+
61+
g(t) = x -> exp(-2 * t) * sinpi(t * x[1]) * (x[2]^2 - 1)
62+
Ug = TransientTrialFESpace(V0, g)
63+
64+
# ## Triangulation and quadrature
65+
66+
# As usual, we equip the model with an integration mesh and a measure
67+
68+
degree = 2
69+
Ω = Triangulation(model)
70+
= Measure(Ω, degree)
71+
72+
# ## Weak form
73+
# We define the thermal diffusivity $\alpha$ and the rate of external temperature generation $f$.
74+
75+
α(t) = x -> 1 + sin(t) * (x[1]^2 + x[2]^2) / 4
76+
f(t) = x -> sin(t) * sinpi(x[1]) * sinpi(x[2])
77+
78+
# We are going to construct a transient linear FEOperator by providing the bilinear forms associated to $\partial_{t} u$ and $u$, as well as the forcing term. Note that they now receive time as an additional argument, and the time derivative operator is `∂t`.
79+
80+
m(t, dtu, v) = (v * dtu)dΩ
81+
a(t, u, v) = (α(t) * (v) (u))dΩ
82+
l(t, v) = (v * f(t))dΩ
83+
op = TransientLinearFEOperator((m, a), l, Ug, V0)
84+
85+
# In our case, the mass term ($m(t, \cdot, \cdot)$) is constant in time. We can take advantage of that to save some computational effort, and indicate it to Gridap as follows
86+
op_opt = TransientLinearFEOperator((m, a), l, Ug, V0, constant_forms=(true, false))
87+
88+
# If the stiffness term ($a(t, \cdot, \cdot)$) had been constant in time, we could have set `constant_forms=(true, true)`.
89+
90+
# ## Transient solver
91+
92+
# Once we have defined the FE operator, we proceed with the definition of the ODE solver, i.e. the scheme that will be used for the integration in time. In this tutorial, we use the `ThetaMethod` with $\theta = 1/2$, resulting in a second-order scheme. The `ThetaMethod` function receives a solver for systems of equations, the time step size $\Delta t$ (constant) and the value of $\theta \in [0, 1]$. Since the ODE is linear the systems of equation that will arise in the time-marching scheme will be linear so we can provide `ThetaMethod` with a linear solver.
93+
94+
ls = LUSolver()
95+
Δt = 0.05
96+
θ = 0.5
97+
solver = ThetaMethod(ls, Δt, θ)
98+
99+
# Gridap also implements explicit and diagonally-implicit Runge-Kutta schemes. One can access the full list of available Butcher tableaus through the exported constant `available_tableaus`. There are also constructors for explicit 2- and 3-stage schemes: `EXRK22(α)` and `EXRK33(α, β)`, `EXRK33_1(α)`, `EXRK33_2(α)` respectively, and diagonally-implicit 1- and 2-stage schemes: `SDIRK11(α)`, `SDIRK12()`, `SDIRK22(α, β, γ)`, `SDIRK23(λ)`. See the documentation of [Runge-Kutta schemes in Gridap](https://gridap.github.io/Gridap.jl/dev/ODEs/#Runge-Kutta) for a description of the corresponding tableaus. For example, one could have chosen a two-stage singly-diagonally-implicit scheme (of order 2) as follows.
100+
tableau = :SDIRK_2_2
101+
solver_rk = RungeKutta(ls, ls, Δt, tableau)
102+
103+
# Let $t_{F} > t_{0}$ be a final time, until when we want to evolve the problem. We define the solution using the `solve` function, giving the ODE solver, the transient FE operator, the initial and final times, and the initial solution. To construct the initial condition we interpolate the initial function $u_{0}$ onto the FE space $U_{g}$ at the initial time. In our case, $u_{0}$ is simply $g(t_{0})$.
104+
105+
t0, tF = 0.0, 10.0
106+
uh0 = interpolate_everywhere(g(t0), Ug(t0))
107+
uh = solve(solver, op, t0, tF, uh0)
108+
109+
# ## Postprocessing
110+
111+
# We highlight that `uh` is an iterable function and the result at each time step is only computed lazily when iterating over it. We can post-process the results and generate the corresponding `vtk` files using the `createpvd` and `createvtk` functions. The former will create a `.pvd` file with the collection of `.vtu` files saved at each time step by `createvtk`. The computation of the problem solutions will be triggered in the following loop:
112+
113+
if !isdir("tmp")
114+
mkdir("tmp")
115+
end
116+
117+
createpvd("results") do pvd
118+
pvd[0] = createvtk(Ω, "tmp/results_0" * ".vtu", cellfields=["u" => uh0])
119+
for (tn, uhn) in uh
120+
pvd[tn] = createvtk(Ω, "tmp/results_$tn" * ".vtu", cellfields=["u" => uhn])
121+
end
122+
end
123+
124+
# ![](../assets/transient_linear/result.gif)

0 commit comments

Comments
 (0)