Skip to content

Commit d401c94

Browse files
committed
Added poisson transient tutorial
1 parent ee38441 commit d401c94

4 files changed

Lines changed: 124 additions & 0 deletions

File tree

Project.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ FillArrays = "1a297f60-69ca-5386-bcde-b61e274b549b"
1010
Gridap = "56d4f2e9-7ea1-5844-9cf6-b9c51ca7ce8e"
1111
GridapDistributed = "f9701e48-63b3-45aa-9a63-9bc6c271f355"
1212
GridapGmsh = "3025c34a-b394-11e9-2a55-3fee550c04c8"
13+
GridapODEs = "55e38337-5b6e-4c7c-9cfc-e00dd49102e6"
1314
GridapP4est = "c2c8e14b-f5fd-423d-9666-1dd9ad120af9"
1415
GridapPETSc = "bcdc36c2-0c3e-11ea-095a-c9dadae499f1"
1516
IJulia = "7073ff75-c697-5162-941a-fcdaad2a7d2a"
2.51 MB
Loading

deps/build.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ 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"
2425

2526
Sys.rm(notebooks_dir;recursive=true,force=true)
2627
for (i,(title,filename)) in enumerate(files)

src/poisson_transient.jl

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
# ## Introduction
2+
3+
# In this tutorial we will learn how to use [`GridapODEs.jl`](https://github.com/gridap/GridapODEs.jl) to solve the transient PDEs by solving the *heat equation*, equivalent to the transient Poisson equation.
4+
5+
# We will focus on the time discretization on the equations, assuming that the reader is familiar with the spatial Finite Element discretization given in [tutorial 1](https://gridap.github.io/Tutorials/stable/pages/t001_poisson/).
6+
7+
# ## Problem statement
8+
9+
# We solve the heat equation in a 2-dimensional domain defined by a square with Homogenous Dirichlet boundaries, $\Gamma_D$. We consider a time-dependent conductivity $\kappa(t)=1.0 + 0.95\sin(2\pi t)$, a time-dependent volumetric forcing term $f(t) = \sin(\pi t)$ and a constant Homogenous boundary condition $g = 0.0$. The initial solution is $u(x,0) = u_0 = 0$. With these definitions, the strong form of the problem reads:
10+
11+
# ```math
12+
# \left\lbrace
13+
# \begin{aligned}
14+
# \frac{\partial u(t)}{\partial t} -\kappa(t)\Delta u(t) = f(t) \ &\text{in} \ \Omega,\\
15+
# u(t) = 0 \ &\text{on}\ \Gamma_{\rm D},\\
16+
# u(0) = 0 \ &\text{in}\ \Omega\\
17+
# \end{aligned}
18+
# \right.
19+
# ```
20+
21+
# The weak form of the problem will read: find $u(t)\in U_g(t)$ such that
22+
23+
# ```math
24+
# m(t,u,v) + a(t,u,v) = b(t,v)\quad \forall v\in \ V
25+
# ```
26+
27+
# Note that $U_g(t)$ is a transient FE space, in the sense that Dirichlet boundary value of functions in $U_g$ can change in time. The definition of $m(u,v)$, $a(u,v)$ and $b(v)$ is as follows:
28+
29+
# ```math
30+
# \begin{aligned}
31+
# m(t,u,v) = \int_\Omega v\frac{\partial u}{\partial t} d\Omega, \\
32+
# a(t,u,v) = \int_\Omega \kappa(t) \nalba v\cdot \nabla u d\Omega, \\
33+
# b(t,v) = \int_\Omega v\ f(t) d\Omega
34+
# \end{aligned}
35+
# ```
36+
37+
# ## Discrete model and Triangulation
38+
39+
# Let us load the two packages that we will use in this tutorial `Gridap` and `GridapODEs`.
40+
using Gridap
41+
using GridapODEs
42+
using GridapODEs.ODETools
43+
using GridapODEs.TransientFETools
44+
45+
# Without going into the details we define the `DiscreteModel` and the `Triangulation`, as it is detailed in [tutorial 2](https://gridap.github.io/Tutorials/stable/pages/t002_validation/).
46+
47+
48+
𝒯 = CartesianDiscreteModel((0,1,0,1),(20,20))
49+
Ω = Interior(𝒯)
50+
= Measure(Ω,2)
51+
52+
# ## FE space
53+
54+
# In this tutorial we will use linear Lagrangian Finite Elements.
55+
refFE = ReferenceFE(lagrangian,Float64,1)
56+
57+
# The space of test functions is constant in time and is defined in steady problems:
58+
V = TestFESpace(𝒯,refFE,dirichlet_tags="boundary")
59+
60+
# The trial space is now a `TransientTrialFESpace`, wich is constructed from a `TestFESpace` and a function (or vector of functions) for the Dirichlet boundary condition/s. In that case, the boundary condition function is a time-independent constant, but it could also be a time-dependent field depending on the coordinates $x$ and time $t$.
61+
g(x,t::Real) = 0.0
62+
g(t::Real) = x -> g(x,t)
63+
U = TransientTrialFESpace(V,g)
64+
65+
# ## Weak form
66+
67+
# The weak form of the problem follows the same structure as other `Gridap` tutorials, where we define the bilinear and linear forms to define the `FEOperator`. In this case we need to deal with time-dependent quantities and with the presence of time derivatives. The former is handled by passing the time, $t$, as an additional argument to the form, i.e. $a(t,u,v)$. The later is defined using the time derivative operator `∂t`.
68+
69+
# The most general way of constructing a transient FE operator is by using the `TransientFEOperator` function, which receives a residual, a jacobian with respect to the unknown and a jacobian with respect to the time derivative.
70+
κ(t) = 1.0 + 0.95*sin(2π*t)
71+
f(t) = sin*t)
72+
res(t,u,v) = ( ∂t(u)*v + κ(t)*((u)(v)) - f(t)*v )dΩ
73+
jac(t,u,du,v) = ( κ(t)*((du)(v)) )dΩ
74+
jac_t(t,u,duₜ,v) = ( duₜ*v )dΩ
75+
op = TransientFEOperator(res,jac,jac_t,U,V)
76+
77+
# We can also take advantage of automatic differentitation techniques and use the `TransientFEOperator` function sending only the residual.
78+
op_AD = TransientFEOperator(res,U,V)
79+
80+
# Alternatively, we can exploit the fact that the problem is linear and use the transient Affine FE operator signature `TransientAffineFEOperator`. In that case, we send a form for the mass contribution, $m$, a form for the stiffness contribution, $a$, and the forcing term, $b$.
81+
m(t,u,v) = ( u*v )dΩ
82+
a(t,u,v) = ( κ(t)*((u)(v)) )dΩ
83+
b(t,v) = ( f(t)*v )dΩ
84+
op_Af = TransientAffineFEOperator(m,a,b,U,V)
85+
86+
# ### Alternative FE operator definitions
87+
88+
# For time-dependent problems with constant coefficients, which is not the case of this tutorial, one could use the optimized operator `TransientConstantMatrixFEOperator`, which assumes that the matrix contributions ($m$ and $a$) are time-independent. That is:
89+
m₀(u,v) = ( u*v )dΩ
90+
a₀(u,v) = ( κ(0.0)*((u)(v)) )dΩ
91+
op_CM = TransientConstantMatrixFEOperator(m,a,b,U,V)
92+
93+
# Going further, if we had a problem with constant forcing term, i.e. constant force and constant boundary conditions, we could have used the `TransientConstantFEOperator`. In that case the linear form is also time-independent.
94+
b₀(v) = ( f(0.0)*v )dΩ
95+
op_C = TransientConstantFEOperator(m,a,b,U,V)
96+
97+
# ## Transient solver
98+
99+
# Once we have the FE operator defined, we proceed with the definition of the transient solver. First, we define a linear solver to be used at each time step. Here we use the `LUSolver`, but other choices could be made.
100+
linear_solver = LUSolver()
101+
102+
# Then, we define the ODE solver. That is, the scheme that will be used for the time integration. In this tutorial we use the `ThetaMethod` with $\theta = 0.5$, resulting in a 2nd order scheme. The `ThetaMethod` function receives the linear solver, the time step size $\Delta t$ (constant) and the value of $\theta $.
103+
Δt = 0.05
104+
θ = 0.5
105+
ode_solver = ThetaMethod(linear_solver,Δt,θ)
106+
107+
# Finally, we define the solution using the `solve` function, giving the ODE solver, the FE operator, an initial solution, an initial time and a final time. To construct the initial condition we interpolate the initial value (in that case a constant value of 0.0) into the FE space $U(t)$ at $t=0.0$.
108+
u₀ = interpolate_everywhere(0.0,U(0.0))
109+
t₀ = 0.0
110+
T = 10.0
111+
uₕₜ = solve(ode_solver,op,u₀,t₀,T)
112+
113+
# ## Postprocessing
114+
115+
# We should highlight that `uₕₜ` is just an iterable function and the results at each time steps are only computed 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`. This can be done as follows:
116+
createpvd("poisson_transient_solution") do pvd
117+
for (uₕ,t) in uₕₜ
118+
pvd[t] = createvtk(Ω,"poisson_transient_solution_$t"*".vtu",cellfields=["u"=>uₕ])
119+
end
120+
end
121+
122+
# ![](../assets/poisson_transient/poisson_transient.gif)

0 commit comments

Comments
 (0)