{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# libCEED for Python examples\n",
    "\n",
    "This is a tutorial to illustrate the main feautures of the Python interface for [libCEED](https://github.com/CEED/libCEED/), the low-level API library for efficient high-order discretization methods developed by the co-design [Center for Efficient Exascale Discretizations](https://ceed.exascaleproject.org/) (CEED) of the [Exascale Computing Project](https://www.exascaleproject.org/) (ECP).\n",
    "\n",
    "While libCEED's focus is on high-order finite/spectral element method implementations, the approach is mostly algebraic and thus applicable to other discretizations in factored form, as explained in the [user manual](https://libceed.org/)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Setting up libCEED for Python\n",
    "\n",
    "Install libCEED for Python by running"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "! python -m pip install libceed"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## CeedVector\n",
    "\n",
    "Here we show some basic examples to illustrate the `libceed.Vector` class. In libCEED, CeedVectors constitute the main data structure and serve as input/output for `libceed.Operator`. \n",
    "\n",
    "We illustrate the simple creation of a `libceed.Vector`, how to specify its size, and how to read or manipulate its data."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import libceed\n",
    "\n",
    "ceed = libceed.Ceed()\n",
    "\n",
    "n = 10\n",
    "x = ceed.Vector(n)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The size of the `CeedVector` can also be specified as "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x = ceed.Vector(size=10)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* In the following example, we associate the data stored in a `libceed.Vector` with a `numpy.array` and use it to set and read the `libceed.Vector`'s data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "\n",
    "ceed = libceed.Ceed()\n",
    "x = ceed.Vector(size=3)\n",
    "\n",
    "a = np.arange(1, 4, dtype=\"float64\")\n",
    "x.set_array(a, cmode=libceed.USE_POINTER)\n",
    "\n",
    "with x.array_read() as b:\n",
    "  print(b)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* In the following example, we set all entries to the same value and then visualize the `libceed.Vector`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ceed = libceed.Ceed()\n",
    "x = ceed.Vector(size=5)\n",
    "\n",
    "x.set_value(10)\n",
    "\n",
    "print(x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* In the following example, we set one vector from the array of another vector"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ceed = libceed.Ceed()\n",
    "n = 10\n",
    "\n",
    "x = ceed.Vector(n)\n",
    "y = ceed.Vector(n)\n",
    "\n",
    "a = np.arange(1, 1 + n, dtype=\"float64\")\n",
    "x.set_array(a, cmode=libceed.USE_POINTER)\n",
    "\n",
    "with x.array() as x_array:\n",
    "  y.set_array(x_array, cmode=libceed.USE_POINTER)\n",
    "\n",
    "with y.array_read() as y_array:\n",
    "  print(y_array)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* In the following example, we access and modify only one entry of the `libceed.Vector`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "n = 10\n",
    "\n",
    "x = ceed.Vector(n)\n",
    "a = np.zeros(n, dtype=\"float64\")\n",
    "x.set_array(a, cmode=libceed.USE_POINTER)\n",
    "\n",
    "with x.array() as b:\n",
    "  b[3] = -3.14;\n",
    "a[3]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* In the following example, we compute the $L_1$, $L_2$ (default), and $L_{\\infty}$ norms of a `libceed.Vector` (keeping in mind that these are local norm computations; not accurate for parallel executions with no reductions)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "n = 10\n",
    "x = ceed.Vector(n)\n",
    "\n",
    "a = np.arange(0, n, dtype=\"float64\")\n",
    "for i in range(n):\n",
    "  if (i % 2 == 0):\n",
    "    a[i] *= -1\n",
    "x.set_array(a, cmode=libceed.USE_POINTER)\n",
    "\n",
    "norm_1 = x.norm(normtype=libceed.NORM_1)\n",
    "print(norm_1)\n",
    "\n",
    "norm_2 = x.norm()\n",
    "print(norm_2)\n",
    "\n",
    "norm_max = x.norm(normtype=libceed.NORM_MAX)\n",
    "print(norm_max)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* If you have installed libCEED with CUDA support and Numba, you can use device memory in your `libceed.Vector`s. In the following example, we create a `libceed.Vector` with a libCEED contex that supports CUDA, associate the data stored in a CeedVector with a `numpy.array`, and get a Numba `DeviceNDArray` containing the data on the device."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ceed_gpu = libceed.Ceed('/gpu/cuda')\n",
    "\n",
    "n = 10\n",
    "x = ceed_gpu.Vector(n)\n",
    "\n",
    "a = np.arange(1, 1 + n, dtype=\"float64\")\n",
    "x.set_array(a, cmode=libceed.USE_POINTER)\n",
    "\n",
    "with x.array_read(memtype=libceed.MEM_DEVICE) as device_array:\n",
    "    print(device_array)"
   ]
  }
 ],
 "metadata": {
  "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.8.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
