Your first simulation

PyLogGrid is designed with ease-of-use in mind, so that users spend less time coding and more time thinking about the physics.

Unlike other big frameworks, users don’t provide a “simulation file” composed only of parameters, but instead provide a python script, which offers much more flexibility.


What we’re simulating

Log-lattices operate in an exponentially decimated grid in Fourier space. By design, the current version is therefore limited to periodic boundary/initial conditions, although there are ongoing efforts to alleviate this constraint.

Due to their sparsity, Log-lattices can simulate very wide range of scales, spanning lots of orders of magnitude, down to the Kolmogorov scale. This enables us to perform geophysical simulations without parameterizing the dissipation.

Log-lattices are intrinsically limited to equations that write (in Fourier space) as linear terms and convolutions (i.e. it can’t handle third order nonlinear terms). Fortunately, that already covers a very vast array of equations. Pyloggrid is a general-purpose academic equation solver, not an industry CFD solver like OpenFOAM.

In the example below, we simulate 3D Navier-Stokes with given initial conditions and forcing.


To get started, let’s look at the examples in the Simulations folder of the source (available on Github ). Copy it and navigate inside:

cd Simulations

In this folder, you will find a simple script called NS3D.py which simulates the 3D Navier-Stokes equations.

Let’s look at what’s inside.

Simulation functions

At the top of the NS3D.py file, you will find several functions that define the equation being simulated:

  • get_forcing returns the forcing field for a given simulation grid. This function is optional, but it’s good practice to define the forcing field separately from the equation itself.

  • equation_nonlinear returns the nonlinear part of the equation. If your equation is \(\partial_t u=A(u)−b\cdot u\), then this corresponds to \(A(u)\).

  • equation_linear returns the linear factor of the equation \(−b\).

  • initial_conditions defines the initial conditions for the simulation.

  • update_gridsize specifies when to update the size of the simulation grid.

For an in-depth explanation on how to use those, read pyloggrid.LogGrid.Framework.Solver.

Parameters

After defining the simulation functions, we set the parameters for the simulation. First, we define the physical parameters of the equation:

fields = ["ux", "uy", "uz"]  # the scalar fields to simulate
D = 3  # the dimension of the space
l_params = {"plastic": False, "a": 1, "b": 2}  # the grid spacing's parameters
Re_F = 1e3
simu_params = {"Re_F": Re_F}  # scalar parameter of the simulation, passed to the equation

Next, we define the numerical parameters:

rtol = 1e-4  # relative tolerance of the solver
n_threads_convolution = 4  # parallelization
N_points = 6  # initial size of the grid

Finally, we define the output parameters:

save_path = f"results/save_3D_f0{f0:.2e}_ReF{Re_F:.2e}"  # save path
end_simulation = {"t": 2000, "ode_step": 1e10}  # when to end the simulation
save_one_in = 50  # save one step every N real steps

Running the simulation

The simulation can be run as-is, but since we set end_simulation = {"t": 2000, "ode_step": 1e10}} it will run for a very long time [1].

If you want to end the simulation after a specified amount of time, you can change this parameter to:

end_simulation = {"elapsed_time": 120}

This will automatically end the simulation after 120 seconds (i.e., 2 minutes).

To run the simulation, simply execute the NS3D.py script:

python NS3D.py

Once the simulation is complete, you can analyze the output data.

Tips

  • By default, numpy is multithreaded. If you want to disable this feature (typically because you want control over how many CPUs your simulation take, and because numpy operations take up a negligible time in the simulation), simply import it as from Libs.singlethread_numpy import np at the top of your simulation file (before it’s imported by any other library).

  • If your simulation includes a forcing term, it’s a good practice to put it in a separate function, as done in the example. That way, you can reuse it in the initialization or in the treatment.

  • There are a number of functions in pyloggrid.Libs.datasci to help manipulating arrays, in particular complex random arrays, which is very useful for creating peusorandom forcings consistent across grid size changes.