Skip to content

Commit 53367a7

Browse files
Renamed TimeSlicing into TimeSpaceFunction and updated constructor
1 parent 16d4035 commit 53367a7

16 files changed

+143
-157
lines changed

docs/src/ODEs.md

+10-10
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,7 @@ A `TransientTrialFESpace` can be evaluated at any time derivative order, and the
9191

9292
For example, the following creates a transient `FESpace` and evaluates its first two time derivatives.
9393
```
94-
gtx(t, x) = x[1] + x[2] * t
95-
g = time_slicing(gtx)
94+
g(t) = x -> x[1] + x[2] * t
9695
V = FESpace(model, reffe, dirichlet_tags="boundary")
9796
U = TransientTrialFESpace (V, g)
9897
@@ -106,13 +105,6 @@ U0 = U(t0)
106105
∂ttU0 = ∂ttU(t0)
107106
```
108107

109-
## The `time_slicing` constructor
110-
Note that the constructor `time_slicing` was used above to prescribe the boundary condition, instead of a more natural function `f(t, x) = gtx(t, x)` or functor `F(t) = x -> gtx(t, x)`. It would be possible to provide `f` instead of `g` in this example, but it would not be possible to do so for time-dependent coefficients inside an integrand, simply because `Gridap` expects an integrand to be a function of space only. This `time_slicing` constructor unifies these two scenarios and is motivated by performance and convenience reasons:
111-
* With the functor approach, `h(t)` is an anonymous function of `x`. This leads to performance drops when evaluating `h(t)(x)`, even when the function `h(t)` is instantiated only once, i.e. creating `ht = h(t)` and calling `ht(x)`. An even more significant loss in performance happens when computing (space or time) derivatives with automatic differentiation. With the `time_slicing` constructor, `g(t)` is a named function of `x`, and brings the same performance as if calling `gtx`.
112-
* With the functor approach, applying spatial differential operators is somewhat cumbersome, for example one would have to write `∂t(h)(t)(x) - Δ(h(t))(x)`. With the `time_slicing` constructor, one can simply write `∂t(g)(t, x) - Δ(g)(t, x)`.
113-
114-
As a summary, `g = time_slicing(gtx)` allows the syntax `op(g)`, `op(g)(t)` and `op(g)(t, x)`, for all spatial and temporal differential operator, i.e. `op` in `(time_derivative, gradient, symmetric_gradient, divergence, curl, laplacian)` and their symbolic aliases (`∂t`, `∂tt`, ``, ...).
115-
116108
## Cell fields
117109
The time-dependent equivalent of `CellField` is `TransientCellField`. It stores the cell field itself together with its derivatives up to the order of the ODE.
118110

@@ -163,7 +155,15 @@ TransientLinearFEOperator((stiffness, mass), res, U, V, constant_forms=(false, t
163155
```
164156
If $\kappa$ is constant, the keyword `constant_forms` could be replaced by `(true, true)`.
165157

166-
**Important** Note that in these examples, for optimal performance, `κ` should be a `time_slicing`.
158+
## The `TimeSpaceFunction` constructor
159+
Apply differential operators on a function that depends on time and space is somewhat cumbersome. Let `f` be a function of time and space, and `g(t) = x -> f(t, x)` (as in the prescription of the boundary conditions `g` above). Applying the operator $\partial_{t} - \Delta$ to `g` and evaluating at $(t, x)$ is written `∂t(g)(t)(x) - Δ(g(t))(x)`.
160+
161+
The constructor `TimeSpaceFunction` allows for simpler notations: let `h = TimeSpaceFunction(g)`. The object `h` is a functor that supports the notations
162+
* `op(h)`: a `TimeSpaceFunction` representing both `t -> x -> op(f)(t, x)` and `(t, x) -> op(f)(t, x)`,
163+
* `op(h)(t)`: a function of space representing `x -> op(f)(t, x)`
164+
* `op(h)(t, x)`: the quantity `op(f)(t, x)` (this notation is equivalent to `op(h)(t)(x)`),
165+
166+
for all spatial and temporal differential operator, i.e. `op` in `(time_derivative, gradient, symmetric_gradient, divergence, curl, laplacian)` and their symbolic aliases (`∂t`, `∂tt`, ``, ...). The operator above applied to `h` and evaluated at `(t, x)` can be conveniently written `∂t(h)(t, x) - Δ(h)(t, x)`.
167167

168168
## Solver and solution
169169
The next step is to choose an ODE solver (see below for a full list) and specify the boundary conditions. The solution can then be iterated over until the final time is reached.

src/Exports.jl

-1
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,6 @@ using Gridap.CellData: ∫; export ∫
182182
@publish Visualization createpvd
183183
@publish Visualization savepvd
184184

185-
@publish ODEs time_slicing
186185
@publish ODEs ∂t
187186
@publish ODEs ∂tt
188187
@publish ODEs ForwardEuler

src/ODEs/ODEs.jl

+1-3
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,7 @@ const ε = 100 * eps()
3535

3636
include("TimeDerivatives.jl")
3737

38-
export TimeSlicing
39-
export time_slicing
40-
38+
export TimeSpaceFunction
4139
export time_derivative
4240
export ∂t
4341
export ∂tt

src/ODEs/TimeDerivatives.jl

+40-43
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,40 @@
1-
################
2-
# Time slicing #
3-
################
1+
#####################
2+
# TimeSpaceFunction #
3+
#####################
44
"""
5-
struct TimeSlicing{F} <: Function end
6-
7-
`TimeSlicing` is an operator that can be applied to a function of time and space, and
8-
that, to a given time, produces the function of space at that time, allowing for the
9-
following syntax: if `f(t, x)` is defined, then `TimeSlicing(f)(t)(x) = f(t, x)`.
10-
11-
This is needed prevent performance drops with automatic differentiation (e.g. when taking
12-
the time derivative of Dirichlet boundary conditions), and with integration (i.e. when
13-
an integrand contains a time-dependent coefficient).
5+
struct TimeSpaceFunction{F} <: Function end
6+
7+
`TimeSpaceFunction` allows for convenient ways to apply differential operators to
8+
functions that depend on time and space. More precisely, if `f` is a function that, to
9+
a given time, returns a function of space (i.e. `f` is evaluated at time `t` and position
10+
`x` via `f(t)(x)`), then `F = TimeSpaceFunction(f)` supports the following syntax:
11+
* `op(F)`: a `TimeSpaceFunction` representing both `t -> x -> op(f)(t)(x)` and `(t, x) -> op(f)(t)(x)`,
12+
* `op(F)(t)`: a function of space representing `x -> op(f)(t)(x)`
13+
* `op(F)(t, x)`: the quantity `op(f)(t)(x)` (this notation is equivalent to `op(F)(t)(x)`),
14+
for all spatial and temporal differential operator, i.e. `op` in `(time_derivative,
15+
gradient, symmetric_gradient, divergence, curl, laplacian)` and their symbolic aliases
16+
(`∂t`, `∂tt`, `∇`, ...).
1417
"""
15-
struct TimeSlicing{F} <: Function
18+
struct TimeSpaceFunction{F} <: Function
1619
f::F
1720
end
1821

19-
function (ts::TimeSlicing)(t)
20-
_ftx(x) = ts.f(t, x)
21-
_ftx
22-
end
23-
24-
function (ts::TimeSlicing)(t, x)
25-
ts.f(t, x)
26-
end
27-
28-
"""
29-
const time_slicing = TimeSlicing
30-
31-
Alias for `TimeSlicing`.
32-
"""
33-
const time_slicing = TimeSlicing
22+
(ts::TimeSpaceFunction)(t) = ts.f(t)
23+
(ts::TimeSpaceFunction)(t, x) = ts.f(t)(x)
3424

3525
##################################
3626
# Spatial differential operators #
3727
##################################
38-
# Using the rule spatial_op(ts)(t, x) = spatial_op(ts(t))(x)
28+
# Using the rule spatial_op(ts)(t)(x) = spatial_op(ts(t))(x)
3929
for op in (:(Fields.gradient), :(Fields.symmetric_gradient), :(Fields.divergence),
4030
:(Fields.curl), :(Fields.laplacian))
4131
@eval begin
42-
function ($op)(ts::TimeSlicing)
43-
_op(t, x) = $op(ts(t))(x)
44-
TimeSlicing(_op)
32+
function ($op)(ts::TimeSpaceFunction)
33+
function _op(t)
34+
_op_t(x) = $op(ts(t))(x)
35+
_op_t
36+
end
37+
TimeSpaceFunction(_op)
4538
end
4639
end
4740
end
@@ -112,33 +105,37 @@ end
112105
# Specialisation for `Function` #
113106
#################################
114107
function time_derivative(f::Function)
115-
function dfdt(t, x)
116-
T = return_type(f, t, x)
117-
_time_derivative(T, f, t, x)
108+
function dfdt(t)
109+
ft = f(t)
110+
function dfdt_t(x)
111+
T = return_type(ft, x)
112+
_time_derivative(T, f, t, x)
113+
end
114+
dfdt_t
118115
end
119116
dfdt
120117
end
121118

122119
function _time_derivative(T::Type{<:Real}, f, t, x)
123-
partial(t) = f(t, x)
120+
partial(t) = f(t)(x)
124121
ForwardDiff.derivative(partial, t)
125122
end
126123

127124
function _time_derivative(T::Type{<:VectorValue}, f, t, x)
128-
partial(t) = get_array(f(t, x))
125+
partial(t) = get_array(f(t)(x))
129126
VectorValue(ForwardDiff.derivative(partial, t))
130127
end
131128

132129
function _time_derivative(T::Type{<:TensorValue}, f, t, x)
133-
partial(t) = get_array(f(t, x))
130+
partial(t) = get_array(f(t)(x))
134131
TensorValue(ForwardDiff.derivative(partial, t))
135132
end
136133

137-
####################################
138-
# Specialisation for `TimeSlicing` #
139-
####################################
140-
function time_derivative(ts::TimeSlicing)
141-
TimeSlicing(time_derivative(ts.f))
134+
##########################################
135+
# Specialisation for `TimeSpaceFunction` #
136+
##########################################
137+
function time_derivative(ts::TimeSpaceFunction)
138+
TimeSpaceFunction(time_derivative(ts.f))
142139
end
143140

144141
###############################

test/ODEsTests/TimeDerivativesTests.jl

+50-50
Original file line numberDiff line numberDiff line change
@@ -8,100 +8,100 @@ using Gridap
88
using Gridap.ODEs
99

1010
# First time derivative, scalar-valued
11-
f1(t, x) = 5 * x[1] * x[2] + x[2]^2 * t^3
12-
∂tf1(t, x) = 3 * x[2]^2 * t^2
11+
f1(t) = x -> 5 * x[1] * x[2] + x[2]^2 * t^3
12+
∂tf1(t) = x -> 3 * x[2]^2 * t^2
1313

14-
f2(t, x) = t^2
15-
∂tf2(t, x) = 2 * t
14+
f2(t) = x -> t^2
15+
∂tf2(t) = x -> 2 * t
1616

17-
f3(t, x) = x[1]^2
18-
∂tf3(t, x) = zero(x[1])
17+
f3(t) = x -> x[1]^2
18+
∂tf3(t) = x -> zero(x[1])
1919

20-
f4(t, x) = x[1]^t^2
21-
∂tf4(t, x) = 2 * t * log(x[1]) * f4(t, x)
20+
f4(t) = x -> x[1]^t^2
21+
∂tf4(t) = x -> 2 * t * log(x[1]) * f4(t)(x)
2222

2323
for (f, ∂tf) in ((f1, ∂tf1), (f2, ∂tf2), (f3, ∂tf3), (f4, ∂tf4),)
24-
dtf(t, x) = ForwardDiff.derivative(t -> f(t, x), t)
24+
dtf(t) = x -> ForwardDiff.derivative(t -> f(t)(x), t)
2525

2626
tv = rand(Float64)
2727
xv = Point(rand(Float64, 2)...)
28-
@test ∂t(f)(tv, xv) ∂tf(tv, xv)
29-
@test ∂t(f)(tv, xv) dtf(tv, xv)
28+
@test ∂t(f)(tv)(xv) ∂tf(tv)(xv)
29+
@test ∂t(f)(tv)(xv) dtf(tv)(xv)
3030

31-
slicing = time_slicing(f)
32-
@test slicing(tv)(xv) f(tv, xv)
33-
@test ∂t(slicing)(tv)(xv) ∂tf(tv, xv)
31+
F = TimeSpaceFunction(f)
32+
@test F(tv)(xv) f(tv)(xv)
33+
@test ∂t(F)(tv)(xv) ∂tf(tv)(xv)
3434
end
3535

3636
# First time derivative, vector-valued
37-
f1(t, x) = VectorValue(5 * x[1] * x[2], x[2]^2 * t^3)
38-
∂tf1(t, x) = VectorValue(zero(x[1]), x[2]^2 * 3 * t^2)
37+
f1(t) = x -> VectorValue(5 * x[1] * x[2], x[2]^2 * t^3)
38+
∂tf1(t) = x -> VectorValue(zero(x[1]), x[2]^2 * 3 * t^2)
3939

40-
f2(t, x) = VectorValue(x[1]^2, zero(x[2]))
41-
∂tf2(t, x) = VectorValue(zero(x[1]), zero(x[2]))
40+
f2(t) = x -> VectorValue(x[1]^2, zero(x[2]))
41+
∂tf2(t) = x -> VectorValue(zero(x[1]), zero(x[2]))
4242

43-
f3(t, x) = VectorValue(x[1]^2, t)
44-
∂tf3(t, x) = VectorValue(zero(x[1]), one(t))
43+
f3(t) = x -> VectorValue(x[1]^2, t)
44+
∂tf3(t) = x -> VectorValue(zero(x[1]), one(t))
4545

4646
for (f, ∂tf) in ((f1, ∂tf1), (f2, ∂tf2), (f3, ∂tf3),)
47-
dtf(t, x) = VectorValue(ForwardDiff.derivative(t -> get_array(f(t, x)), t))
47+
dtf(t) = x -> VectorValue(ForwardDiff.derivative(t -> get_array(f(t)(x)), t))
4848

4949
tv = rand(Float64)
5050
xv = Point(rand(Float64, 2)...)
51-
@test ∂t(f)(tv, xv) ∂tf(tv, xv)
52-
@test ∂t(f)(tv, xv) dtf(tv, xv)
51+
@test ∂t(f)(tv)(xv) ∂tf(tv)(xv)
52+
@test ∂t(f)(tv)(xv) dtf(tv)(xv)
5353

54-
slicing = time_slicing(f)
55-
@test slicing(tv)(xv) f(tv, xv)
56-
@test ∂t(slicing)(tv)(xv) ∂tf(tv, xv)
54+
F = TimeSpaceFunction(f)
55+
@test F(tv)(xv) f(tv)(xv)
56+
@test ∂t(F)(tv)(xv) ∂tf(tv)(xv)
5757
end
5858

5959
# First time derivative, tensor-valued
60-
f1(t, x) = TensorValue(x[1] * t, x[2] * t, x[1] * x[2], x[1] * t^2)
61-
∂tf1(t, x) = TensorValue(x[1], x[2], zero(x[1]), 2 * x[1] * t)
60+
f1(t) = x -> TensorValue(x[1] * t, x[2] * t, x[1] * x[2], x[1] * t^2)
61+
∂tf1(t) = x -> TensorValue(x[1], x[2], zero(x[1]), 2 * x[1] * t)
6262

6363
for (f, ∂tf) in ((f1, ∂tf1),)
64-
dtf(t, x) = TensorValue(ForwardDiff.derivative(t -> get_array(f(t, x)), t))
64+
dtf(t) = x -> TensorValue(ForwardDiff.derivative(t -> get_array(f(t)(x)), t))
6565

6666
tv = rand(Float64)
6767
xv = Point(rand(Float64, 2)...)
68-
@test ∂t(f)(tv, xv) ∂tf(tv, xv)
69-
@test ∂t(f)(tv, xv) dtf(tv, xv)
68+
@test ∂t(f)(tv)(xv) ∂tf(tv)(xv)
69+
@test ∂t(f)(tv)(xv) dtf(tv)(xv)
7070

71-
slicing = time_slicing(f)
72-
@test slicing(tv)(xv) f(tv, xv)
73-
@test ∂t(slicing)(tv)(xv) ∂tf(tv, xv)
71+
F = TimeSpaceFunction(f)
72+
@test F(tv)(xv) f(tv)(xv)
73+
@test ∂t(F)(tv)(xv) ∂tf(tv)(xv)
7474
end
7575

7676
# Spatial derivatives
77-
ftx(t, x) = x[1]^2 * t + x[2]
78-
f = time_slicing(ftx)
77+
ft(t) = x -> x[1]^2 * t + x[2]
78+
f = TimeSpaceFunction(ft)
7979

8080
tv = rand(Float64)
8181
xv = Point(rand(Float64, 2)...)
82-
@test (f)(tv, xv) Point(2 * xv[1] * tv, 1.0)
82+
@test (f)(tv)(xv) Point(2 * xv[1] * tv, 1.0)
8383

8484
# Second time derivative, scalar-valued
85-
f1(t, x) = t^2
86-
∂tf1(t, x) = 2 * t
87-
∂ttf1(t, x) = 2 * one(t)
85+
f1(t) = x -> t^2
86+
∂tf1(t) = x -> 2 * t
87+
∂ttf1(t) = x -> 2 * one(t)
8888

89-
f2(t, x) = x[1] * t^2
90-
∂tf2(t, x) = 2 * x[1] * t
91-
∂ttf2(t, x) = 2 * x[1]
89+
f2(t) = x -> x[1] * t^2
90+
∂tf2(t) = x -> 2 * x[1] * t
91+
∂ttf2(t) = x -> 2 * x[1]
9292

9393
for (f, ∂tf, ∂ttf) in ((f1, ∂tf1, ∂ttf1), (f2, ∂tf2, ∂ttf2),)
94-
dtf(t, x) = ForwardDiff.derivative(t -> f(t, x), t)
95-
dttf(t, x) = ForwardDiff.derivative(t -> dtf(t, x), t)
94+
dtf(t) = x -> ForwardDiff.derivative(t -> f(t)(x), t)
95+
dttf(t) = x -> ForwardDiff.derivative(t -> dtf(t)(x), t)
9696

9797
tv = rand(Float64)
9898
xv = Point(rand(Float64, 2)...)
99-
@test ∂tt(f)(tv, xv) ∂ttf(tv, xv)
100-
@test ∂tt(f)(tv, xv) dttf(tv, xv)
99+
@test ∂tt(f)(tv)(xv) ∂ttf(tv)(xv)
100+
@test ∂tt(f)(tv)(xv) dttf(tv)(xv)
101101

102-
slicing = time_slicing(f)
103-
@test slicing(tv)(xv) f(tv, xv)
104-
@test ∂tt(slicing)(tv)(xv) ∂ttf(tv, xv)
102+
F = TimeSpaceFunction(f)
103+
@test F(tv)(xv) f(tv)(xv)
104+
@test ∂tt(F)(tv)(xv) ∂ttf(tv)(xv)
105105
end
106106

107107
end # module TimeDerivativesTests

test/ODEsTests/TransientCellFieldsTests.jl

+1-2
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,7 @@ using Gridap.MultiField
1212
using Gridap.ODEs
1313
using Gridap.ODEs: TransientMultiFieldCellField
1414

15-
ftx(t, x) = sum(x)
16-
f = time_slicing(ftx)
15+
f(t) = x -> sum(x)
1716

1817
domain = (0, 1, 0, 1)
1918
partition = (5, 5)

test/ODEsTests/TransientFEOperatorsSolutionsTests.jl

+6-6
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ using Gridap.ODEs
1515
include("ODESolversMocks.jl")
1616

1717
# Analytical functions
18-
utx(t, x) = x[1] * (1 - x[2]) * (1 + t)
19-
u = time_slicing(utx)
18+
ut(t) = x -> x[1] * (1 - x[2]) * (1 + t)
19+
u = TimeSpaceFunction(ut)
2020

2121
# Geometry
2222
domain = (0, 1, 0, 1)
@@ -104,8 +104,8 @@ end
104104
# First-order #
105105
###############
106106
order = 1
107-
ftx(t, x) = ∂t(u)(t)(x) - Δ(u(t))(x)
108-
f = time_slicing(ftx)
107+
ft(t) = x -> ∂t(u)(t, x) - Δ(u)(t, x)
108+
f = TimeSpaceFunction(ft)
109109

110110
mass(t, ∂ₜu, v) = (∂ₜu v) *
111111
mass(t, u, ∂ₜu, v) = mass(t, ∂ₜu, v)
@@ -200,8 +200,8 @@ end
200200
# Second-order #
201201
################
202202
order = 2
203-
ftx(t, x) = ∂tt(u)(t)(x) + ∂t(u)(t)(x) - Δ(u(t))(x)
204-
f = time_slicing(ftx)
203+
ft(t) = x -> ∂tt(u)(t, x) + ∂t(u)(t, x) - Δ(u)(t, x)
204+
f = TimeSpaceFunction(ft)
205205

206206
mass(t, ∂ₜₜu, v) = (∂ₜₜu v) *
207207
mass(t, u, ∂ₜₜu, v) = mass(t, ∂ₜₜu, v)

test/ODEsTests/TransientFEProblemsTests/FreeSurfacePotentialFlowTests.jl

+2-4
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,8 @@ tF = 10 * dt # 2 * π
2323
tol = 1.0e-2
2424

2525
# Exact solution
26-
ϕₑtx(t, x) = ω / k * ξ * (cosh(k * (x[2]))) / sinh(k * H) * sin(k * x[1] - ω * t)
27-
ϕₑ = time_slicing(ϕₑtx)
28-
ηₑtx(t, x) = ξ * cos(k * x[1] - ω * t)
29-
ηₑ = time_slicing(ηₑtx)
26+
ϕₑ(t) = x -> ω / k * ξ * (cosh(k * (x[2]))) / sinh(k * H) * sin(k * x[1] - ω * t)
27+
ηₑ(t) = x -> ξ * cos(k * x[1] - ω * t)
3028

3129
# Domain
3230
domain = (0, L, 0, H)

0 commit comments

Comments
 (0)