Partitioning a time interval¶
Goalie is focused on solving PDEs and their adjoints over some time interval on sequences of meshes.
Suppose we have a time interval
for simulation start and end times
\(0 \leq t_{\mathrm{start}} < t_{\mathrm{end}}\).
One of the fundamental objects in
Goalie is the TimePartition
,
which subdivides \(\mathcal T\) into
\(n\in\mathbb N\) subintervals,
where \(t^{(0)}=t_{\mathrm{start}}\) and \(t^{(n)}=t_{\mathrm{end}}\). In a time-dependent mesh adaptive simulation, each subinterval will be associated with a single mesh.
We begin by importing Goalie.
from goalie import *
To create a TimePartition
, we
need at least four ingredients:
the end time;
the number of subintervals;
the timestep on each subinterval;
a list of field names for the solution components.
If the start time is not set then it is assumed to be zero.
The simplest possible partition is to consider a single subinterval. Suppose the interval is \((0,1]\), the timestep is \(\Delta t=\frac18\) and the field name is “solution”. This is written in code as
end_time = 1.0
num_subintervals = 1
dt = 0.125
field_names = ["solution"]
With these definitions, we should get
one subinterval of \((0,1]\) containing
eight timesteps. When constructing a
TimePartition
(or any other object),
it is often useful to use Goalie’s debugging
mode. This is specified using set_log_level()
.
set_log_level(DEBUG)
tp = TimePartition(end_time, num_subintervals, dt, field_names)
Notice that one of the things which is printed
out is num_timesteps_per_export
, which controls
how frequently data is to be exported to file
during a simulation. It defaults to one, but may
be specified as a keyword argument.
Based on the above values, the
TimePartition
computes the number
of exports per subinterval. Note that this
number assumes that exports occur at both
the start and end of the subinterval, so the
value may be one larger than you expect.
This partition isn’t particularly interesting. Let’s try constructing a new one with more than one subinterval.
num_subintervals = 2
tp = TimePartition(
end_time, num_subintervals, dt, field_names, num_timesteps_per_export=2
)
In some problems, the dynamics evolve such that different timesteps are suitable during different parts of the simulation. To account for that, it is possible to specify a list of timesteps corresponding to each subinterval.
dt = [0.125, 0.0625]
tp = TimePartition(
end_time, num_subintervals, dt, field_names, num_timesteps_per_export=2
)
Note that this means that there are more
exports in the second subinterval than the first.
This can be remedied by also setting
num_timesteps_per_export
as a list.
tp = TimePartition(
end_time, num_subintervals, dt, field_names, num_timesteps_per_export=[2, 4]
)
So far, we have assumed that the subintervals
are of uniform length. This need not be the case.
To set up a TimePartition
with non-uniform
subintervals, the subintervals need to be passed
to the constructor as a list of tuples.
subintervals = [(0.0, 0.75), (0.75, 1.0)]
tp = TimePartition(
end_time,
num_subintervals,
dt,
field_names,
num_timesteps_per_export=[2, 4],
subintervals=subintervals,
)
In the next demo, we
learn how to build a MeshSeq
object
on top of a partitioned time interval, so that
we can solve PDEs on multiple meshes.
This demo can also be accessed as a Python script.