1.2: First steps in Trixi.jl: Create your first setup
In this part of the introductory guide, we will create a first Trixi.jl setup as an extension of elixir_advection_basic.jl
. Since Trixi.jl has a common basic structure for the setups, you can create your own by extending and modifying the following example.
Let's consider the linear advection equation for a state $u = u(x, y, t)$ on the two-dimensional spatial domain $[-1, 1] \times [-1, 1]$ with a source term
\[\frac{\partial}{\partial t}u + \frac{\partial}{\partial x} (0.2 u) - \frac{\partial}{\partial y} (0.7 u) = - 2 e^{-t} \sin\bigl(2 \pi (x - t) \bigr) \sin\bigl(2 \pi (y - t) \bigr),\]
with the initial condition
\[u(x, y, 0) = \sin\bigl(\pi x \bigr) \sin\bigl(\pi y \bigr),\]
and periodic boundary conditions.
The first step is to create and open a file with the .jl extension. You can do this with your favorite text editor (if you do not have one, we recommend VS Code). In this file, you will create your setup. The file can then be executed in Julia using, for example, trixi_include()
. Alternatively, you can execute each line of the following code one by one in the Julia REPL. This will generate useful output for nearly every command and improve your comprehension of the process.
To be able to use functionalities of Trixi.jl, you always need to load Trixi.jl itself and the OrdinaryDiffEq.jl package.
using Trixi
using OrdinaryDiffEq
The next thing to do is to choose an equation that is suitable for your problem. To see all the currently implemented equations, take a look at src/equations
. If you are interested in adding a new physics model that has not yet been implemented in Trixi.jl, take a look at the tutorials Adding a new scalar conservation law or Adding a non-conservative equation.
The linear scalar advection equation in two spatial dimensions
\[\frac{\partial}{\partial t}u + \frac{\partial}{\partial x} (a_1 u) + \frac{\partial}{\partial y} (a_2 u) = 0\]
is already implemented in Trixi.jl as LinearScalarAdvectionEquation2D
, for which we need to define a two-dimensional parameter advection_velocity
describing the parameters $a_1$ and $a_2$. Appropriate for our problem is (0.2, -0.7)
.
advection_velocity = (0.2, -0.7)
equations = LinearScalarAdvectionEquation2D(advection_velocity)
┌──────────────────────────────────────────────────────────────────────────────────────────────────┐
│ LinearScalarAdvectionEquation2D │
│ ═══════════════════════════════ │
│ #variables: ………………………………………………… 1 │
│ │ variable 1: …………………………………………… scalar │
└──────────────────────────────────────────────────────────────────────────────────────────────────┘
To solve our problem numerically using Trixi.jl, we have to discretize the spatial domain, for which we set up a mesh. One of the most used meshes in Trixi.jl is the TreeMesh
. The spatial domain used is $[-1, 1] \times [-1, 1]$. We set an initial number of elements in the mesh using initial_refinement_level
, which describes the initial number of hierarchical refinements. In this simple case, the total number of elements is 2^initial_refinement_level
throughout the simulation. The variable n_cells_max
is used to limit the number of elements in the mesh, which cannot be exceeded when using adaptive mesh refinement.
All minimum and all maximum coordinates must be combined into Tuples
.
coordinates_min = (-1.0, -1.0)
coordinates_max = (1.0, 1.0)
mesh = TreeMesh(coordinates_min, coordinates_max,
initial_refinement_level = 4,
n_cells_max = 30_000)
┌──────────────────────────────────────────────────────────────────────────────────────────────────┐
│ TreeMesh{2, Trixi.SerialTree{2, Float64}} │
│ ═════════════════════════════════════════ │
│ center: …………………………………………………………… [0.0, 0.0] │
│ length: …………………………………………………………… 2.0 │
│ periodicity: ……………………………………………… (true, true) │
│ current #cells: ……………………………………… 341 │
│ #leaf-cells: ……………………………………………… 256 │
│ maximum #cells: ……………………………………… 30000 │
└──────────────────────────────────────────────────────────────────────────────────────────────────┘
To approximate the solution of the defined model, we create a DGSEM
solver. The solution in each of the recently defined mesh elements will be approximated by a polynomial of degree polydeg
. For more information about discontinuous Galerkin methods, check out the Introduction to DG methods tutorial. By default, in the weak formulation DGSEM
initializes the surface flux as flux_central
and uses the physical flux for the volume integral.
solver = DGSEM(polydeg = 3)
┌──────────────────────────────────────────────────────────────────────────────────────────────────┐
│ DG{Float64} │
│ ═══════════ │
│ basis: ……………………………………………………………… LobattoLegendreBasis{Float64}(polydeg=3) │
│ mortar: …………………………………………………………… LobattoLegendreMortarL2{Float64}(polydeg=3) │
│ surface integral: ………………………………… SurfaceIntegralWeakForm │
│ │ surface flux: ……………………………………… flux_central │
│ volume integral: …………………………………… VolumeIntegralWeakForm │
└──────────────────────────────────────────────────────────────────────────────────────────────────┘
Now we need to define an initial condition for our problem. All the already implemented initial conditions for LinearScalarAdvectionEquation2D
can be found in src/equations/linear_scalar_advection_2d.jl
. If you want to use, for example, a Gaussian pulse, it can be used as follows:
initial_conditions = initial_condition_gauss
But to show you how an arbitrary initial condition can be implemented in a way suitable for Trixi.jl, we define our own initial conditions.
\[u(x, y, 0) = \sin\bigl(\pi x \bigr) \sin\bigl(\pi y \bigr).\]
The initial conditions function must take spatial coordinates, time and equation as arguments and returns an initial condition as a statically sized vector SVector
. Following the same structure, you can define your own initial conditions. The time variable t
can be unused in the initial condition, but might also be used to describe an analytical solution if known. If you use the initial condition as analytical solution, you can analyze your numerical solution by computing the error, see also the section about analyzing the solution.
function initial_condition_sinpi(x, t, equations::LinearScalarAdvectionEquation2D)
u = sinpi(x[1]) * sinpi(x[2])
return SVector(u)
end
initial_condition = initial_condition_sinpi
initial_condition_sinpi (generic function with 1 method)
The next step is to define a function of the source term corresponding to our problem.
\[f(u, x, y, t) = - 2 e^{-t} \sin\bigl(2 \pi (x - t) \bigr) \sin\bigl(2 \pi (y - t) \bigr)\]
This function must take the state variable, the spatial coordinates, the time and the equation itself as arguments and returns the source term as a static vector SVector
.
function source_term_exp_sinpi(u, x, t, equations::LinearScalarAdvectionEquation2D)
u = -2 * exp(-t) * sinpi(2 * (x[1] - t)) * sinpi(2 * (x[2] - t))
return SVector(u)
end
source_term_exp_sinpi (generic function with 1 method)
Now we collect all the information that is necessary to define a spatial discretization,
semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver;
source_terms = source_term_exp_sinpi)
┌──────────────────────────────────────────────────────────────────────────────────────────────────┐
│ SemidiscretizationHyperbolic │
│ ════════════════════════════ │
│ #spatial dimensions: ………………………… 2 │
│ mesh: ………………………………………………………………… TreeMesh{2, Trixi.SerialTree{2, Float64}} with length 341 │
│ equations: …………………………………………………… LinearScalarAdvectionEquation2D │
│ initial condition: ……………………………… initial_condition_sinpi │
│ boundary conditions: ………………………… Trixi.BoundaryConditionPeriodic │
│ source terms: …………………………………………… source_term_exp_sinpi │
│ solver: …………………………………………………………… DG │
│ total #DOFs per field: …………………… 4096 │
└──────────────────────────────────────────────────────────────────────────────────────────────────┘
which leaves us with an ODE problem in time with a span from 0.0
to 1.0
. This approach is commonly referred to as the method of lines.
tspan = (0.0, 1.0)
ode = semidiscretize(semi, tspan)
ODEProblem with uType Vector{Float64} and tType Float64. In-place: true
timespan: (0.0, 1.0)
u0: 4096-element Vector{Float64}:
0.0
0.0
0.0
0.0
0.0
0.011734602208786986
0.030369402873034978
0.04145470668348209
0.0
0.030369402873034978
⋮
0.0
0.04145470668348209
0.030369402873034978
0.011734602208786986
0.0
0.0
0.0
0.0
0.0
At this point, our problem is defined. We will use the solve
function defined in OrdinaryDiffEq.jl to get the solution. OrdinaryDiffEq.jl gives us the ability to customize the solver using callbacks without actually modifying it. Trixi.jl already has some implemented Callbacks. The most widely used callbacks in Trixi.jl are step control callbacks that are activated at the end of each time step to perform some actions, e.g. to print statistics. We will show you how to use some of the common callbacks.
To print a summary of the simulation setup at the beginning and to reset timers to zero, we use the SummaryCallback
.
summary_callback = SummaryCallback()
SummaryCallback
We also want to analyze the current state of the solution in regular intervals. The AnalysisCallback
outputs some useful statistical information during the simulation every interval
time steps.
analysis_callback = AnalysisCallback(semi, interval = 20)
┌──────────────────────────────────────────────────────────────────────────────────────────────────┐
│ AnalysisCallback │
│ ════════════════ │
│ interval: ……………………………………………………… 20 │
│ analyzer: ……………………………………………………… LobattoLegendreAnalyzer{Float64}(polydeg=6) │
│ │ error 1: …………………………………………………… l2_error │
│ │ error 2: …………………………………………………… linf_error │
│ │ integral 1: …………………………………………… entropy_timederivative │
│ save analysis to file: …………………… no │
└──────────────────────────────────────────────────────────────────────────────────────────────────┘
To indicate that a simulation is still running, we utilize the inexpensive AliveCallback
to periodically print information to the screen, such as the current time, every alive_interval
time steps.
alive_callback = AliveCallback(alive_interval = 10)
┌──────────────────────────────────────────────────────────────────────────────────────────────────┐
│ AliveCallback │
│ ═════════════ │
│ interval: ……………………………………………………… 10 │
└──────────────────────────────────────────────────────────────────────────────────────────────────┘
It is also possible to control the time step size using the StepsizeCallback
if the time integration method isn't adaptive itself. To get more details, look at CFL based step size control.
stepsize_callback = StepsizeCallback(cfl = 0.9)
┌──────────────────────────────────────────────────────────────────────────────────────────────────┐
│ StepsizeCallback │
│ ════════════════ │
│ CFL number: ………………………………………………… 0.9 │
└──────────────────────────────────────────────────────────────────────────────────────────────────┘
To save the current solution in regular intervals we use the SaveSolutionCallback
. We would like to save the initial and final solutions as well. The data will be saved as HDF5 files located in the out
folder. Afterwards it is possible to visualize a solution from saved files using Trixi2Vtk.jl and ParaView, which is described below in the section Visualize the solution.
save_solution = SaveSolutionCallback(interval = 20,
save_initial_solution = true,
save_final_solution = true)
┌──────────────────────────────────────────────────────────────────────────────────────────────────┐
│ SaveSolutionCallback │
│ ════════════════════ │
│ interval: ……………………………………………………… 20 │
│ solution variables: …………………………… cons2prim │
│ save initial solution: …………………… yes │
│ save final solution: ………………………… yes │
│ output directory: ………………………………… /home/runner/work/Trixi.jl/Trixi.jl/docs/out │
└──────────────────────────────────────────────────────────────────────────────────────────────────┘
Alternatively, we have the option to print solution files at fixed time intervals.
save_solution = SaveSolutionCallback(dt = 0.1,
save_initial_solution = true,
save_final_solution = true)
Another useful callback is the SaveRestartCallback
. It saves information for restarting in regular intervals. We are interested in saving a restart file for the final solution as well. To perform a restart, you need to configure the restart setup in a special way, which is described in the section Restart simulation.
save_restart = SaveRestartCallback(interval = 100, save_final_restart = true)
┌──────────────────────────────────────────────────────────────────────────────────────────────────┐
│ SaveRestartCallback │
│ ═══════════════════ │
│ interval: ……………………………………………………… 100 │
│ save final solution: ………………………… yes │
│ output directory: ………………………………… /home/runner/work/Trixi.jl/Trixi.jl/docs/out │
└──────────────────────────────────────────────────────────────────────────────────────────────────┘
Create a CallbackSet
to collect all callbacks so that they can be passed to the solve
function.
callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback,
stepsize_callback,
save_solution, save_restart);
The last step is to choose the time integration method. OrdinaryDiffEq.jl defines a wide range of ODE solvers, including the three-stage, third-order strong stability preserving Runge-Kutta method SSPRK33
. We will pass the ODE problem, the ODE solver and the callbacks to the solve
function. Also, to use StepsizeCallback
, we must explicitly specify the initial trial time step dt
, the selected value is not important, because it will be overwritten by the StepsizeCallback
. And there is no need to save every step of the solution, as we are only interested the output provided by our callback SaveSolutionCallback
.
sol = solve(ode, SSPRK33(); dt = 1.0, save_everystep = false, callback = callbacks);
████████╗██████╗ ██╗██╗ ██╗██╗
╚══██╔══╝██╔══██╗██║╚██╗██╔╝██║
██║ ██████╔╝██║ ╚███╔╝ ██║
██║ ██╔══██╗██║ ██╔██╗ ██║
██║ ██║ ██║██║██╔╝ ██╗██║
╚═╝ ╚═╝ ╚═╝╚═╝╚═╝ ╚═╝╚═╝
┌──────────────────────────────────────────────────────────────────────────────────────────────────┐
│ SemidiscretizationHyperbolic │
│ ════════════════════════════ │
│ #spatial dimensions: ………………………… 2 │
│ mesh: ………………………………………………………………… TreeMesh{2, Trixi.SerialTree{2, Float64}} with length 341 │
│ equations: …………………………………………………… LinearScalarAdvectionEquation2D │
│ initial condition: ……………………………… initial_condition_sinpi │
│ boundary conditions: ………………………… Trixi.BoundaryConditionPeriodic │
│ source terms: …………………………………………… source_term_exp_sinpi │
│ solver: …………………………………………………………… DG │
│ total #DOFs per field: …………………… 4096 │
└──────────────────────────────────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────────────────────────────────┐
│ TreeMesh{2, Trixi.SerialTree{2, Float64}} │
│ ═════════════════════════════════════════ │
│ center: …………………………………………………………… [0.0, 0.0] │
│ length: …………………………………………………………… 2.0 │
│ periodicity: ……………………………………………… (true, true) │
│ current #cells: ……………………………………… 341 │
│ #leaf-cells: ……………………………………………… 256 │
│ maximum #cells: ……………………………………… 30000 │
└──────────────────────────────────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────────────────────────────────┐
│ LinearScalarAdvectionEquation2D │
│ ═══════════════════════════════ │
│ #variables: ………………………………………………… 1 │
│ │ variable 1: …………………………………………… scalar │
└──────────────────────────────────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────────────────────────────────┐
│ DG{Float64} │
│ ═══════════ │
│ basis: ……………………………………………………………… LobattoLegendreBasis{Float64}(polydeg=3) │
│ mortar: …………………………………………………………… LobattoLegendreMortarL2{Float64}(polydeg=3) │
│ surface integral: ………………………………… SurfaceIntegralWeakForm │
│ │ surface flux: ……………………………………… flux_central │
│ volume integral: …………………………………… VolumeIntegralWeakForm │
└──────────────────────────────────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────────────────────────────────┐
│ AnalysisCallback │
│ ════════════════ │
│ interval: ……………………………………………………… 20 │
│ analyzer: ……………………………………………………… LobattoLegendreAnalyzer{Float64}(polydeg=6) │
│ │ error 1: …………………………………………………… l2_error │
│ │ error 2: …………………………………………………… linf_error │
│ │ integral 1: …………………………………………… entropy_timederivative │
│ save analysis to file: …………………… no │
└──────────────────────────────────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────────────────────────────────┐
│ AliveCallback │
│ ═════════════ │
│ interval: ……………………………………………………… 10 │
└──────────────────────────────────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────────────────────────────────┐
│ StepsizeCallback │
│ ════════════════ │
│ CFL number: ………………………………………………… 0.9 │
└──────────────────────────────────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────────────────────────────────┐
│ SaveSolutionCallback │
│ ════════════════════ │
│ interval: ……………………………………………………… 20 │
│ solution variables: …………………………… cons2prim │
│ save initial solution: …………………… yes │
│ save final solution: ………………………… yes │
│ output directory: ………………………………… /home/runner/work/Trixi.jl/Trixi…build/tutorials/first_steps/out │
└──────────────────────────────────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────────────────────────────────┐
│ SaveRestartCallback │
│ ═══════════════════ │
│ interval: ……………………………………………………… 100 │
│ save final solution: ………………………… yes │
│ output directory: ………………………………… /home/runner/work/Trixi.jl/Trixi…build/tutorials/first_steps/out │
└──────────────────────────────────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Time integration │
│ ════════════════ │
│ Start time: ………………………………………………… 0.0 │
│ Final time: ………………………………………………… 1.0 │
│ time integrator: …………………………………… SSPRK33 │
│ adaptive: ……………………………………………………… false │
└──────────────────────────────────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Environment information │
│ ═══════════════════════ │
│ #threads: ……………………………………………………… 1 │
└──────────────────────────────────────────────────────────────────────────────────────────────────┘
────────────────────────────────────────────────────────────────────────────────────────────────────
Simulation running 'LinearScalarAdvectionEquation2D' with DGSEM(polydeg=3)
────────────────────────────────────────────────────────────────────────────────────────────────────
#timesteps: 0 run time: 8.61000000e-07 s
Δt: 1.00000000e+00 └── GC time: 0.00000000e+00 s (0.000%)
sim. time: 0.00000000e+00 (0.000%) time/DOF/rhs!: NaN s
PID: Inf s
#DOFs per field: 4096 alloc'd memory: 1108.101 MiB
#elements: 256
Variable: scalar
L2 error: 5.57371016e-06
Linf error: 2.37926999e-05
∑∂S/∂U ⋅ Uₜ : 4.42993996e-17
────────────────────────────────────────────────────────────────────────────────────────────────────
#timesteps: 10 │ Δt: 3.1250e-02 │ sim. time: 3.1250e-01 (31.250%) │ run time: 5.0000e-02 s
────────────────────────────────────────────────────────────────────────────────────────────────────
Simulation running 'LinearScalarAdvectionEquation2D' with DGSEM(polydeg=3)
────────────────────────────────────────────────────────────────────────────────────────────────────
#timesteps: 20 run time: 6.25269850e-02 s
Δt: 3.12500000e-02 └── GC time: 0.00000000e+00 s (0.000%)
sim. time: 6.25000000e-01 (62.500%) time/DOF/rhs!: 3.83705815e-08 s
PID: 2.48966345e-07 s
#DOFs per field: 4096 alloc'd memory: 1112.552 MiB
#elements: 256
Variable: scalar
L2 error: 6.70015343e-01
Linf error: 1.45520916e+00
∑∂S/∂U ⋅ Uₜ : -3.74390676e-02
────────────────────────────────────────────────────────────────────────────────────────────────────
#timesteps: 20 │ Δt: 3.1250e-02 │ sim. time: 6.2500e-01 (62.500%) │ run time: 5.6858e-02 s
#timesteps: 30 │ Δt: 3.1250e-02 │ sim. time: 9.3750e-01 (93.750%) │ run time: 6.2122e-02 s
────────────────────────────────────────────────────────────────────────────────────────────────────
Simulation running 'LinearScalarAdvectionEquation2D' with DGSEM(polydeg=3)
────────────────────────────────────────────────────────────────────────────────────────────────────
#timesteps: 32 run time: 7.08124000e-02 s
Δt: 3.12500000e-02 └── GC time: 0.00000000e+00 s (0.000%)
sim. time: 1.00000000e+00 (100.000%) time/DOF/rhs!: 3.78765836e-08 s
PID: 4.27940674e-08 s
#DOFs per field: 4096 alloc'd memory: 1116.793 MiB
#elements: 256
Variable: scalar
L2 error: 8.65828105e-01
Linf error: 1.75847551e+00
∑∂S/∂U ⋅ Uₜ : -1.70590047e-02
────────────────────────────────────────────────────────────────────────────────────────────────────
────────────────────────────────────────────────────────────────────────────────────────────────────
Trixi.jl simulation finished. Final time: 1.0 Time steps: 32 (accepted), 32 (total)
────────────────────────────────────────────────────────────────────────────────────────────────────
Finally, we print the timer summary.
summary_callback()
────────────────────────────────────────────────────────────────────────────────────
Trixi.jl Time Allocations
─────────────────────── ────────────────────────
Tot / % measured: 82.2ms / 28.0% 15.1MiB / 77.0%
Section ncalls time %tot avg alloc %tot avg
────────────────────────────────────────────────────────────────────────────────────
rhs! 96 14.9ms 64.8% 155μs 9.33KiB 0.1% 100B
source terms 96 9.05ms 39.3% 94.3μs 0.00B 0.0% 0.00B
volume integral 96 3.82ms 16.6% 39.8μs 0.00B 0.0% 0.00B
interface flux 96 1.13ms 4.9% 11.8μs 0.00B 0.0% 0.00B
surface integral 96 376μs 1.6% 3.91μs 0.00B 0.0% 0.00B
prolong2interfaces 96 317μs 1.4% 3.30μs 0.00B 0.0% 0.00B
~rhs!~ 96 99.2μs 0.4% 1.03μs 9.33KiB 0.1% 100B
Jacobian 96 52.7μs 0.2% 549ns 0.00B 0.0% 0.00B
reset ∂u/∂t 96 45.6μs 0.2% 475ns 0.00B 0.0% 0.00B
prolong2mortars 96 4.70μs 0.0% 49.0ns 0.00B 0.0% 0.00B
prolong2boundaries 96 4.58μs 0.0% 47.8ns 0.00B 0.0% 0.00B
mortar flux 96 4.18μs 0.0% 43.5ns 0.00B 0.0% 0.00B
boundary flux 96 2.91μs 0.0% 30.3ns 0.00B 0.0% 0.00B
analyze solution 3 5.47ms 23.8% 1.82ms 11.3MiB 96.8% 3.76MiB
I/O 6 2.62ms 11.4% 437μs 370KiB 3.1% 61.7KiB
save solution 3 1.47ms 6.4% 490μs 302KiB 2.5% 101KiB
~I/O~ 6 1.15ms 5.0% 192μs 68.3KiB 0.6% 11.4KiB
get element variables 3 411ns 0.0% 137ns 0.00B 0.0% 0.00B
save mesh 3 171ns 0.0% 57.0ns 0.00B 0.0% 0.00B
get node variables 3 100ns 0.0% 33.3ns 0.00B 0.0% 0.00B
calculate dt 33 4.30μs 0.0% 130ns 0.00B 0.0% 0.00B
────────────────────────────────────────────────────────────────────────────────────
Now you can plot the solution as shown below, analyze it and improve the stability, accuracy or efficiency of your setup.
Visualize the solution
In the previous part of the tutorial, we calculated the final solution of the given problem, now we want to visualize it. A more detailed explanation of visualization methods can be found in the section Visualization.
Using Plots.jl
The first option is to use the Plots.jl package directly after calculations, when the solution is saved in the sol
variable.
using Plots
As was shown in the Getting started section, you can plot all variables from the system of equations by executing the following.
plot(sol)
Alternatively, you can configure the plot more precisely. Trixi.jl provides a special data type, PlotData2D
, to extract the visualization data from the solution.
pd = PlotData2D(sol);
You can plot specific variables from the system of equations by referring to their names. To obtain the names of all variables, execute the following.
@show pd.variable_names;
pd.variable_names = ["scalar"]
Plot the variable named "scalar" (which is the name of the variable for the linear advection equation in Trixi.jl).
plot(pd["scalar"])
Mesh extraction is possible using the getmesh
function. Plots.jl has the plot!
function that allows you to modify an already built graph.
plot!(getmesh(pd))
Using Trixi2Vtk.jl
Another way to visualize a solution is to extract it from a saved HDF5 file. After we used the solve
function with SaveSolutionCallback
there is a file with the final solution. It is located in the out
folder and is named as follows: solution_index.h5
. The index
is the final time step of the solution that is padded to 6 digits with zeros from the beginning. With Trixi2Vtk you can convert the HDF5 output file generated by Trixi.jl into a VTK/VTU files. VTK/VTU are specialized formats designed to store structured data required for visualization purposes. This can be used in visualization tools such as ParaView or VisIt to plot the solution.
If you haven't added Trixi2Vtk.jl to your project yet, you can add it as follows.
import Pkg
Pkg.add(["Trixi2Vtk"])
Now we load the Trixi2Vtk.jl package and convert the file out/solution_000000032.h5
with the final solution using the trixi2vtk
function saving the resulting file in the out
folder.
using Trixi2Vtk
trixi2vtk(joinpath("out", "solution_000000032.h5"), output_directory = "out")
──────────────────────────────────────────────────────────────────────────────────────────
Time Allocations
─────────────────────── ────────────────────────
Tot / % measured: 1.54s / 68.2% 73.4MiB / 70.1%
Section ncalls time %tot avg alloc %tot avg
──────────────────────────────────────────────────────────────────────────────────────────
read mesh 1 354ms 33.8% 354ms 12.6MiB 24.5% 12.6MiB
interpolate data 1 319ms 30.4% 319ms 7.76MiB 15.1% 7.76MiB
add data to VTK file 1 285ms 27.2% 285ms 9.12MiB 17.7% 9.12MiB
scalar 1 119ms 11.4% 119ms 5.53MiB 10.7% 5.53MiB
add data to VTK file 1 511μs 0.0% 511μs 118KiB 0.2% 118KiB
cell_ids 1 269μs 0.0% 269μs 35.6KiB 0.1% 35.6KiB
element_ids 1 200μs 0.0% 200μs 37.8KiB 0.1% 37.8KiB
levels 1 31.4μs 0.0% 31.4μs 37.8KiB 0.1% 37.8KiB
build VTK grid (node data) 1 51.6ms 4.9% 51.6ms 15.6MiB 30.3% 15.6MiB
read data 1 29.2ms 2.8% 29.2ms 946KiB 1.8% 946KiB
prepare VTK cells (node data) 1 6.93ms 0.7% 6.93ms 5.04MiB 9.8% 5.04MiB
build VTK grid (cell data) 1 922μs 0.1% 922μs 353KiB 0.7% 353KiB
save VTK file 2 548μs 0.1% 274μs 4.91KiB 0.0% 2.45KiB
prepare VTK cells (cell data) 1 98.4μs 0.0% 98.4μs 100KiB 0.2% 100KiB
──────────────────────────────────────────────────────────────────────────────────────────
Now two files solution_000000032.vtu
and solution_000000032_celldata.vtu
have been generated in the out
folder. The first one contains all the information for visualizing the solution, the second one contains all the cell-based or discretization-based information.
Now let's visualize the solution from the generated files in ParaView. Follow this short instruction to get the visualization.
- Download, install and open ParaView.
- Press
Ctrl+O
and select the generated filessolution_000000032.vtu
andsolution_000000032_celldata.vtu
from theout
folder. - In the upper-left corner in the Pipeline Browser window, left-click on the eye-icon near
solution_000000032.vtu
. - In the lower-left corner in the Properties window, change the Coloring from Solid Color to scalar. This already generates the visualization of the final solution.
- Now let's add the mesh to the visualization. In the upper-left corner in the Pipeline Browser window, left-click on the eye-icon near
solution_000000032_celldata.vtu
. - In the lower-left corner in the Properties window, change the Representation from Surface to Wireframe. Then a white grid should appear on the visualization.
Now, if you followed the instructions exactly, you should get a similar image as shown in the section Using Plots.jl:
After completing this tutorial you are able to set up your own simulations with Trixi.jl. If you have an interest in contributing to Trixi.jl as a developer, refer to the third part of the introduction titled Changing Trixi.jl itself.
This page was generated using Literate.jl.