
.. DO NOT EDIT.
.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY.
.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE:
.. "auto_examples/nonhydro/convection_and_closures.py"
.. LINE NUMBERS ARE GIVEN BELOW.

.. only:: html

    .. note::
        :class: sphx-glr-download-link-note

        :ref:`Go to the end <sphx_glr_download_auto_examples_nonhydro_convection_and_closures.py>`
        to download the full example code.

.. rst-class:: sphx-glr-example-title

.. _sphx_glr_auto_examples_nonhydro_convection_and_closures.py:


Convection and Closures
=======================

Testing different momentum closures in a 2D convection problem.

In this example, I test different closures for the momentum equations. The
model setup is a 2D stratified fluid with a buoyancy perturbation in the middle
of the domain. Thies buoyancy perturbation leads to static instability and
convection. When no closure is used, a lot of energy is accumulated at the grid
scale, leading to a very noisy solution:

without any closure
-------------------
.. video:: videos/convection_and_closures_no_closure.mp4

The most straightforward closure is by including harmonic diffusion with 
turbulent viscosities and diffusivities. The following video shows the solution
when using :py:class:`HarmonicDiffusion <fridom.framework.modules.closures.HarmonicDiffusion>`
with a coefficient of :math:`10^{-4}`:

harmonic diffusion
------------------
.. video:: videos/convection_and_closures_harmonic_diffusion.mp4

The solution is much smoother, but there is also an unrealistic amount of energy
dissipation. To minimize the effect of the closure on the larger scales, one can
use a biharmonic diffusion closure instead of a harmonic one. The following video
shows the solution when using 
:py:class:`BiharmonicDiffusion <fridom.framework.modules.closures.BiharmonicDiffusion>`
with a coefficient of :math:`10^{-6}`:

biharmonic diffusion
--------------------
.. video:: videos/convection_and_closures_biharmonic_diffusion.mp4

Finally, there is also the Smagorinsky-Lilly closure implemented in the model,
this is a dynamic closure that adjusts the turbulent viscosities and diffusivities
based on the resolved scales. The following video shows the solution when using
:py:class:`SmagorinskyLilly <fridom.nonhydro.closures.SmagorinskyLilly>`:

smagorinsky-lilly
-----------------
.. video:: videos/convection_and_closures_Smagorinsky-Lilly.mp4

.. note::
    The diffusion coefficients are not fine-tuned. The purpose of this example
    is to show how different closures can be used in the model and how they
    affect the solution.

.. GENERATED FROM PYTHON SOURCE LINES 51-152







.. code-block:: Python


    import fridom.nonhydro as nh
    import fridom.framework as fr
    import numpy as np

    # ----------------------------------------------------------------
    #  Settings
    # ----------------------------------------------------------------
    make_video  = True
    fps         = 30
    make_netcdf = False
    exp_name    = "convection_and_closures"
    run_length  = np.timedelta64(1, 'h')
    thumbnail   = f"figures/{exp_name}.png"

    class Plotter(nh.modules.animation.ModelPlotter):
        def create_figure():
            import matplotlib.pyplot as plt
            return plt.figure(figsize=(6, 4.5), dpi=32*12, tight_layout=True)

        def prepare_arguments(mz: nh.ModelState) -> dict:
            return {"b": mz.z.b.xr, "t": mz.clock.time}

        def update_figure(fig, b, t) -> None:
            import cmocean
            ax = fig.add_subplot(111)
            b.plot(ax=ax, cmap=cmocean.cm.dense_r, extend='both', vmax=3e-4, vmin=-3e-4)
            ax.set_aspect('equal')
            ax.set_title(f"t = {nh.utils.humanize_number(t, 'seconds')}", fontsize=16)

    def test_closure(closure: fr.modules.Module):

        grid = nh.grid.cartesian.Grid(
            N=(512, 1, 512), L=(100, 1, 100), periodic_bounds=(True, True, False))
        mset = nh.ModelSettings(grid=grid, f0=0, N2=2.5e-5)
        mset.time_stepper.dt = np.timedelta64(1, 's')

        fname = exp_name + "_" + closure.name

        # add a video writer
        if make_video:
            mset.diagnostics.add_module(nh.modules.animation.VideoWriter(
                Plotter,
                model_time_per_second=np.timedelta64(4, "m"),
                filename=f"{fname}", fps=fps))

        # create a NetCDF writer to save the output
        if make_netcdf:
            mset.diagnostics.add_module(nh.modules.NetCDFWriter(
                write_trigger = nh.ClockTrigger(time_interval=np.timedelta64(10, "m")),
                filename=fname))

        # add the closure
        mset.tendencies.add_module(closure)

        # setup all the modules
        mset.setup()

        # create an initial condition
        ncp = nh.config.ncp  # the array backend (numpy, cupy, ...)
        X, Y, Z = grid.X
        z = nh.State(mset)
        z.b.arr = 1e-3 * ncp.exp(-((X-50)**2 + (Z-50)**2)/(10)**2)

        # create the model, set initial conditions, and run
        model = nh.Model(mset)
        model.z = z
        model.run(runlen=run_length)

        # save thumbnail from biharmonic diffusion
        if closure.name == "biharmonic_diffusion":
            import os
            os.makedirs("figures", exist_ok=True)
            fig = Plotter(model.model_state)
            fig.savefig(thumbnail)

    @fr.utils.skip_on_doc_build
    def main():
        # test without any closure
        dummy_closure = fr.modules.Module()
        dummy_closure.name = "no_closure"
        test_closure(dummy_closure)

        # test with smagorinsky-lilly closure
        test_closure(nh.modules.closures.SmagorinskyLilly())

        # test with harmonic diffusion
        coeff = 1e-4
        friction = nh.modules.closures.HarmonicFriction(ah=coeff, av=coeff)
        mixing = nh.modules.closures.HarmonicMixing(kh=coeff, kv=coeff)
        test_closure(fr.modules.ModuleContainer("harmonic_diffusion", [friction, mixing]))

        # test with biharmonic diffusion
        coeff = 1e-6
        friction = nh.modules.closures.BiharmonicFriction(ah=coeff, av=coeff)
        mixing = nh.modules.closures.BiharmonicMixing(kh=coeff, kv=coeff)
        test_closure(fr.modules.ModuleContainer("biharmonic_diffusion", [friction, mixing]))


    if __name__ == "__main__":
        main()


.. rst-class:: sphx-glr-timing

   **Total running time of the script:** (0 minutes 1.062 seconds)


.. _sphx_glr_download_auto_examples_nonhydro_convection_and_closures.py:

.. only:: html

  .. container:: sphx-glr-footer sphx-glr-footer-example

    .. container:: sphx-glr-download sphx-glr-download-python

      :download:`Download Python source code: convection_and_closures.py <convection_and_closures.py>`

    .. container:: sphx-glr-download sphx-glr-download-zip

      :download:`Download zipped: convection_and_closures.zip <convection_and_closures.zip>`
