{ "cells": [ { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from __future__ import print_function\n", "\n", "%matplotlib inline" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Tutorial: understand how FluidSim works" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A goal of FluidSim is to be as simple as possible to allow anyone knowing a little bit of Python to understand how it works internally. For this tutorial, it is assumed that the reader knows how to run simulations with FluidSim. If it is not the case, first read the tutorial [running a simulation (user perspective)](tuto_user.ipynb)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## A class to organize parameters" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "First, we need to present the important class [fluiddyn.util.paramcontainer.ParamContainer] used to contain information.\n", "\n", "[fluiddyn.util.paramcontainer.ParamContainer]: http://fluiddyn.readthedocs.org/en/latest/generated/fluiddyn.util.paramcontainer.html\n", "\n", "[ParamContainer]: http://fluiddyn.readthedocs.org/en/latest/generated/fluiddyn.util.paramcontainer.html\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from fluiddyn.util.paramcontainer import ParamContainer\n", "\n", "params = ParamContainer(tag=\"params\")\n", "params._set_attribs({\"a0\": 1, \"a1\": 1})\n", "params._set_attrib(\"a2\", 1)\n", "params._set_child(\"child0\", {\"a0\": 1})\n", "params.a2 = 2\n", "params.child0.a0 = \"other option\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A `ParamContainer` can be represented as xml" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "params" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "FluidSim uses instances of this class to store the information of a particular solver and the parameters of a particular simulation." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## The Simul classes and the default parameters" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The first step to run a simulation is to import a Simul class from a solver module, for example:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from fluidsim.solvers.ns2d.solver import Simul" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Any solver module has to define a class called Simul which has to have some important attributes:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "[name for name in dir(Simul) if not name.startswith(\"__\")]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The first attribute `InfoSolver` is a class deriving from {class}`fluidsim.base.solvers.info_base.InfoSolverBase` (which is a [ParamContainer]). This class is usually defined in the `solver` module. It is used during the instantiation of the Simul object to produce a [ParamContainer] containing a description of the solver, in practice the names and the modules of the classes used for the different tasks that need to be performed during the simulation.\n", "\n", "There are also four other functions. `compute_freq_diss` and `tendencies_nonlin` are used during the simulation and describe the equations that are solved.\n", "\n", "`create_default_params` and `_complete_params_with_default` are used to produce the `ParamContainer` containing the default parameters for a simulation:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "params = Simul.create_default_params()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "During the creation of `params`, the class `InfoSolver` has been used to create a `ParamContainer` named `info_solver`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "Simul.info_solver" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We see that this solver uses many classes and that they are organized in tasks (\"Operator\", \"InitFields\", \"TimeStepping\", \"State\", \"Output\", \"Forcing\"). Some first-level classes (for example \"Output\") have second-level classes (\"PrintStdOut\", \"Spectra\", \"PhysFields\", etc.). Such description of a solver is very general. It is also very conveniant to create a new solver from a similar existing solver.\n", "\n", "Every classes can have a class function or a static function `_complete_params_with_default` that is called when the object containing the default parameters is created.\n", "\n", "The objects `params` and `Simul.info_solver` are then used to instantiate the simulation (here with the default parameters for the solver):" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sim = Simul(params)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's print the attributes of `sim` that are not class attributes:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "[name for name in dir(sim) if not name.startswith(\"_\") and name not in dir(Simul)]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Except `name_run` and `info`, the attributes are instances of the first-level classes defined in `Simul.info_solver`. These different objects have to interact together. We are going to present these different hierarchies of classes but first we come back to the two functions describing the equations in a pseudo-spectral solver." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Description of the solved equations" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The functions `Simul.compute_freq_diss` and `Simul.tendencies_nonlin` define the solved equations. Looking at the documentation of the solver module {mod}`fluidsim.solvers.ns2d.solver`, we see that `Simul.tendencies_nonlin` is defined in this module and that `Simul.compute_freq_diss` is inherited from the base class {class}`fluidsim.base.solvers.pseudo_spect.SimulBasePseudoSpectral`. By clicking on these links, you can look at the documentation and the sources of these functions. The documentation explains how this function define the solved equations. I think the sources are quite clear and can be understood by anyone knowing a little bit of Python for science. Most of the objects involved in these functions are functions or [numpy.ndarray](http://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.html)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## State classes (`sim.state`)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`sim.state` is an instance of {class}`fluidsim.solvers.ns2d.state.StateNS2D`. It contains `numpy.ndarray`, actually slightly modified `numpy.ndarray` named {class}`fluidsim.base.setofvariables.SetOfVariables`. This class is used to stack variables together in a single `numpy.ndarray`.\n", "\n", "The state classes are also able to compute other variables from the state of the simulation. It is an interface hiding the actual way the data are stored." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Operator classes (`sim.oper`)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`sim.oper` is an instance of {class}`fluidsim.operators.operators2d.OperatorsPseudoSpectral2D`.\n", "\n", "It contains the information on the grids (in physical and spectral space) and provides many optimized functions on arrays representing fields on these grids.\n", "\n", "It has to be fast! For the two dimensional Fourier pseudo-spectral solvers, it is written in Cython." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## TimeStepping classes (`sim.time_stepping`)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`sim.time_stepping` is an instance of {class}`fluidsim.base.time_stepping.pseudo_spect.TimeSteppingPseudoSpectral`, which is based on {class}`fluidsim.base.time_stepping.base.TimeSteppingBase`.\n", "\n", "This class contains the functions for the time advancement, i.e. Runge-Kutta functions and the actual loop than increments the time stepping index `sim.time_stepping.it`. The Runge-Kutta functions call the function `sim.tendencies_nonlin` and modify the state in Fourier space `sim.state.state_fft`.\n", "\n", "The loop function also call the function `sim.output.one_time_step`." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Output classes (`sim.output`)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`sim.output` is an instance of {class}`fluidsim.solvers.ns2d.output.Output`.\n", "\n", "Saving and plotting of online or on-the-fly postprocessed data - i.e., data generated by processing the solver state variables at regular intervals during simulation time. It could include physical fields, spatially averaged means, spectral energy budgets, PDFs etc." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Forcing classes (`sim.forcing`)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`sim.forcing` is an instance of {class}`fluidsim.solvers.ns2d.forcing.ForcingNS2D`.\n", "\n", "If `params.forcing.enable` is True, it is used in `sim.tendencies_nonlin` to add the forcing term." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.10.7" } }, "nbformat": 4, "nbformat_minor": 4 }