.. _pyfun-ex02-bullet:

Demo 2: Inviscid Bullet with Reports
====================================

This is a second CAPE/pyfun demo using a bullet shape and demonstrating how to
use the inviscid solver in FUN3D.

To get started, clone the repo and run a few short commands:

    .. code-block:: console

        $ git clone https://github.com/nasa-ddalle/pyfun02-bullet.git
        $ cd pyfun02-bullet
        $ ./copy-files.py
        $ cd work/

This will copy all of the files into a newly created ``work/`` folder. Follow
the instructions below by entering that ``work/`` folder; the purpose is that
you can easily delete the ``work/`` folder and restart the tutorial at any
time.

To keep computation time low for the purposes of running examples. The example
seeks to introduce the pyFun user to data books and automated reports,
including sweep plots. It should be noted that the :ref:`Cart3D examples
<pycart-examples>` are more descriptive, and users are encouraged to consider
reading those examples since most of the process is the same for each solver.

The geometry used for this shape is a capped cylinder and little or nothing
else. An inviscid volume mesh was created using AFLR3. The surface
triangulation, ``bullet.tri``, is shown below.

    .. figure:: bullet01.png
        :width: 4in
        
        Simple bullet shape triangulation with four fins
        
The files in this folder are listed below with a short description.  In this
case, the run matrix is defined within the ``pyFun.json`` file.

    * ``pyFun.json``: Master input control file for pyFun
    * ``matrix.csv``: Run matrix
    * ``fun3d.nml``: Template namelist file
    * ``bullet-inviscid.ugrid``: Volume grid, ASCII AFLR3 format
    * ``bullet-inviscid.mapbc``: Boundary conditions file
    * ``bullet-far.tri``: (Not used) Cart3D surface triangulation
    * ``bullet.xml``: XML files used to name each numbered component
    * ``slice-y0.py``: Python script for use with Paraview
    

.. _pyfun-ex02-run:
    
Running Cases
-------------
Assuming the present working directory is in this demo folder, i.e.
``pyfun02_bullet``, a good first test command is the following, which checks
the status of each case in the matrix.

    .. code-block:: console
    
        $ pyfun -c
        Case Config/Run Directory  Status  Iterations  Que CPU Time 
        ---- --------------------- ------- ----------- --- --------
        0    bullet/m0.80a0.0b0.0  ---     /           .            
        1    bullet/m0.80a4.0b0.0  ---     /           .     
        ...
        23   bullet/m1.75a30.0b0.0 ---     /           .            
        
        ---=24, 

This example contains 24 cases in the run matrix, and the computation time is
kept low in order to run each case within a few minutes (using the serial
version of FUN3D).

Running case number 3 (note zero-based indexing) has the following output.

    .. code-block:: console
    
        $ pyfun -I 3
        Case Config/Run Directory  Status  Iterations  Que CPU Time 
        ---- --------------------- ------- ----------- --- --------
        3    bullet/m0.80a30.0b0.0 ---     /           .            
          Case name: 'bullet/m0.80a30.0b0.0' (index 3)
             Starting case 'bullet/m0.80a30.0b0.0'
         > nodet --animation_freq 100
             (PWD = 'bullet/m0.80a30.0b0.0')
             (STDOUT = 'fun3d.out')
         > nodet --animation_freq 100
             (PWD = 'bullet/m0.80a30.0b0.0')
             (STDOUT = 'fun3d.out')
        
        Submitted or ran 1 job(s).
        
        ---=1,

We can then check how much CPU time that used.

    .. code-block:: console
    
        $ pyfun -I 3 -c
        Case Config/Run Directory  Status  Iterations  Que CPU Time 
        ---- --------------------- ------- ----------- --- --------
        3    bullet/m0.80a30.0b0.0 DONE    200/200     .        0.1 
        
        DONE=1, 
        
In the master input file ``pyFun.json``, the key section is the ``"Fun3D"``
section, which modifies the template namelist ``fun3d.nml``.  The example
is set up to run two phases.  The first phase has a starting CFL number of 0.1
which ramps up to 100.0.  The second phase has a constant CFL number of 100.0.

    .. code-block:: javascript

        "Fun3D": {
            "nonlinear_solver_parameters": {
                "schedule_cfl": [[0.1, 100.0], [100.0, 100.0]],
                "schedule_iteration": [[1, 100], [1, 50]]
            },
            "global": {
                "volume_animation_freq": -1
            },
            "code_run_control": {
                "restart_read": ["off", "on"]
            },
            "inviscid_flux_method": {
                "first_order_iterations": [50, 0],
                "flux_construction": "roe",
                "flux_construction_lhs": "vanleer",
                "flux_limiter": "hvanalbada",
                "freeze_limiter_iteration": [150, 0]
            },
            "special_parameters": {
                "large_angle_fix": "on"
            },
            "boundary_output_variables": {
                "boundary_list": "7-9",
                "cp": true,
                "ptot": true
            }
        }

Another interesting parameter is the *Config>File*, which is set to
``"bullet.xml"``.  This is an XML file that prescribes a name for each
component and furthermore can be used to define groups of components.  While
this is not a recognized FUN3D file format, it is used by pyFun to make some of
the setup easier.  Some of the text from the XML file are shown below.

    .. code-block:: xml
    
        <?xml version="1.0" encoding="ISO-8859-1"?>

        <Configuration Name="arrow sample" Source="arrow-far.tri">
        
        <!-- triangulated components -->
         <Component Name="cap" Parent="bullet_no_base" Type="tri">
          <Data> Face Label=1 </Data>
         </Component>
         
         <Component Name="body" Parent="bullet_no_base" Type="tri">
          <Data> Face Label=2 </Data>
         </Component>
         
         <Component Name="base" Parent="bullet_total" Type="tri">
          <Data> Face Label=3 </Data>
         </Component>
         
        <!-- Containers -->
         <Component Name="fins" Type="container" Parent="bullet_no_base">
         </Component>
         <Component Name="bullet_no_base" Type="container" Parent="bullet_total">
         </Component>
         <Component Name="bullet_total"   Type="container">
         </Component>
        
        </Configuration>

In particular, this allows pyFun to set the correct namelist parameters to
track the forces and moments on each component.  This is important because
FUN3D internally renumbers all the components 1,2,...,*N* according to the
lines of the ``.mapbc`` file.  The present setup in the *Config* section of
``pyFun.json`` prevents the need to figure out the component number(s) for
each component.

Before moving on to the next session, let's also run case 17 so we can complete
the rest of the tutorial.  Some of the aerodynamic data book is already in
place, but cases 3 and 17 are missing.  Users may wish to run all 24 cases or
just a few more in order to do more experimenting.


.. _pycart-ex02-report:

Automated Single-Case Report
----------------------------
This example is set up to create a report called ``report-case.pdf`` in the
``report/`` folder.  It includes a couple of summary tables, 8 iterative
history plots, and a flow visualization slide that works with Paraview.
:numref:`fig-pyfun-ex02-slice-y0` shows an example of this Paraview image from
case 17 (``bullet/m1.50a4.0b0.0``).

    .. _fig-pyfun-ex02-slice-y0:
    .. figure:: m1.50a4.0b0.0/slice-y0.png
        :width: 4.0 in
        
        Surface :math:`c_p` and :math:`y{=}0` Mach slice

**Note about Paraview figure**: This example requires
`ParaView with VisIt Bridge <www.paraview.org/Wiki/VisIt_Database_Bridge>`_
since it reads binary Tecplot (``.plt``) files.  Installation can be tricky,
and prepackaged ParaView modules often do not have the VisIt bridge.  One
relatively easy workaround is to install the free and open-source software
`SALOME <http://www.salome-platform.org/downloads/current-version>`_, which
does include the appropriate version.  It is fairly simple to download a
version of SALOME and then use the included ParaView binaries within that
installation.

The report also includes axial force coefficient (*CA*), side force coefficient
(*CY*), and normal force (*CY*) coefficient on both ``bullet_no_base`` and
``cap``.  The ``bullet_no_base`` component includes bot the rounded nose
``cap`` and the cylindrical portion.  :numref:`fig-pyfun-ex02-bullet-CN` shows
one of these plots.

    .. _fig-pyfun-ex02-bullet-CN:
    .. figure:: m1.50a4.0b0.0/bullet_CN.*
        :width: 3.2 in
        
        Iterative history on bullet (not including base) normal force
        coefficient (*CN*) for ``bullet/m1.50a4.0b0.0``

In addition, there is a plot of overall pitching moment coefficient, and a
residual plot.  Both :numref:`fig-pyfun-ex02-bullet-CN` and
:numref:`fig-pyfun-ex02-L2` show a big change of behavior at iteration 50, when
the first-order iterations end.  The residual history also shows a change of
behavior at iteration 75; the residual stops dropping for a while while the
fluxes are frozen.

    .. _fig-pyfun-ex02-L2:
    .. figure:: m1.50a4.0b0.0/L2.*
        :width: 3.2 in
        
        Overall :math:`L_2` residual for ``bullet/m1.50a4.0b0.0``

The Paraview subfigure settings from the JSON file are shown below.

    .. code-block:: javascript
    
        "slice-y0": {
            "Type": "Paraview",
            "Caption": "Surface $c_p$ and $y{=}0$ Mach slice",
            "Width": 0.33,
            "Layout": "slice-y0.py",
            "ImageFile": "slice-y0.png"
        }

This points pyFun to the Python script ``slice-y0.py``.  The image is
created by the system command ``pvpython slice-y0.py`` in each case folder.
This :download:`slice-y0.py` was created by recording a Python script in
ParaView interactively and then modifying the resulting script later.  At the
time of writing, this is found in the *Tools* menu under *Tools>Start Trace*.

The header of this script contains some helper functions that were added in
order to provide a solution for users who do not have a version of FUN3D
compiled with the TecIO library.  It does require the user to use Tecplot's
``preplot`` tool, which can be downloaded from the `Tecplot TecIO library
website <http://www.tecplot.com/downloads/tecio-library/>`_.  The first few
lines of :download:`slice-y0.py` are shown below.

    .. code-block:: python
    
        #### import the simple module from the paraview
        from paraview.simple import *
        #### disable automatic camera reset on 'Show'
        paraview.simple._DisableFirstRenderCameraReset()
        
        # System interface
        import os
        # Check for DAT instead of PLT file
        for f in ['arrow_tec_boundary', 'arrow_plane-y0']:
            # Name of DAT and PLT files
            fdat = '%s.dat' % f
            fplt = '%s.plt' % f
            # Check for DAT file
            if os.path.isfile(fdat):
                # Delete any PLT file
                if os.path.isfile(fplt): os.remove(fplt)
                # Create new PLT file
                os.system('preplot %s %s' % (fdat, fplt))

Most of the rest of the contents of the Python script come from the `ParaView
API <http://www.paraview.org/ParaView/Doc/Nightly/www/py-doc/>`_, but the
command at the end is relevant.

    .. code-block:: python
        
        # save screenshot
        SaveScreenshot('slice-y0.png', 
            magnification=1, quality=100, view=renderView1)
            
This is the command that actually saves the image, and it is relevant to
explain here that the name of the image, ``'slice-y0.png'``, must line up with
the *ImageFile* option from the JSON subfigure definition.


.. _pycart-ex02-databook:

Aerodynamic Data Book and Sweep Plots
-------------------------------------
The provided example in ``$PYCART/examples/pyfun/02_bullet/`` includes an
aerodynamic database for all but two of the 24 conditions in the
``data/bullet`` folder.  The contents of an aero data book file are the same
here as for Cart3D, and a selection of text from the main ``bullet_no_base``
file can be seen below.  These aero data book files have the file name
``aero_$COMP.csv`` for an arbitrary component *COMP*.

    .. code-block:: none
        
        # Database statistics for 'bullet_no_base' extracted on 2017-04-09 19:35:55 
        #
        #mach,alpha,beta,q,T,config,Label,CA,CY,CN,...,nOrders,nIter,nStats
        0.8,0,0,1250,475.33,bullet,,0.1293,-0.0036,-0.0001,...,6.7889,200,50
        0.8,4,0,1250,475.33,bullet,,0.1260,-0.0046,0.1854,...,6.8890,200,50
        ...
        1.75,30,0,1250,475.33,bullet,,0.6291,-0.0010,2.8408,...,4.5099,200,50

This is a relatively simple data book definition, as shown in the *DataBook*
section of ``pyFun.json``, reproduced below.  We include five data book
components here, and all are restricted to be just forces to make some of the
files smaller.  Normally, a user would not include the lines such as ``"cap":
{"Type": "Force"}``.  Without a user-specified type, components have the type
``"FM"``, which stand for "Force & Moment" (except for Cart3D data books, which
are by default ``"Force"``).  The *DataBook>nStats* component means that at
least 50 iterations must be included in the averaging window for each
coefficient of each component, and *nMin* states that only iterations after
iteration 150 are allowed to be included.

    .. code-block:: javascript
    
        "DataBook": {
            // List of components
            "Components": [
                "bullet_no_base", "bullet_total",
                "cap", "body", "base"
            ],
            // Location
            "Folder": "data/bullet",
            // Overall statistic inputs
            "nStats": 50,
            "nMin": 150,
            // Definitions
            "bullet_no_base": {"Type": "Force"},
            "bullet_total": {"Type": "Force"},
            "cap": {"Type": "Force"},
            "body": {"Type": "Force"},
            "base": {"Type": "Force"}
        }
        

Running the command ``pyfun --aero`` will fill in the other two cases.

    .. code-block:: console
    
        $ pyfun -I :3 --aero
        bullet/m0.80a0.0b0.0
        bullet/m0.80a4.0b0.0
        bullet/m0.80a10.0b0.0
        bullet/m0.80a30.0b0.0
          Adding new databook entry at iteration 200.
        bullet/m0.95a0.0b0.0
        bullet/m0.95a4.0b0.0
        bullet/m0.95a10.0b0.0
        bullet/m0.95a30.0b0.0
        bullet/m1.10a0.0b0.0
        bullet/m1.10a4.0b0.0
        bullet/m1.10a10.0b0.0
        bullet/m1.10a30.0b0.0
        bullet/m1.25a0.0b0.0
        bullet/m1.25a4.0b0.0
        bullet/m1.25a10.0b0.0
        bullet/m1.25a30.0b0.0
        bullet/m1.50a0.0b0.0
        bullet/m1.50a4.0b0.0
          Adding new databook entry at iteration 200.
        bullet/m1.50a10.0b0.0
        bullet/m1.50a30.0b0.0
        bullet/m1.75a0.0b0.0
        bullet/m1.75a4.0b0.0
        bullet/m1.75a10.0b0.0
        bullet/m1.75a30.0b0.0

The ``pyFun.json`` ``"Report"`` section also includes a Mach sweep figure.
Details of the Mach sweep (with an angle of attack carpet plot) are the same as
in the Cart3D example :ref:`pycart-ex-data-arrow`, but
:numref:`fig-pyfun-ex02-mach-cap-CN` gives an example of one of the plots from
the resulting ``report-mach.pdf``.

    .. _fig-pyfun-ex02-mach-cap-CN:
    .. figure:: b0/mach_cap_CN.*
        :width: 3.5 in
        
        Mach sweep of *CN* on ``cap`` for various angles of attacks.

To generate this report, issue the following command:

    .. code-block:: console
    
        $ pyfun --report mach
        mach/bullet/m0.80a0.0b0.0
          SweepConds: New subfig
          SweepList: New subfig
          mach_bullet_CA: New subfig
          mach_bullet_CY: New subfig
          mach_bullet_CN: New subfig
          mach_total_CA: New subfig
          mach_total_CY: New subfig
          mach_total_CN: New subfig
          mach_cap_CA: New subfig
          mach_cap_CY: New subfig
          mach_cap_CN: New subfig
        Compiling...
        Compiling...
        Cleaning up...

Actually, :numref:`fig-pyfun-ex02-mach-cap-CN` is missing two data points (one
of these is obvious while the other is somewhat hidden).  If the user has run
the suggested ``pyfun --aero`` command from earlier, the resulting plots will
include these two missing points.