{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# A MINI-WEATHER APPLICATION\n",
    "\n",
    "In this lab we will accelerate a Fluid Simulation in the context of atmosphere and weather simulation.\n",
    "The mini weather code mimics the basic dynamics seen in the atmspheric weather and climate.\n",
    "\n",
    "The figure below demonstrates how a narrow jet of fast and slightly cold wind is injected into a balanced, neutral atmosphere at rest from the left domain near the model.\n",
    "\n",
    "<img src=\"images/Time.jpg\" width=\"80%\" height=\"80%\">\n",
    "\n",
    "Simulation is a repetitive process from 0 to the desired simulated time, increasing by Δt on every iteration.\n",
    "Each Δt step is practically the same operation. Each simulation is solving a differential equation that represents how the flow of the atmosphere (fluid) changes according to small perturbations. To simplify this solution the code uses dimensional splitting: Each dimension X and Z are treated independently.\n",
    "\n",
    "<img src=\"images/X_Y.jpg\" width=\"80%\" height=\"80%\">\n",
    "\n",
    "The differential equation has a time derivative that needs integrating, and a simple low-storage Runge-Kutta ODE solver is used to integrate the time derivative. Each time step, the order in which the dimentions are solved is reversed, giving second-order accuracy. \n",
    "\n",
    "<img src=\"images/Range-Kutta.jpg\" width=\"70%\" height=\"70%\">\n",
    "\n",
    "### The objective of this exercise is not to dwell into the Maths part of it but to make use of OpenACC to parallelize and improve the performance.\n",
    "\n",
    "The general flow of the code is as shown in diagram below. For each time step the differential equations are solved.\n",
    "\n",
    "<img src=\"images/Outer_Loop.jpg\" width=\"70%\" height=\"70%\">\n",
    "\n",
    "\n",
    "```cpp\n",
    "while (etime < sim_time) {\n",
    "    //If the time step leads to exceeding the simulation time, shorten it for the last step\n",
    "    if (etime + dt > sim_time) { dt = sim_time - etime; }\n",
    "    //Perform a single time step\n",
    "    perform_timestep(state,state_tmp,flux,tend,dt);\n",
    "    //Inform the user\n",
    "    if (masterproc) { printf( \"Elapsed Time: %lf / %lf\\n\", etime , sim_time ); }\n",
    "    //Update the elapsed time and output counter\n",
    "    etime = etime + dt;\n",
    "    output_counter = output_counter + dt;\n",
    "    //If it's time for output, reset the counter, and do output\n",
    "    if (output_counter >= output_freq) {\n",
    "      output_counter = output_counter - output_freq;\n",
    "      output(state,etime);\n",
    "    }\n",
    "  }\n",
    "  \n",
    "```\n",
    "\n",
    "At every time step the direction is reversed to get second order derivative.\n",
    "\n",
    "\n",
    "<img src=\"images/Time_Step.jpg\" width=\"70%\" height=\"70%\">\n",
    "\n",
    "```cpp\n",
    "void perform_timestep( double *state , double *state_tmp , double *flux , double *tend , double dt ) {\n",
    "  if (direction_switch) {\n",
    "    //x-direction first\n",
    "    semi_discrete_step( state , state     , state_tmp , dt / 3 , DIR_X , flux , tend );\n",
    "    semi_discrete_step( state , state_tmp , state_tmp , dt / 2 , DIR_X , flux , tend );\n",
    "    semi_discrete_step( state , state_tmp , state     , dt / 1 , DIR_X , flux , tend );\n",
    "    //z-direction second\n",
    "    semi_discrete_step( state , state     , state_tmp , dt / 3 , DIR_Z , flux , tend );\n",
    "    semi_discrete_step( state , state_tmp , state_tmp , dt / 2 , DIR_Z , flux , tend );\n",
    "    semi_discrete_step( state , state_tmp , state     , dt / 1 , DIR_Z , flux , tend );\n",
    "  } else {\n",
    "    //z-direction second\n",
    "    semi_discrete_step( state , state     , state_tmp , dt / 3 , DIR_Z , flux , tend );\n",
    "    semi_discrete_step( state , state_tmp , state_tmp , dt / 2 , DIR_Z , flux , tend );\n",
    "    semi_discrete_step( state , state_tmp , state     , dt / 1 , DIR_Z , flux , tend );\n",
    "    //x-direction first\n",
    "    semi_discrete_step( state , state     , state_tmp , dt / 3 , DIR_X , flux , tend );\n",
    "    semi_discrete_step( state , state_tmp , state_tmp , dt / 2 , DIR_X , flux , tend );\n",
    "    semi_discrete_step( state , state_tmp , state     , dt / 1 , DIR_X , flux , tend );\n",
    "  }\n",
    "  if (direction_switch) { direction_switch = 0; } else { direction_switch = 1; }\n",
    "}\n",
    "```\n",
    "\n",
    "<img src=\"images/Semi_Discrete.jpg\" width=\"70%\" height=\"70%\">\n",
    "\n",
    "--- \n",
    "\n",
    "## Licensing \n",
    "\n",
    "This material is released by NVIDIA Corporation under the Creative Commons Attribution 4.0 International (CC BY 4.0). "
   ]
  }
 ],
 "metadata": {
  "anaconda-cloud": {},
  "kernelspec": {
   "display_name": "Python 3",
   "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.7.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
