|
| 1 | +# In this tutorial, we will look at how to |
| 2 | +# - Evaluate `CellFields` at arbitrary points |
| 3 | +# - Interpolate finite element functions defined on different |
| 4 | +# triangulations. We will consider examples for |
| 5 | +# - Lagrangian finite element spaces |
| 6 | +# - Raviart Thomas finite element spaces |
| 7 | +# - Vector-Valued Spaces |
| 8 | +# - Multifield finite element spaces |
| 9 | + |
| 10 | +# ## Problem Statement |
| 11 | +# Let $\mathcal{T}_1$ and $\mathcal{T}_2$ be two triangulations of a |
| 12 | +# domain $\Omega$. Let $V_i$ be the finite element space defined on |
| 13 | +# the triangulation $\mathcal{T}_i$ for $i=1,2$. Let $f_h \in V_1$. The |
| 14 | +# interpolation problem is to find $g_h \in V_2$ such that |
| 15 | +# |
| 16 | +# ```math |
| 17 | +# dof_k^{V_2}(g_h) = dof_k^{V_2}(f_h),\quad \forall k \in |
| 18 | +# \{1,\dots,N_{dof}^{V_2}\} |
| 19 | +# ``` |
| 20 | + |
| 21 | + |
| 22 | +# ## Setup |
| 23 | +# For the purpose of this tutorial we require `Test`, `Gridap` along with the |
| 24 | +# following submodules of `Gridap` |
| 25 | + |
| 26 | +using Test |
| 27 | +using Gridap |
| 28 | +using Gridap.CellData |
| 29 | +using Gridap.Visualization |
| 30 | + |
| 31 | +# We now create a computational domain on the unit square $[0,1]^2$ consisting |
| 32 | +# of 5 cells per direction |
| 33 | + |
| 34 | +domain = (0,1,0,1) |
| 35 | +partition = (5,5) |
| 36 | +𝒯₁ = CartesianDiscreteModel(domain, partition) |
| 37 | + |
| 38 | + |
| 39 | +# ## Background |
| 40 | +# `Gridap` offers the feature to evaluate functions at arbitrary |
| 41 | +# points in the domain. This will be shown in the next |
| 42 | +# section. Interpolation then takes advantage of this feature to |
| 43 | +# obtain the `FEFunction` in the new space from the old one by |
| 44 | +# evaluating the appropriate degrees of freedom. Interpolation works |
| 45 | +# using the composite type `Interpolable` to tell `Gridap` that the |
| 46 | +# argument can be interpolated between triangulations. |
| 47 | + |
| 48 | +# ## Interpolating between Lagrangian FE Spaces |
| 49 | + |
| 50 | +# Let us define the infinite dimensional function |
| 51 | + |
| 52 | +f(x) = x[1] + x[2] |
| 53 | + |
| 54 | +# This function will be interpolated to the source `FESpace` |
| 55 | +# $V_1$. The space can be built using |
| 56 | + |
| 57 | +reffe₁ = ReferenceFE(lagrangian, Float64, 1) |
| 58 | +V₁ = FESpace(𝒯₁, reffe₁) |
| 59 | + |
| 60 | +# Finally to build the function $f_h$, we do |
| 61 | + |
| 62 | +fₕ = interpolate_everywhere(f,V₁) |
| 63 | + |
| 64 | +# To construct arbitrary points in the domain, we use `Random` package: |
| 65 | + |
| 66 | +using Random |
| 67 | +pt = Point(rand(2)) |
| 68 | +pts = [Point(rand(2)) for i in 1:3] |
| 69 | + |
| 70 | +# The finite element function $f_h$ can be evaluated at arbitrary points (or |
| 71 | +# array of points) by |
| 72 | + |
| 73 | +fₕ(pt), fₕ.(pts) |
| 74 | + |
| 75 | +# We can also check our results using |
| 76 | + |
| 77 | +@test fₕ(pt) ≈ f(pt) |
| 78 | +@test fₕ.(pts) ≈ f.(pts) |
| 79 | + |
| 80 | +# Now let us define the new triangulation $\mathcal{T}_2$ of |
| 81 | +# $\Omega$. We build the new triangulation using a partition of 20 cells per |
| 82 | +# direction. The map can be passed as an argument to |
| 83 | +# `CartesianDiscreteModel` to define the position of the vertices in |
| 84 | +# the new mesh. |
| 85 | + |
| 86 | +partition = (20,20) |
| 87 | +𝒯₂ = CartesianDiscreteModel(domain,partition) |
| 88 | + |
| 89 | +# As before, we define the new `FESpace` consisting of second order |
| 90 | +# elements |
| 91 | + |
| 92 | +reffe₂ = ReferenceFE(lagrangian, Float64, 2) |
| 93 | +V₂ = FESpace(𝒯₂, reffe₂) |
| 94 | + |
| 95 | +# Now we interpolate $f_h$ onto $V_2$ to obtain the new function |
| 96 | +# $g_h$. The first step is to create the `Interpolable` version of |
| 97 | +# $f_h$. |
| 98 | + |
| 99 | +ifₕ = Interpolable(fₕ) |
| 100 | + |
| 101 | +# Then to obtain $g_h$, we dispatch `ifₕ` and the new `FESpace` $V_2$ |
| 102 | +# to the `interpolate_everywhere` method of `Gridap`. |
| 103 | + |
| 104 | +gₕ = interpolate_everywhere(ifₕ, V₂) |
| 105 | + |
| 106 | +# We can also use |
| 107 | +# `interpolate` if interpolating only on the free dofs or |
| 108 | +# `interpolate_dirichlet` if interpolating the Dirichlet dofs of the |
| 109 | +# `FESpace`. |
| 110 | + |
| 111 | +ḡₕ = interpolate(ifₕ, V₂) |
| 112 | + |
| 113 | +# The finite element function $\bar{g}_h$ is the same as $g_h$ in this |
| 114 | +# example since all the dofs are free. |
| 115 | + |
| 116 | +@test gₕ.cell_dof_values == ḡₕ.cell_dof_values |
| 117 | + |
| 118 | +# Now we obtain a finite element function using `interpolate_dirichlet` |
| 119 | + |
| 120 | +g̃ₕ = interpolate_dirichlet(ifₕ, V₂) |
| 121 | + |
| 122 | +# Now $\tilde{g}_h$ will be equal to 0 since there are |
| 123 | +# no Dirichlet nodes defined in the `FESpace`. We can check by running |
| 124 | + |
| 125 | +g̃ₕ.cell_dof_values |
| 126 | + |
| 127 | +# Like earlier we can check our results for `gₕ`: |
| 128 | + |
| 129 | +@test fₕ(pt) ≈ gₕ(pt) ≈ f(pt) |
| 130 | +@test fₕ.(pts) ≈ gₕ.(pts) ≈ f.(pts) |
| 131 | + |
| 132 | +# We can visualize the results using Paraview |
| 133 | + |
| 134 | +writevtk(get_triangulation(fₕ), "source", cellfields=["fₕ"=>fₕ]) |
| 135 | +writevtk(get_triangulation(gₕ), "target", cellfields=["gₕ"=>gₕ]) |
| 136 | + |
| 137 | +# which produces the following output |
| 138 | + |
| 139 | +#  |
| 140 | + |
| 141 | +# ## Interpolating between Raviart-Thomas FESpaces |
| 142 | + |
| 143 | +# The procedure is identical to Lagrangian finite element spaces, as |
| 144 | +# discussed in the previous section. The extra thing here is that |
| 145 | +# functions in Raviart-Thomas spaces are vector-valued. The degrees of |
| 146 | +# freedom of the RT spaces are fluxes of the function across the edge |
| 147 | +# of the element. Refer to the |
| 148 | +# [tutorial](https://gridap.github.io/Tutorials/dev/pages/t007_darcy/) |
| 149 | +# on Darcy equation with RT for more information on the RT |
| 150 | +# elements. |
| 151 | + |
| 152 | +# Assuming a function |
| 153 | + |
| 154 | +f(x) = VectorValue([x[1], x[2]]) |
| 155 | + |
| 156 | +# on the domain, we build the associated finite dimensional version |
| 157 | +# $f_h \in V_1$. |
| 158 | + |
| 159 | +reffe₁ = ReferenceFE(raviart_thomas, Float64, 1) # RT space of order 1 |
| 160 | +V₁ = FESpace(𝒯₁, reffe₁) |
| 161 | +fₕ = interpolate_everywhere(f, V₁) |
| 162 | + |
| 163 | +# As before, we can evaluate the RT function on any arbitrary point in |
| 164 | +# the domain. |
| 165 | + |
| 166 | +fₕ(pt), fₕ.(pts) |
| 167 | + |
| 168 | +# Constructing the target RT space and building the `Interpolable` |
| 169 | +# object, |
| 170 | + |
| 171 | +reffe₂ = ReferenceFE(raviart_thomas, Float64, 1) # RT space of order 1 |
| 172 | +V₂ = FESpace(𝒯₂, reffe₂) |
| 173 | +ifₕ = Interpolable(fₕ) |
| 174 | + |
| 175 | +# we can construct the new `FEFunction` $g_h \in V_2$ from $f_h$ |
| 176 | + |
| 177 | +gₕ = interpolate_everywhere(ifₕ, V₂) |
| 178 | + |
| 179 | +# Like earlier we can check our results |
| 180 | + |
| 181 | +@test gₕ(pt) ≈ f(pt) ≈ fₕ(pt) |
| 182 | + |
| 183 | +# ## Interpolating vector-valued functions |
| 184 | + |
| 185 | +# We can also interpolate vector-valued functions across |
| 186 | +# triangulations. First, we define a vector-valued function on a |
| 187 | +# two-dimensional mesh. |
| 188 | + |
| 189 | +f(x) = VectorValue([x[1], x[1]+x[2]]) |
| 190 | + |
| 191 | +# We then create a vector-valued reference element containing linear |
| 192 | +# elements along with the source finite element space $V_1$. |
| 193 | + |
| 194 | +reffe₁ = ReferenceFE(lagrangian, VectorValue{2,Float64}, 1) |
| 195 | +V₁ = FESpace(𝒯₁, reffe₁) |
| 196 | +fₕ = interpolate_everywhere(f, V₁) |
| 197 | + |
| 198 | +# The target finite element space $V_2$ can be defined in a similar manner. |
| 199 | + |
| 200 | +reffe₂ = ReferenceFE(lagrangian, VectorValue{2,Float64}, 2) |
| 201 | +V₂ = FESpace(𝒯₂, reffe₂) |
| 202 | + |
| 203 | +# The rest of the process is similar to the previous sections, i.e., |
| 204 | +# define the `Interpolable` version of $f_h$ and use |
| 205 | +# `interpolate_everywhere` to find $g_h \in V₂$. |
| 206 | + |
| 207 | +ifₕ = Interpolable(fₕ) |
| 208 | +gₕ = interpolate_everywhere(ifₕ, V₂) |
| 209 | + |
| 210 | +# We can then check the results |
| 211 | + |
| 212 | +@test gₕ(pt) ≈ f(pt) ≈ fₕ(pt) |
| 213 | + |
| 214 | + |
| 215 | +# ## Interpolating Multi-field Functions |
| 216 | + |
| 217 | +# Similarly, it is possible to interpolate between multi-field finite element |
| 218 | +# functions. First, we define the components $h_1(x), h_2(x)$ of a |
| 219 | +# multi-field function $h(x)$ as follows. |
| 220 | + |
| 221 | +h₁(x) = x[1]+x[2] |
| 222 | +h₂(x) = x[1] |
| 223 | + |
| 224 | +# Next we create a Lagrangian finite element space containing linear |
| 225 | +# elements. |
| 226 | + |
| 227 | +reffe₁ = ReferenceFE(lagrangian, Float64, 1) |
| 228 | +V₁ = FESpace(𝒯₁, reffe₁) |
| 229 | + |
| 230 | +# Next we create a `MultiFieldFESpace` $V_1 \times V_1$ and |
| 231 | +# interpolate the function $h(x)$ to the source space $V_1$. |
| 232 | + |
| 233 | +V₁xV₁ = MultiFieldFESpace([V₁,V₁]) |
| 234 | +fₕ = interpolate_everywhere([h₁, h₂], V₁xV₁) |
| 235 | + |
| 236 | +# Similarly, the target multi-field finite element space is created |
| 237 | +# using $\Omega_2$. |
| 238 | + |
| 239 | +reffe₂ = ReferenceFE(lagrangian, Float64, 2) |
| 240 | +V₂ = FESpace(𝒯₂, reffe₂) |
| 241 | +V₂xV₂ = MultiFieldFESpace([V₂,V₂]) |
| 242 | + |
| 243 | +# Now, to find $g_h \in V_2 \times V_2$, we first extract the components of |
| 244 | +# $f_h$ and obtain the `Interpolable` version of the components. |
| 245 | + |
| 246 | +fₕ¹, fₕ² = fₕ |
| 247 | +ifₕ¹ = Interpolable(fₕ¹) |
| 248 | +ifₕ² = Interpolable(fₕ²) |
| 249 | + |
| 250 | +# We can then use `interpolate_everywhere` on the `Interpolable` |
| 251 | +# version of the components and obtain $g_h \in V_2 \times V_2$ as |
| 252 | +# follows. |
| 253 | + |
| 254 | +gₕ = interpolate_everywhere([ifₕ¹,ifₕ²], V₂xV₂) |
| 255 | + |
| 256 | +# We can then check the results of the interpolation, component-wise. |
| 257 | + |
| 258 | +gₕ¹, gₕ² = gₕ |
| 259 | +@test fₕ¹(pt) ≈ gₕ¹(pt) |
| 260 | +@test fₕ²(pt) ≈ gₕ²(pt) |
| 261 | + |
| 262 | +# ## Acknowledgements |
| 263 | + |
| 264 | +# Gridap contributors acknowledge support received from Google, |
| 265 | +# Inc. through the Google Summer of Code 2021 project [A fast finite |
| 266 | +# element interpolator in |
| 267 | +# Gridap.jl](https://summerofcode.withgoogle.com/projects/#6175012823760896). |
0 commit comments