{"action":"created","comment":{"url":"https://api.github.com/repos/scipy-conference/scipy_proceedings/pulls/comments/67078331","id":67078331,"diff_hunk":"@@ -0,0 +1,1022 @@\n+:author: Prabhu Ramachandran\n+:email: prabhu@aero.iitb.ac.in\n+:institution: Department of Aerospace Engineering\n+:institution: IIT Bombay, Mumbai, India\n+:corresponding:\n+:bibliography: references\n+\n+----------------------------------------------------------------------------------------\n+PySPH: a reproducible and high-performance framework for smoothed particle hydrodynamics\n+----------------------------------------------------------------------------------------\n+\n+.. class:: abstract\n+\n+    Smoothed Particle Hydrodynamics (SPH) is a general purpose technique to\n+    numerically compute the solutions to partial differential equations.  The\n+    method is grid-free and uses particles to discretize the various\n+    properties of interest.  The method is Lagrangian and particles are moved\n+    with the local velocity.\n+\n+    PySPH is an open source framework for Smoothed Particle Hydrodynamics.  It\n+    is implemented in a mix of Python and Cython.  It is designed to be easy\n+    to use on multiple platforms, high-performance and support parallel\n+    execution.  Users write pure-Python code and HPC code is generated on the\n+    fly, compiled, and executed.  PySPH supports OpenMP and MPI for\n+    distributed computing.  This is transparent to the user.  PySPH is also\n+    designed to make it easy to perform reproducible research.  In this paper\n+    we discuss the design and implementation of PySPH.\n+\n+\n+Background and Introduction\n+----------------------------\n+\n+SPH (Smoothed Particle Hydrodynamics) is a general purpose technique to\n+numerically compute the solutions to partial differential equations.  The\n+method is grid-free and uses particles to discretize the various properties of\n+interest.  The method is Lagrangian and particles are moved with the local\n+velocity.  The method was originally developed for astrophysical problems\n+:cite:`lucy77`, :cite:`monaghan77` (compressible gas-dynamics) but has since\n+been extended to simulate incompressible fluids\n+:cite:`sph:fsf:monaghan-jcp94`, solid mechanics :cite:`sph-elastic:gray:2001`,\n+free-surface problems :cite:`sph:fsf:monaghan-jcp94` and a variety of other\n+problems.  Monaghan :cite:`monaghan-review:2005`, provides a good review of\n+the method.\n+\n+The SPH method is relatively easy to implement.  This has resulted in a large\n+number of schemes and implementations proposed by various researchers.  It is\n+often difficult to reproduce published results due to the variety of\n+implementations.  While a few standard packages like (SPHysics\n+:cite:`sphysics`, DualSPHysics :cite:`dualsphysics`, JOSEPHINE\n+:cite:`josephine-sph:cpc:2015`, GADGET-2 :cite:`gadget2-springel:mnras:2005`\n+etc.)  exist, they are usually tailor-made for particular applications and are\n+not general purpose.  They are all implemented in FORTRAN (77 or 90) or C.\n+They do not have a convenient Python interface.\n+\n+Our group has been developing PySPH (http://pysph.bitbucket.org) over the last\n+5 years.  PySPH is open source, and distributed under the new BSD license.\n+Our initial implementation was based on Cython :cite:`behnel2010cython` and\n+also featured some parallelization using MPI.  This was presented at SciPy\n+2010 :cite:`PR-pysph-scipy-2010`.  Unfortunately, this previous version of\n+PySPH, proved difficult to use as users were forced to implement most of their\n+code in Cython.  This was not a matter of simply writing a few high\n+performance functions in Cython.  Our library is object oriented and\n+supporting a new SPH formulation would require subclassing one or more classes\n+and this would need to be done with Cython.  Our type system also ended up\n+becoming more rigid because if we desired performance as well as a general\n+purpose code, all the types needed to be pre-defined.  In addition, writing\n+all this in Cython meant that users had to manage compilation and linking the\n+Cython code during development.  This was not pleasant.\n+\n+It was felt that we might as well have implemented it all in C++ and exposed a\n+Python interface to that.  A traditional compiled language has more developer\n+tooling around it.  For example debugging, performance tuning, profiling would\n+all be easier if everything were written in C or C++.  Unfortunately, such a\n+mixed code-base would not be as easy to use, extend or maintain as a largely\n+pure Python library.  In our experience, a pure Python library is a lot easier\n+for say an undergraduate student to grasp and use over a C/C++ code.  Others\n+are also finding this to be true :cite:`py:nature:2015`.  Many of the top US\n+universities are teaching Python as their first language\n+:cite:`py:teaching-us`. This means that a Python library would also be easier\n+for relatively inexperienced programmers.  It is also true that a Python\n+library would be easier and shorter to write for the other\n+non-high-performance aspects (which is often a significant amount of code).\n+So it seemed that our need for performance was going against our desire for an\n+easy to use Python library that could be used by programmers who were not\n+C/C++ developers.\n+\n+In early 2013, we redesigned PySPH so that users were able to implement an\n+entire simulation using pure Python.  This was done by auto-generating HPC\n+code from the pure Python code that users provided.  This version ended up\n+being faster than our original Cython implementation!  Since we were\n+auto-generating code, with a bit of additional effort it was possible to\n+support OpenMP as well.  The external user API did not change so users did not\n+have to modify their code at all to benefit from this development.  PySPH has\n+thus matured into an easy to use, yet high-performance framework where users\n+can develop their schemes in pure Python and yet obtain performance close to\n+that of a lower-level language implementation.  PySPH has always supported\n+running on a cluster of machines via MPI.  This is seamless and a serial\n+script using PySPH can be run with almost no changes using MPI.\n+\n+PySPH features a reasonable test-suite and we use continuous integration\n+servers to test it on Linux and Windows.  Our documentation is hosted on\n+http://pysph.readthedocs.org.  The framework supports several of the standard\n+SPH schemes.  A suite of about 30 examples are provided.  These are shipped as\n+part of the sources and installed when a user does a pip install for example.\n+The examples are written in a way that makes it easy to extend and also\n+perform comparisons between schemes.  These features make PySPH well suited\n+for reproducible numerical work.  In fact one of our recent papers was written\n+such that every figure in the paper is automatically generated using PySPH.\n+\n+In this paper we discuss the use, design, and implementation of PySPH.  In the\n+next section we provide a high-level overview of the SPH method.\n+\n+Smoothed Particle Hydrodynamics\n+-------------------------------\n+\n+The SPH method works by approximating the identity:\n+\n+.. math::\n+\n+   f(x) = \\int f(x') \\delta (x-x') dx',\n+\n+where, :math:`\\delta` is the Dirac Delta distribution.  This identity is\n+approximated using:\n+\n+.. math::\n+   :label: eq:delta-approx\n+\n+   f(x) \\approx \\int f(x') W (x-x', h) dx',\n+\n+where :math:`W` is a smooth and compact function and is called the kernel.  It\n+is an approximate Dirac delta distribution that is parametrized on the\n+parameter :math:`h` and :math:`W \\rightarrow \\delta` as :math:`h\\rightarrow\n+0`.  :math:`h` is called the smoothing length or smoothing radius of the\n+kernel.  The kernel typically will need to satisfy a few properties if this\n+approximation is to be accurate.  Notably, its area should be unity and if it\n+is symmetric, it can be shown that the approximation is at least second order\n+in :math:`h`.  The above equation can be discretized as,\n+\n+.. math::\n+   :label: eq:sph-discr\n+\n+   f(x) \\approx \\langle f(x) \\rangle = \\sum_{j \\in \\mathcal{N}(x)} W(x-x_j, h) f(x_j) \\Delta x_j,\n+\n+where :math:`x_j` is the position of the particle :math:`j`, :math:`\\Delta\n+x_j` is the volume associated with this particle.  :math:`\\mathcal{N}(x)` is the\n+set of particle indices that are in the neighborhood of :math:`x`.  In SPH\n+each particle carries a mass :math:`m` and associated density :math:`\\rho`\n+with it and the particle volume is typically chosen as\n+:math:`\\Delta x_j = m_j/\\rho_j`.  This results in the following SPH\n+approximation for a function,\n+\n+.. math::\n+   :label: eq:sph-approx\n+\n+   <f(x)> = \\sum_{j \\in \\mathcal{N}(x)} \\frac{m_j}{\\rho_j} W(x-x_j, h) f(x_j).\n+\n+Derivatives of functions at a location :math:`x_i` are readily approximated by\n+taking the derivative of the smooth kernel.  This results in,\n+\n+.. math::\n+   :label: eq:deriv-sph-approx\n+\n+   \\frac{\\partial f_i}{\\partial x_i} = \\sum_{j \\in \\mathcal{N}(x)}\n+        \\frac{m_j}{\\rho_j} (f_j - f_i) \\frac{\\partial W_{ij}}{\\partial x_i}.\n+\n+Here :math:`W_{ij} = W(x_i - x_j)`.  Similar discretizations exist for the\n+divergence and curl operators.  Given that derivatives can be approximated one\n+can solve differential equations fairly easily.  For example the conservation\n+of mass equation for a fluid can be written as,\n+\n+.. math::\n+   :label: eq:cons-mass\n+\n+   \\frac{d \\rho}{dt} = - \\rho \\nabla \\cdot \\vec{v},\n+\n+where :math:`v` is the velocity of the fluid and the LHS is the material or\n+total derivative of the density.  The equation :ref:`eq:cons-mass` is in a\n+Lagrangian form, in that it represents the rate of change of density as one is\n+moving locally with the fluid.  If an SPH discretization of this equation were\n+performed we would get,\n+\n+.. math::\n+   :label: eq:sph-continuity\n+\n+   \\frac{d \\rho_i}{d t} =  -\\rho_i \\sum_{j \\in \\mathcal{N}(x)}\n+   \\frac{m_j}{\\rho_j} \\vec{v}_{ji} \\cdot \\nabla_i W_{ij},\n+\n+where :math:`\\vec{v}_{ji} = \\vec{v}_j - \\vec{v}_i`.  This equation is typical\n+of most SPH discretizations.  SPH can therefore be used to discretize any\n+differential equation.  This works particularly well for a variety of\n+continuum mechanics problems.  Consider the momentum equation for an inviscid\n+fluid,\n+\n+.. math::\n+   :label: eq:momentum\n+\n+   \\frac{d \\vec{u}}{dt} = - \\frac{1}{\\rho} \\nabla p\n+\n+A typical SPH discretization of this could be written as,\n+\n+.. math::\n+   :label: eq:sph-momentum\n+\n+   \\frac{d \\vec{u_i} }{dt} = -\\sum_j m_j \\left ( \\frac{p_j}{\\rho_j^2} +\n+   \\frac{p_i}{\\rho_i^2} \\right) \\nabla W_{ij}\n+\n+\n+More details of these and various other equations can be seen in the review by\n+Monaghan :cite:`monaghan-review:2005`.  It is easy to see that equations\n+:ref:`eq:sph-continuity` and :ref:`eq:sph-momentum` are ordinary differential\n+equations that govern the rate of change of the density and velocity of a\n+fluid particle.  In principle, one can integrate these ODEs to obtain the flow\n+solution given a suitable initial condition and appropriate boundary\n+conditions.\n+\n+\n+Numerical implementation\n+-------------------------\n+\n+As discussed in the previous section, in an SPH scheme, the field properties\n+are first discretized into particles carrying them.  Partial differential\n+equations are reduced to a system of coupled ordinary differential equations\n+and discretized using an SPH approximation.  This results in a system of ODEs\n+for each particle.  These ODEs need to be integrated in time along with\n+suitable boundary and initial conditions in order to solve a particular\n+problem.  To summarize, a typical SPH computation proceeds as follows,\n+\n+- Given an initial condition, the field variables are discretized into\n+  particles carrying the various properties.\n+- Depending on the scheme used to integrate the ODEs, the RHS of the ODEs\n+  needs to be computed (see equations :ref:`eq:sph-continuity` and\n+  :ref:`eq:sph-momentum`).  These RHS terms are called \"accelerations\" or\n+  \"acceleration terms\".\n+- Once the RHS is computed, the ODE can be integrated using a suitable scheme\n+  and the fluid properties are found at the next timestep.\n+\n+The RHS is typically computed as follows:\n+\n+- Initalize the particle accelerations (i.e. the RHS terms).\n+- For each particle in the flow, identify the neighbors of the particle which\n+  will influence the particle.\n+- For each neighbor compute the acceleration due to that particle and\n+  increment the acceleration.\n+\n+Given the total accelerations, the ODEs can be readily integrated with a\n+variety of schemes.  Any general purpose abstraction of the SPH method must\n+hence provide functionality to:\n+\n+1. Easily discretize properties into particles.  This is easily done with\n+   ``numpy`` arrays representing the property values in Python.\n+2. Given a particle, identify the neighbors that influence the particle.  This\n+   is typically called Nearest Neighbor Particle Search (NNPS) in the\n+   literature.\n+3. Define the interactions between the particles, i.e. an easy way to specify\n+   the inter particle accelerations.  In PySPH these are called \"Equations\".\n+4. Define how the ODEs should be integrated.\n+\n+Of the above, the NNPS algorithm is usually a well-known algorithm.  For\n+incompressible flows where the smoothing radius of the particles, :math:`h`,\n+is constant, a simple bin-based linked list implementation is standard.  For\n+cases where :math:`h` varies, a tree-based algorithm is typically used.  Users\n+usually do not need to experiment or modify the NNPS.  PySPH allows the rest\n+of the tasks to be all implemented in pure Python.\n+\n+\n+The PySPH framework\n+-------------------\n+\n+PySPH allows a user to specify the inter-particle interactions as well as the\n+ODE integration in pure Python with a rather simple and low-level syntax.\n+This is described in greater detail further below.  As discussed in the\n+introduction, with older versions of PySPH as discussed in\n+:cite:`PR-pysph-scipy-2010`, these interactions would all need to be written\n+in Cython.  This was not very easy or convenient.  It was also rather\n+limiting.\n+\n+The current version of PySPH supports the following:\n+\n+- Define a complete SPH simulation entirely in Python.\n+- High-performance code is generated from this high-level Python code\n+  automatically and called.  The performance of this code is comparable to\n+  hand-written FORTRAN solvers.\n+- PySPH can use OpenMP seamlessly.  Users do not need to modify their code at\n+  all to use this.  This works on Linux, OS X, and Windows, and\n+  produces good scale-up.\n+- PySPH also works with MPI and once again this is transparent to the user in\n+  that the user does not have to change code to use multiple machines.  This\n+  feature requires mpi4py_ and Zoltan_ to be installed.\n+- PySPH provides a built-in 3D viewer for the particle data generated.  The\n+  viewer requires Mayavi_ :cite:`it:mayavi:cise:gael2011` To be installed.\n+- PySPH is also free and currently hosted at http://pysph.bitbucket.org\n+\n+\n+In the following subsection we provide a high-level overview of PySPH and see\n+how it can be used by a user.  Subsequent subsections discuss the design and\n+implementation in greater detail.\n+\n+.. _mpi4py: http://mpi4py.scipy.org\n+.. _Zoltan: http://www.cs.sandia.gov/zoltan/\n+.. _Mayavi: http://code.enthought.com/projects/mayavi\n+\n+\n+\n+High-level overview\n+~~~~~~~~~~~~~~~~~~~\n+\n+PySPH is tested to work with Python-2.6.x to 2.7.x and also with Python\n+3.4/3.5.  PySPH is a typical Python package and can be installed fairly easily\n+by running::\n+\n+  $ pip install pysph\n+\n+PySPH will require a C++ compiler.  On Linux, this is trivial to get and\n+usually pre-installed.  On OS X, clang will work as will gcc (which can be\n+easily installed using brew_). On Windows the Visual C++ Compiler for Python\n+will need to be installed.  Detailed instructions for all these are available\n+from the `PySPH documentation`_.\n+\n+If one wishes to use OpenMP,\n+\n+- On Linux one needs to have libgomp installed.\n+- On OS X one needs to install OpenMP for clang or one could use GCC which\n+  supports OpenMP via brew_.\n+- On Windows, just having the Visual C++ computer for Python will work.\n+\n+If one wishes to use MPI for distributed computing, one must install Zoltan_\n+which is typically easy to install.  PySPH provides a simple script for this.\n+mpi4py_ is also needed in this case.  Unfortunately, MPI is not tested on\n+Windows by us currently.\n+\n+PySPH also provides an optional 3D viewer and this depends on Mayavi_.\n+\n+In summary, PySPH is easy to install if one has a C++ compiler installed.\n+MPI support is a little involved due to the requirement to install Zoltan_.\n+\n+.. _brew: http://brew.sh/\n+.. _PySPH Documentation: http://pysph.readthedocs.io\n+\n+\n+Once PySPH is installed an executable called ``pysph`` is available.  This is\n+a convenient entry point for various tasks.  Running ``pysph -h`` will provide\n+a listing of these possible tasks.  For example, the test suite can be run\n+using::\n+\n+  $ pysph test\n+\n+This uses nose_ internally and can be passed any arguments that ``nosetests``\n+accepts.\n+\n+PySPH installs about 30 useful examples along with the sources and any of\n+these examples can be readily run.  For example::\n+\n+  $ pysph run\n+  1. cavity\n+     Lid driven cavity using the Transport Velocity\n+     formulation. (10 minutes)\n+  2. couette\n+     Couette flow using the transport velocity\n+     formulation (30 seconds).\n+  [...]\n+  6. elliptical_drop\n+     Evolution of a circular patch of incompressible\n+     fluid. (60 seconds)\n+  [...]\n+  Enter example number you wish to run:\n+\n+\n+Will provide a listing of the examples prompting for a particular one to run.\n+Each example also provides a convenient time estimate if it were to be run in\n+serial.  The example number can be provided at the prompt.  If the name of the\n+example to run is known, one may directly specify it::\n+\n+  $ pysph run elliptical_drop\n+\n+This example will accept a large number of command line arguments.  To find\n+these one can run::\n+\n+  $ pysph run elliptical_drop -h\n+\n+``pysph run`` will execute the standard example.  Note that internally this is\n+somewhat equivalent to running::\n+\n+  $ python -m pysph.examples.elliptical_drop\n+\n+The example may therefore be imported in Python and also extended by users.\n+This is by design.\n+\n+When the example is run using ``pysph run``, the example documentation is\n+first printed and then the example is run.  The example will typically dump\n+the output of the computations to a directory called ``example_name_output``,\n+in the above case this would be ``elliptical_drop_output``.  This output can\n+be viewed using the Mayavi viewer.  This can be done using::\n+\n+  $ pysph view elliptical_drop_output\n+\n+This will start up the viewer with the saved files dumped in the directory.\n+Figure :ref:`fig:pysph-viewer` shows the viewer in action.  The viewer\n+provides a very convenient interface to view the data.  On the right side, one\n+has a standard Mayavi widget which also features a Mayavi icon on the toolbar.\n+Clicking this will open the Mayavi UI with which one can easily change the\n+visualization.  On the left pane there are three sub panels.  On the top, one\n+can see a slider for the file count. This can be used to move through the\n+simulation in time.  This can be also animated by checking the \"Play\" checkbox\n+which will iterate over the files.  The \"Directory\" button allows one to view\n+data from a different output directory.  Hitting the refresh button will\n+rescan the directory to check for any new files.  This makes it convenient to\n+visualize the results from a running simulation.  The \"Connection\" tab can be\n+used when the visualization is in \"Live mode\" when it can connect to a running\n+simulation and view the data live.  While this is very cool in principle, it\n+is seldom used in practice as it is a lot more efficient to just view the\n+dumped files and use the \"Refresh\" button is convenient.  Regardless, it does\n+show another feature of PySPH in that one can actually pause a running\n+simulation and query it if needed.  Below this pane is a \"Solver\" pane which\n+shows the various solver parameters of interest.  The \"Movie\" tab allows a\n+user to dump screenshots and easily produce a movie if needed.  At the bottom\n+of the interface are two panels called \"Particle arrays\" and \"Interpolator\".\n+The particle arrays lists all the particles and different scalar properties\n+associated with the SPH simulation.  Selecting different scalars will display\n+those scalars.  The interpolator tab allows a user to specify a rectilinear\n+region on which the particle properties may be interpolated and visualized --\n+for example if one wishes to see a contour of velocity magnitudes this would\n+be useful.  Right at the bottom is a button to launch a Python shell.  This\n+can be used for advanced scripting and is seldom used by beginners.  This\n+entire viewer is written using about 1024 lines of code and ships with PySPH.\n+\n+\n+.. figure:: mayavi_viewer.png\n+   :alt: Mayavi-based viewer bundled with PySPH.\n+\n+   The viewer provides a convenient interface to view data dumped by\n+   simulations. :label:`fig:pysph-viewer`\n+\n+\n+PySPH output can be dumped either in the form of ``.npz`` files (which are\n+generated by NumPy_) or HDF5 files if h5py_ is installed.  These files can be\n+viewed using other tools or with Python scripts if desired.  The HDF5 in\n+particular can be viewed more easily.  In addition, the ``pysph dump_vtk``\n+command can be used to dump VTK output files that can be used to visualize the\n+output using any tool that supports VTK files like ParaView etc.  This can use\n+either Mayavi or can use pyvisfile_ which has no dependency on VTK.  Finally,\n+the saved data files can be loaded in Python very easily, for example:\n+\n+.. code-block:: python\n+\n+    from pysph.solver.utils import load\n+    data = load('elliptical_drop_100.hdf5')\n+    # if one has only npz files the syntax is the same.\n+    data = load('elliptical_drop_100.npz')\n+\n+This provides a dictionary from which one can obtain the particle arrays and\n+solver data:\n+\n+.. code-block:: python\n+\n+    particle_arrays = data['arrays']\n+    solver_data = data['solver_data']\n+\n+``particle_arrays`` is a dictionary of all the PySPH particle arrays.\n+One can obtain the PySPH particle array, ``fluid``, like so:\n+\n+.. code-block:: python\n+\n+    fluid = particle_arrays['fluid']\n+    p = fluid.p\n+\n+Here ``p`` is a NumPy array of the pressure of each particle.  Particle arrays\n+are described in greater detail in the following sections.  Our intention here\n+is to show that the dumped data can be very easily loaded back into Python if\n+desired.\n+\n+\n+.. _nose: https://pypi.python.org/pypi/nose\n+.. _NumPy: http://numpy.scipy.org\n+.. _h5py: http://www.h5py.org\n+.. _pyvisfile: http://mathema.tician.de/software/pyvisfile\n+\n+\n+As discussed earlier, PySPH supports OpenMP and MPI.  To use multiple cores on\n+a computer one can simply run an example or script as::\n+\n+  $ pysph run elliptical_drop --openmp\n+\n+This will use OpenMP transparently and should work for all the PySPH\n+examples.  PySPH will honor the ``OMP_NUM_THREADS`` environment variable to\n+pick the number of threads.  If PySPH is installed with MPI support through\n+Zoltan, then one may run for example::\n+\n+  $ mpirun -np 4 pysph run dam_break_3d\n+\n+This will run the ``dam_break_3d`` example with 4 processors.  The amount of\n+scale-up depends on the size of the problem and the network.  OpenMP will\n+scale fairly well for moderately sized problems.  Note that for a general\n+PySPH script written by the user, the command to run would simply be::\n+\n+  $ mpirun -np 4 python my_script.py\n+\n+Similarly when using OpenMP::\n+\n+  $ python my_example.py --openmp\n+\n+This provides a very high-level introduction to PySPH in general.  The next\n+section discusses some essential software engineering used in the development\n+of PySPH.  This is followed by details on the underlying design of PySPH.\n+\n+\n+Essential software engineering\n+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n+\n+PySPH follows several of the standard software development practices that most\n+modern open source implementations follow.  For example:\n+\n+- Our sources are hosted on bitbucket (http://pysph.bitbucket.org).  We are\n+  thinking of shifting to github because github has much better integration\n+  with continuous integration services and this is a rather frustrating pain\n+  point with bitbucket.\n+- We use pull-requests to review all new features and bug fixes.  At this\n+  point there is only a single reviewer (the author) but this should hopefully\n+  increase over time.\n+- PySPH has a reasonable set of unit tests and functional tests.  Each time a\n+  bug is found, a test case is first created (when possible or reasonable),\n+  and then fixed.  nose_ is used for discovering and executing tests.  One of\n+  our functional tests runs one time step of every single example that ships\n+  with PySPH.  tox_ based tests are also supported.  This makes it easy to\n+  test on Python 2.6, 2.7 and 3.x.\n+- We use continuous integration services from http://shippable.com for Linux,\n+  http://appveyor.com for Windows and http://codeship.com for faster Linux\n+  builds.\n+- Our documentation is generated using Sphinx and hosted online on\n+  http://pysph.readthedocs.io.\n+- Releases are pushed to pypi.\n+- The `pysph-users mailing list\n+  <https://groups.google.com/forum/#!forum/pysph-users>`_ is also available\n+  where users can post their questions.  Unfortunately, the response time is\n+  currently slow as the author does not have the time for this but we are\n+  hoping this will improve as more graduate students start getting involved\n+  with PySPH.\n+\n+These greatly improve the quality, reliability and usability of the software\n+and also encourage open collaboration.\n+\n+\n+.. _tox: https://pypi.python.org/pypi/tox\n+\n+\n+Design overview\n+~~~~~~~~~~~~~~~~\n+\n+In the previous sections a high-level description of the project was\n+provided.  This section provides more design details of how PySPH works\n+internally.  The general approach used in PySPH is as follows:\n+\n+1. Create particles: discretize the initial materials into particles with\n+   suitable properties.\n+2. Choose an appropriate kernel for the SPH approximation.\n+3. Create equations: write out the equations that specify the inter-particle\n+   interactions.\n+4. Setup the integrator and specify the integration steps, for example one\n+   could use an Euler scheme or a predictor-corrector scheme and each of these\n+   involve slightly different integration steps.  These need to be specified\n+   explicitly.\n+\n+PySPH allows a user to do all of these from pure Python.\n+\n+1. In PySPH, particles of a particular kind are managed by a ``ParticleArray``\n+   instance.  A particle array is assigned a unique name and manages a\n+   collection of properties.  Each property is internally represented as a\n+   contiguous block of memory.  All properties have the same number of\n+   elements.  A particle array may also have any number of \"constants\"\n+   associated with it.  Each constant can be a scalar or an array but its size\n+   is independent of the number of particles.\n+\n+2. The kernels are implemented in pure Python and a default collection of\n+   kernels is available in ``pysph.base.kernels``.  A new kernel class would\n+   implement the following methods, note that the default arguments have no\n+   meaning except that they help the code generator use the correct types:\n+\n+.. code-block:: python\n+\n+   class MyKernel(object):\n+       def __init__(self, dim):\n+           # ...\n+       def kernel(self, xij=[0., 0, 0], rij=1.0,\n+                  h=1.0):\n+           # ...\n+       def gradient(self, xij=[0., 0, 0], rij=1.0,\n+                    h=1.0, grad=[0, 0, 0]):\n+           # ...\n+\n+\n+3. In PySPH, the equations can also be created in pure Python and this is\n+   discussed in detail in the following.\n+\n+4. The integrators are split into two parts, an integrator and an integrator\n+   step.  This is also written in pure Python and discussed with an example\n+   further below.\n+\n+\n+\n+A typical example is considered first to illustrate the design.  Consider the\n+example ``pysph/pysph/examples/elliptical_drop.py``.  When installed, this may\n+be imported as ``import pysph.examples.elliptical_drop``. This example\n+simulates the evolution of a fluid drop that is initially circular and imposed\n+an initial velocity field of the form :math:`\\vec{V} = -100x \\hat{i} + 100y\n+\\hat{j}`.  This problem is a simple benchmark problem that was first solved in\n+the context of SPH by :cite:`sph:fsf:monaghan-jcp94`.  The key parts of the\n+example are shown below:\n+\n+.. code-block:: python\n+\n+   from numpy import array, ones_like, mgrid, sqrt\n+\n+   # PySPH base and carray imports\n+   from pysph.base.utils import get_particle_array\n+   from pysph.base.kernels import Gaussian\n+\n+   # PySPH solver and integrator\n+   from pysph.solver.application import Application\n+   from pysph.sph.integrator import EPECIntegrator\n+   from pysph.sph.scheme import WCSPHScheme\n+\n+   class EllipticalDrop(Application):\n+       def initialize(self):\n+           # ...\n+       def create_particles(self):\n+           # ...\n+       def create_scheme(self):\n+           # ...\n+       def post_process(self, info_file_or_dir):\n+           # ...\n+\n+   if __name__ ==  '__main__':\n+       app = EllipticalDrop()\n+       app.run()\n+       app.post_process(app.info_filename)\n+\n+This illustrative example deliberately excludes several details to focus on\n+the general structure and API.  There are a few common imports at the top\n+starting with NumPy specific imports first.  The next imports are PySPH\n+specific:\n+\n+- ``get_particle_array`` is a convenient function that helps create a\n+  ``ParticleArray`` instance.\n+- The ``Gaussian`` kernel is used for the SPH simulation.\n+- The ``Application`` class is subclassed to create the new example.\n+- The ``WCSPHScheme`` encapsulates a particular scheme, in this case this\n+  class abstracts out the requirements for a weakly-compressible scheme\n+  applied to incompressible flows.  Internally the WCSPH scheme is responsible\n+  to setup the equations and the integrator.  By abstracting this into a\n+  scheme it becomes easy to reuse this instead of spelling out the equations\n+  for each example.\n+\n+The typical entry point for a user is to subclass ``Application`` to solve\n+their particular problem.  The methods listed above are:\n+\n+- ``initialize``, this is automatically called by ``Application.__init__`` and\n+  is typically not used but sometimes useful when one wishes to have some\n+  common attributes setup.\n+- ``create_particles`` generates the initial particle distribution and returns\n+  a sequence of ``ParticleArray`` instances.\n+- ``create_scheme`` creates the particular scheme.  A ``SchemeChooser`` is\n+  also available which can be given multiple schemes and allows the user to\n+  switch between them via command line arguments.\n+- the ``post_process`` method is run in the end to compute any useful\n+  quantities that may be used to check the accuracy of the simulation or\n+  facilitate comparisons between different schemes.\n+\n+The ``if __name__`` block is listed to just illustrate how this application\n+can be used.  When ``run`` is called, the command line arguments are parsed,\n+the various objects involved are suitably configured and the simulation\n+executed.  At the end, the ``post_process`` method is called.  This also shows\n+that a user could potentially rewrite the post processing code and simply\n+rerun that part instead of re-running the simulation (which can sometimes run\n+for days).\n+\n+We next look inside the ``create_particles`` and ``create_scheme`` methods:\n+\n+.. code-block:: python\n+   :linenos:\n+\n+    def create_particles(self):\n+        x, y = mgrid[-1.05:1.05+1e-4:dx,\n+                     -1.05:1.05+1e-4:dx]\n+        x, y = x.ravel(), y.ravel()\n+        m = ones_like(x)*dx*dx\n+        h = ones_like(x)*hdx*dx\n+        # ...\n+        u = -100*x\n+        v = 100*y\n+\n+        # remove particles outside the circle\n+        indices = []\n+        for i in range(len(x)):\n+            if sqrt(x[i]*x[i] + y[i]*y[i]) - 1 > 1e-10:\n+                indices.append(i)\n+\n+        pa = get_particle_array(\n+              x=x, y=y, m=m, rho=rho, h=h, p=p, u=u, v=v,\n+              cs=cs, name='fluid')\n+        pa.remove_particles(indices)\n+\n+        self.scheme.setup_properties([pa])\n+        return [pa]\n+\n+    def create_scheme(self):\n+        s = WCSPHScheme(\n+            ['fluid'], [], dim=2, rho0=self.ro, c0=co,\n+            h0=self.dx*self.hdx, hdx=self.hdx,\n+            gamma=7.0, alpha=0.1, beta=0.0\n+        )\n+        kernel = Gaussian(dim=2)\n+        dt = 5e-6; tf = 0.0076\n+        s.configure_solver(\n+            kernel=kernel, integrator_cls=EPECIntegrator,\n+            dt=dt, tf=tf,\n+            adaptive_timestep=True, cfl=0.3, n_damp=50,\n+        )\n+        return s\n+\n+\n+The ``create_particles`` method above is straightforward.  NumPy arrays are\n+created that set the position, mass, smoothing radius :math:`h`, the velocity\n+etc.  The arrays are all one dimensional.  The indices that are outside the\n+circle are identified between lines 11 and 14 and these are removed in\n+line 20.  This could have also been done with pure NumPy indexing.  In Line 17\n+the particle array instance is created and is called ``\"fluid\"``.  Line 22\n+delegates to the ``scheme`` to setup any additional properties for the\n+particle array and finally a list of particle arrays is returned.\n+\n+The ``create_scheme`` method is fairly simple.  A ``WCSPHScheme`` is\n+instantiated and passed arguments as defaults.  The kernel is created and this\n+is all passed to a scheme method called ``configure_solver``, this also\n+specifies the integrator to use, the timestep to use, the time for which the\n+simulation is to be run etc.  To someone who is familiar with SPH, these are\n+fairly obvious parameters.  The scheme may also allow a user to set these\n+parameters via command line arguments.  This can be found by simply running::\n+\n+  $ pysph run elliptical_drop -h\n+\n+\n+The ``post_process`` method is also fairly straightforward and is entirely\n+optional.  With just this code, one may run the example.  As soon as this is\n+done, PySPH will generate high-performance code, compile it, and use that code\n+to run the example.\n+\n+The scheme in this case is really doing a lot of work because it encapsulates\n+the creation of the equations and the integrators.  In order to understand\n+this better, we look at a lower-level implementation of the same example.\n+This example also ships with PySPH and is called\n+``elliptical_drop_no_scheme.py``.  Unsurprisingly, this example can be run\n+as::\n+\n+  $ pysph run elliptical_drop_no_scheme\n+\n+This implementation does not use a scheme but instead creates the equations\n+and the ``Solver`` instance directly.  The example differs from the\n+``elliptical_drop`` in that there is no ``create_scheme`` method but instead\n+there are two additional methods:\n+- ``create_equations`` which explicitly creates the equations.\n+- ``create_solver`` which sets up the solver, stepper and integrators.\n+The ``create_particles`` and ``post_process`` etc. are all identical.  The\n+code is listed below:\n+\n+.. code-block:: python\n+\n+    def create_equations(self):\n+        equations = [\n+            Group(equations=[\n+              TaitEOS(\n+                  dest='fluid', sources=None,\n+                  rho0=self.ro, c0=self.co, gamma=7.0),\n+            ], real=False),\n+\n+            Group(equations=[\n+                ContinuityEquation(\n+                    dest='fluid', sources=['fluid',]),\n+\n+                MomentumEquation(\n+                    dest='fluid', sources=['fluid'],\n+                    alpha=self.alpha, beta=0.0, c0=self.co),\n+\n+                XSPHCorrection(dest='fluid', sources=['fluid']),\n+\n+            ]),\n+        ]\n+        return equations\n+\n+As can be seen, the equations are simply instantiated.  We look closer at\n+equations further below but at this stage it can be seen that:\n+\n+- Each equation has a destination ``dest`` and a list of sources.  A\n+  destination is a particle on which the acceleration is to be computed a\n+  source is one that influences the particle.  In this problem there is only\n+  one destination and source, ``\"fluid\"``.  Note that the names of the arrays\n+  are used here to determine the appropriate particle array.\n+- The ``TaitEOS`` is an equation of state, i.e. it does not depend on any\n+  neighbors and is simply an equation of the form :math:`p = (\\rho - \\rho_0)\n+  c^2` or something along those lines.  This does not require any \"sources\".\n+- Equations can be \"grouped\" using a ``Group``.  Each time the acceleration is\n+  computed, all equations in a group are evaluated for all the particles\n+  before the next group is considered.  This is important in the above case as\n+  an equation of state is needed to compute the pressure.  The pressure must\n+  be found for all particles before the other accelerations are evaluated.\n+- The other equations describe the physics of the problem, namely, continuity\n+  and momentum.  The ``XSPHCorrection`` is an SPH-specific correction (see\n+  :cite:`monaghan-review:2005`).\n+- The group containing ``TaitEOS`` has an additional argument ``real=False``\n+  this is only used when the example is run via MPI and specifies that the\n+  equation of state be computed for all particles local and remote.\n+\n+\n+.. code-block:: python\n+\n+    def create_solver(self):\n+        kernel = Gaussian(dim=2)\n+\n+        integrator = EPECIntegrator(fluid=WCSPHStep())\n+\n+        dt = 5e-6; tf = 0.0076\n+        solver = Solver(\n+            kernel=kernel, dim=2, integrator=integrator,\n+            dt=dt, tf=tf, adaptive_timestep=True,\n+            cfl=0.3, n_damp=50,\n+            output_at_times=[0.0008, 0.0038])\n+\n+        return solver\n+\n+The ``create_solver`` method simply instantiates a ``EPECIntegrator`` and asks\n+that the fluid particles be stepped with the ``WCSPHStep`` stepper.  A solver\n+is then constructed which combines the kernel, integrator, and any integration\n+parameters.  The scheme automatically creates the equations and solver.\n+Specifying equations directly can be error prone and schemes make this task a\n+lot easier.  Schemes also support command line arguments which the direct\n+example would require additional code for.\n+\n+The only thing that remains is to see how the equations and steppers are\n+actually implemented.  Let us consider the continuity equation\n+(:ref:`eq:sph-continuity`) and see how the ``ContinuityEquation`` class is\n+implemented.\n+\n+.. code-block:: python\n+\n+   class ContinuityEquation(Equation):\n+       def initialize(self, d_idx, d_arho):\n+           d_arho[d_idx] = 0.0\n+\n+       def loop(self, d_idx, d_arho, s_idx,\n+                s_m, DWIJ, VIJ):\n+           vijdotdwij = DWIJ[0]*VIJ[0] + \\\n+               DWIJ[1]*VIJ[1] + DWIJ[2]*VIJ[2]\n+           d_arho[d_idx] += s_m[s_idx]*vijdotdwij\n+\n+\n+In this class there are two methods:\n+\n+- ``initialize``: this is called first for every destination particle with\n+  index ``d_idx``.\n+\n+- ``loop``: this is called for every destination source pair.  Thus,\n+  internally all the nearest neighbors of the destination particle are\n+  identified and looped over.\n+\n+There are some simple conventions followed with the variable names.\n+\n+- ``d_*`` indicates a destination array.  The name that follows ``d_`` is the\n+  same as the property name of the array.\n+\n+- ``s_*`` indicates a source array.\n+\n+- ``d_idx`` is a destination index and ``s_idx`` the source index.\n+\n+- A method can take any arguments in arbitrary order and these are\n+  automatically passed in the right order.\n+\n+\n+Clearly this seems rather low-level, however, it is simple to write and maps\n+almost exactly with the actual SPH discretized equation (see equation\n+:ref:`eq:sph-continuity`).\n+\n+The integrator and integrator stepper code is similarly quite simple and low\n+level.  It is written entirely in pure Python.  More details are available in\n+the online `PySPH design overview\n+<http://pysph.readthedocs.io/en/latest/design/overview.html>`_ document.\n+\n+This approach allows a user to specify new equations and integration schemes\n+very easily and use them to perform SPH simulations.  The ``Application``\n+class also has several other convenient methods that can be overridden by the\n+user to perform a variety of tasks.  For example:\n+\n+- ``add_user_options`` can be overridden to add any user-defined command line\n+  arguments.  The argument parsing is done using argparse_.  Once processed,\n+  the options are available in ``self.options``.\n+- ``consume_user_options`` is used to use any of the parsed options.  This is\n+  called after the command line arguments are parsed but before the\n+  ``create_particles`` etc.\n+- ``create_domain`` can be used to create a periodic domain.\n+- ``configure_scheme`` can be used to configure a created scheme based on\n+  command line arguments.  This is also useful in conjunction with\n+  user-defined command line arguments.\n+- ``pre_step, post_step, post_stage`` are convenient methods which will be\n+  called before each timestep, after each timestep and after each integration\n+  stage if these are defined.  These are convenient for a variety of user\n+  defined actions including debugging, adaptive refinement, checking for\n+  errors etc.\n+\n+Together, these features are extremely powerful and allow a user a great deal\n+of flexibility.\n+\n+\n+.. _argparse: https://docs.python.org/3/library/argparse.html\n+\n+\n+\n+High performance\n+~~~~~~~~~~~~~~~~~\n+\n+While PySPH allows a user to write the code in pure Python, internally,\n+high-performance Cython code is generated, compiled, and used to extract as\n+much performance as possible.  This is done using Mako_ templates.  A general\n+Mako template is written to compute the accelerations, this is in\n+``pysph/sph/acceleration_eval_cython.mako``.  The main module is\n+``pysph.sph.acceleration_eval`` which is implemented in pure Python.  A helper\n+class ``pysph.sph.acceleration_eval_cython_helper`` uses all the high-level\n+information from the user code and provides several methods that are called\n+from the mako template.\n+\n+The user Python code is already implemented in a low-level allowing us to\n+directly inject the sources into the Cython code.  The\n+``pysph.base.cython_generator`` module helps with the generation of Cython\n+code from Python code.  The ``pysph.base.ext_module`` takes the generated\n+Cython and compiles this.  The extension modules are stored in\n+``~/.pysph/source`` in a Python version and architecture specific directory.\n+The md5sum of the Cython code is checked and if an extension for that md5sum\n+exists the code is not recompiled.  Care is taken to look for changes in\n+dependencies of this generated source.\n+\n+As a result of this, our code performs as well as a hand-written FOTRAN code.\n+We have compared running both 2D and 3D problems with the SPHysics serial\n+code.  Our code is about 1.3 to 1.5 times slower.  In 2D this is because by\n+default our implementation is 3D.  In addition SPHysics symmetrizes the\n+inter-particle computations, i.e. while computing the interaction of a source\n+on a destination, they also compute the opposite force and store it.  This\n+appears to give them the extra 1.3 fold speed improvement.  Regardless, it is\n+clear that PySPH is comparable in performance with SPHysics.  However, PySPH\n+is a lot easier to use and much easier to extend.\n+\n+\n+\n+.. _Mako: https://pypi.python.org/pypi/Mako\n+\n+\n+Reproducibility\n+~~~~~~~~~~~~~~~~\n+\n+\n+The object-oriented API of PySPH makes it easy to extend and use.  The design\n+allows for a large amount of code reuse.\n+\n+We have found that it is extremely important to treat our examples to be as\n+important as the source itself.  This forces us to design our examples to be\n+reusable.  This is extremely important for various reasons:\n+\n+- It forces a clean API for an end-user.  This drives us to minimize\n+  repetitive code, and simplify the API.\n+- The examples are all reusable.  If a user wishes to try a new scheme they\n+  need to just focus on the new scheme.  The particle creation and post\n+  processing are already implemented.\n+\n+- We find that while post-processing results, we also dump out the\n+  post-processed data into a separate file.  This makes it trivial to compare\n+  the output of different schemes.\n+\n+- Shipping the examples with the installation is very important.\n+\n+- We also provide some simple tools in ``pysph.tools.automation`` which make\n+  it easy to use PySPH in an automation framework.\n+\n+Recently, we have used these features to make an entire publication completely\n+reproducible.  Every figure produced in the paper (a total of 23 in number)","path":"papers/prabhu_ramachandran_pysph/prabhu_ramachandran_pysph.rst","position":978,"original_position":978,"commit_id":"64c1d9c255f82cca1bb2b932f4994d0e9e666fdd","original_commit_id":"64c1d9c255f82cca1bb2b932f4994d0e9e666fdd","user":{"login":"jakevdp","id":781659,"avatar_url":"https://avatars.githubusercontent.com/u/781659?v=3","gravatar_id":"","url":"https://api.github.com/users/jakevdp","html_url":"https://github.com/jakevdp","followers_url":"https://api.github.com/users/jakevdp/followers","following_url":"https://api.github.com/users/jakevdp/following{/other_user}","gists_url":"https://api.github.com/users/jakevdp/gists{/gist_id}","starred_url":"https://api.github.com/users/jakevdp/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jakevdp/subscriptions","organizations_url":"https://api.github.com/users/jakevdp/orgs","repos_url":"https://api.github.com/users/jakevdp/repos","events_url":"https://api.github.com/users/jakevdp/events{/privacy}","received_events_url":"https://api.github.com/users/jakevdp/received_events","type":"User","site_admin":false},"body":"Which paper?","created_at":"2016-06-14T23:48:37Z","updated_at":"2016-06-14T23:48:37Z","html_url":"https://github.com/scipy-conference/scipy_proceedings/pull/214#discussion_r67078331","pull_request_url":"https://api.github.com/repos/scipy-conference/scipy_proceedings/pulls/214","_links":{"self":{"href":"https://api.github.com/repos/scipy-conference/scipy_proceedings/pulls/comments/67078331"},"html":{"href":"https://github.com/scipy-conference/scipy_proceedings/pull/214#discussion_r67078331"},"pull_request":{"href":"https://api.github.com/repos/scipy-conference/scipy_proceedings/pulls/214"}}},"pull_request":{"url":"https://api.github.com/repos/scipy-conference/scipy_proceedings/pulls/214","id":71969786,"html_url":"https://github.com/scipy-conference/scipy_proceedings/pull/214","diff_url":"https://github.com/scipy-conference/scipy_proceedings/pull/214.diff","patch_url":"https://github.com/scipy-conference/scipy_proceedings/pull/214.patch","issue_url":"https://api.github.com/repos/scipy-conference/scipy_proceedings/issues/214","number":214,"state":"open","locked":false,"title":"Paper: PySPH. Prabhu Ramachandran","user":{"login":"prabhuramachandran","id":272585,"avatar_url":"https://avatars.githubusercontent.com/u/272585?v=3","gravatar_id":"","url":"https://api.github.com/users/prabhuramachandran","html_url":"https://github.com/prabhuramachandran","followers_url":"https://api.github.com/users/prabhuramachandran/followers","following_url":"https://api.github.com/users/prabhuramachandran/following{/other_user}","gists_url":"https://api.github.com/users/prabhuramachandran/gists{/gist_id}","starred_url":"https://api.github.com/users/prabhuramachandran/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/prabhuramachandran/subscriptions","organizations_url":"https://api.github.com/users/prabhuramachandran/orgs","repos_url":"https://api.github.com/users/prabhuramachandran/repos","events_url":"https://api.github.com/users/prabhuramachandran/events{/privacy}","received_events_url":"https://api.github.com/users/prabhuramachandran/received_events","type":"User","site_admin":false},"body":"The paper is now complete in terms of content.  I will revise it over the course of the week.","created_at":"2016-05-31T07:31:57Z","updated_at":"2016-06-14T23:48:37Z","closed_at":null,"merged_at":null,"merge_commit_sha":"4075ff88b9854155dacb21ef74f6235a4b98ba95","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/scipy-conference/scipy_proceedings/pulls/214/commits","review_comments_url":"https://api.github.com/repos/scipy-conference/scipy_proceedings/pulls/214/comments","review_comment_url":"https://api.github.com/repos/scipy-conference/scipy_proceedings/pulls/comments{/number}","comments_url":"https://api.github.com/repos/scipy-conference/scipy_proceedings/issues/214/comments","statuses_url":"https://api.github.com/repos/scipy-conference/scipy_proceedings/statuses/64c1d9c255f82cca1bb2b932f4994d0e9e666fdd","head":{"label":"prabhuramachandran:2016_paper2","ref":"2016_paper2","sha":"64c1d9c255f82cca1bb2b932f4994d0e9e666fdd","user":{"login":"prabhuramachandran","id":272585,"avatar_url":"https://avatars.githubusercontent.com/u/272585?v=3","gravatar_id":"","url":"https://api.github.com/users/prabhuramachandran","html_url":"https://github.com/prabhuramachandran","followers_url":"https://api.github.com/users/prabhuramachandran/followers","following_url":"https://api.github.com/users/prabhuramachandran/following{/other_user}","gists_url":"https://api.github.com/users/prabhuramachandran/gists{/gist_id}","starred_url":"https://api.github.com/users/prabhuramachandran/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/prabhuramachandran/subscriptions","organizations_url":"https://api.github.com/users/prabhuramachandran/orgs","repos_url":"https://api.github.com/users/prabhuramachandran/repos","events_url":"https://api.github.com/users/prabhuramachandran/events{/privacy}","received_events_url":"https://api.github.com/users/prabhuramachandran/received_events","type":"User","site_admin":false},"repo":{"id":20846741,"name":"scipy_proceedings","full_name":"prabhuramachandran/scipy_proceedings","owner":{"login":"prabhuramachandran","id":272585,"avatar_url":"https://avatars.githubusercontent.com/u/272585?v=3","gravatar_id":"","url":"https://api.github.com/users/prabhuramachandran","html_url":"https://github.com/prabhuramachandran","followers_url":"https://api.github.com/users/prabhuramachandran/followers","following_url":"https://api.github.com/users/prabhuramachandran/following{/other_user}","gists_url":"https://api.github.com/users/prabhuramachandran/gists{/gist_id}","starred_url":"https://api.github.com/users/prabhuramachandran/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/prabhuramachandran/subscriptions","organizations_url":"https://api.github.com/users/prabhuramachandran/orgs","repos_url":"https://api.github.com/users/prabhuramachandran/repos","events_url":"https://api.github.com/users/prabhuramachandran/events{/privacy}","received_events_url":"https://api.github.com/users/prabhuramachandran/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/prabhuramachandran/scipy_proceedings","description":"Tools used to generate the SciPy conference proceedings","fork":true,"url":"https://api.github.com/repos/prabhuramachandran/scipy_proceedings","forks_url":"https://api.github.com/repos/prabhuramachandran/scipy_proceedings/forks","keys_url":"https://api.github.com/repos/prabhuramachandran/scipy_proceedings/keys{/key_id}","collaborators_url":"https://api.github.com/repos/prabhuramachandran/scipy_proceedings/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/prabhuramachandran/scipy_proceedings/teams","hooks_url":"https://api.github.com/repos/prabhuramachandran/scipy_proceedings/hooks","issue_events_url":"https://api.github.com/repos/prabhuramachandran/scipy_proceedings/issues/events{/number}","events_url":"https://api.github.com/repos/prabhuramachandran/scipy_proceedings/events","assignees_url":"https://api.github.com/repos/prabhuramachandran/scipy_proceedings/assignees{/user}","branches_url":"https://api.github.com/repos/prabhuramachandran/scipy_proceedings/branches{/branch}","tags_url":"https://api.github.com/repos/prabhuramachandran/scipy_proceedings/tags","blobs_url":"https://api.github.com/repos/prabhuramachandran/scipy_proceedings/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/prabhuramachandran/scipy_proceedings/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/prabhuramachandran/scipy_proceedings/git/refs{/sha}","trees_url":"https://api.github.com/repos/prabhuramachandran/scipy_proceedings/git/trees{/sha}","statuses_url":"https://api.github.com/repos/prabhuramachandran/scipy_proceedings/statuses/{sha}","languages_url":"https://api.github.com/repos/prabhuramachandran/scipy_proceedings/languages","stargazers_url":"https://api.github.com/repos/prabhuramachandran/scipy_proceedings/stargazers","contributors_url":"https://api.github.com/repos/prabhuramachandran/scipy_proceedings/contributors","subscribers_url":"https://api.github.com/repos/prabhuramachandran/scipy_proceedings/subscribers","subscription_url":"https://api.github.com/repos/prabhuramachandran/scipy_proceedings/subscription","commits_url":"https://api.github.com/repos/prabhuramachandran/scipy_proceedings/commits{/sha}","git_commits_url":"https://api.github.com/repos/prabhuramachandran/scipy_proceedings/git/commits{/sha}","comments_url":"https://api.github.com/repos/prabhuramachandran/scipy_proceedings/comments{/number}","issue_comment_url":"https://api.github.com/repos/prabhuramachandran/scipy_proceedings/issues/comments{/number}","contents_url":"https://api.github.com/repos/prabhuramachandran/scipy_proceedings/contents/{+path}","compare_url":"https://api.github.com/repos/prabhuramachandran/scipy_proceedings/compare/{base}...{head}","merges_url":"https://api.github.com/repos/prabhuramachandran/scipy_proceedings/merges","archive_url":"https://api.github.com/repos/prabhuramachandran/scipy_proceedings/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/prabhuramachandran/scipy_proceedings/downloads","issues_url":"https://api.github.com/repos/prabhuramachandran/scipy_proceedings/issues{/number}","pulls_url":"https://api.github.com/repos/prabhuramachandran/scipy_proceedings/pulls{/number}","milestones_url":"https://api.github.com/repos/prabhuramachandran/scipy_proceedings/milestones{/number}","notifications_url":"https://api.github.com/repos/prabhuramachandran/scipy_proceedings/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/prabhuramachandran/scipy_proceedings/labels{/name}","releases_url":"https://api.github.com/repos/prabhuramachandran/scipy_proceedings/releases{/id}","deployments_url":"https://api.github.com/repos/prabhuramachandran/scipy_proceedings/deployments","created_at":"2014-06-15T02:50:13Z","updated_at":"2016-05-28T18:40:50Z","pushed_at":"2016-06-06T08:17:56Z","git_url":"git://github.com/prabhuramachandran/scipy_proceedings.git","ssh_url":"git@github.com:prabhuramachandran/scipy_proceedings.git","clone_url":"https://github.com/prabhuramachandran/scipy_proceedings.git","svn_url":"https://github.com/prabhuramachandran/scipy_proceedings","homepage":"","size":5417,"stargazers_count":1,"watchers_count":1,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":1,"default_branch":"master"}},"base":{"label":"scipy-conference:2016","ref":"2016","sha":"c91d1c88fe313a4503f92413cbdc879ecddf223a","user":{"login":"scipy-conference","id":4884274,"avatar_url":"https://avatars.githubusercontent.com/u/4884274?v=3","gravatar_id":"","url":"https://api.github.com/users/scipy-conference","html_url":"https://github.com/scipy-conference","followers_url":"https://api.github.com/users/scipy-conference/followers","following_url":"https://api.github.com/users/scipy-conference/following{/other_user}","gists_url":"https://api.github.com/users/scipy-conference/gists{/gist_id}","starred_url":"https://api.github.com/users/scipy-conference/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/scipy-conference/subscriptions","organizations_url":"https://api.github.com/users/scipy-conference/orgs","repos_url":"https://api.github.com/users/scipy-conference/repos","events_url":"https://api.github.com/users/scipy-conference/events{/privacy}","received_events_url":"https://api.github.com/users/scipy-conference/received_events","type":"Organization","site_admin":false},"repo":{"id":1825629,"name":"scipy_proceedings","full_name":"scipy-conference/scipy_proceedings","owner":{"login":"scipy-conference","id":4884274,"avatar_url":"https://avatars.githubusercontent.com/u/4884274?v=3","gravatar_id":"","url":"https://api.github.com/users/scipy-conference","html_url":"https://github.com/scipy-conference","followers_url":"https://api.github.com/users/scipy-conference/followers","following_url":"https://api.github.com/users/scipy-conference/following{/other_user}","gists_url":"https://api.github.com/users/scipy-conference/gists{/gist_id}","starred_url":"https://api.github.com/users/scipy-conference/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/scipy-conference/subscriptions","organizations_url":"https://api.github.com/users/scipy-conference/orgs","repos_url":"https://api.github.com/users/scipy-conference/repos","events_url":"https://api.github.com/users/scipy-conference/events{/privacy}","received_events_url":"https://api.github.com/users/scipy-conference/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/scipy-conference/scipy_proceedings","description":"Tools used to generate the SciPy conference proceedings","fork":false,"url":"https://api.github.com/repos/scipy-conference/scipy_proceedings","forks_url":"https://api.github.com/repos/scipy-conference/scipy_proceedings/forks","keys_url":"https://api.github.com/repos/scipy-conference/scipy_proceedings/keys{/key_id}","collaborators_url":"https://api.github.com/repos/scipy-conference/scipy_proceedings/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/scipy-conference/scipy_proceedings/teams","hooks_url":"https://api.github.com/repos/scipy-conference/scipy_proceedings/hooks","issue_events_url":"https://api.github.com/repos/scipy-conference/scipy_proceedings/issues/events{/number}","events_url":"https://api.github.com/repos/scipy-conference/scipy_proceedings/events","assignees_url":"https://api.github.com/repos/scipy-conference/scipy_proceedings/assignees{/user}","branches_url":"https://api.github.com/repos/scipy-conference/scipy_proceedings/branches{/branch}","tags_url":"https://api.github.com/repos/scipy-conference/scipy_proceedings/tags","blobs_url":"https://api.github.com/repos/scipy-conference/scipy_proceedings/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/scipy-conference/scipy_proceedings/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/scipy-conference/scipy_proceedings/git/refs{/sha}","trees_url":"https://api.github.com/repos/scipy-conference/scipy_proceedings/git/trees{/sha}","statuses_url":"https://api.github.com/repos/scipy-conference/scipy_proceedings/statuses/{sha}","languages_url":"https://api.github.com/repos/scipy-conference/scipy_proceedings/languages","stargazers_url":"https://api.github.com/repos/scipy-conference/scipy_proceedings/stargazers","contributors_url":"https://api.github.com/repos/scipy-conference/scipy_proceedings/contributors","subscribers_url":"https://api.github.com/repos/scipy-conference/scipy_proceedings/subscribers","subscription_url":"https://api.github.com/repos/scipy-conference/scipy_proceedings/subscription","commits_url":"https://api.github.com/repos/scipy-conference/scipy_proceedings/commits{/sha}","git_commits_url":"https://api.github.com/repos/scipy-conference/scipy_proceedings/git/commits{/sha}","comments_url":"https://api.github.com/repos/scipy-conference/scipy_proceedings/comments{/number}","issue_comment_url":"https://api.github.com/repos/scipy-conference/scipy_proceedings/issues/comments{/number}","contents_url":"https://api.github.com/repos/scipy-conference/scipy_proceedings/contents/{+path}","compare_url":"https://api.github.com/repos/scipy-conference/scipy_proceedings/compare/{base}...{head}","merges_url":"https://api.github.com/repos/scipy-conference/scipy_proceedings/merges","archive_url":"https://api.github.com/repos/scipy-conference/scipy_proceedings/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/scipy-conference/scipy_proceedings/downloads","issues_url":"https://api.github.com/repos/scipy-conference/scipy_proceedings/issues{/number}","pulls_url":"https://api.github.com/repos/scipy-conference/scipy_proceedings/pulls{/number}","milestones_url":"https://api.github.com/repos/scipy-conference/scipy_proceedings/milestones{/number}","notifications_url":"https://api.github.com/repos/scipy-conference/scipy_proceedings/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/scipy-conference/scipy_proceedings/labels{/name}","releases_url":"https://api.github.com/repos/scipy-conference/scipy_proceedings/releases{/id}","deployments_url":"https://api.github.com/repos/scipy-conference/scipy_proceedings/deployments","created_at":"2011-05-31T10:26:48Z","updated_at":"2016-05-14T13:06:42Z","pushed_at":"2016-06-14T19:16:56Z","git_url":"git://github.com/scipy-conference/scipy_proceedings.git","ssh_url":"git@github.com:scipy-conference/scipy_proceedings.git","clone_url":"https://github.com/scipy-conference/scipy_proceedings.git","svn_url":"https://github.com/scipy-conference/scipy_proceedings","homepage":"","size":165726,"stargazers_count":87,"watchers_count":87,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":170,"mirror_url":null,"open_issues_count":25,"forks":170,"open_issues":25,"watchers":87,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/scipy-conference/scipy_proceedings/pulls/214"},"html":{"href":"https://github.com/scipy-conference/scipy_proceedings/pull/214"},"issue":{"href":"https://api.github.com/repos/scipy-conference/scipy_proceedings/issues/214"},"comments":{"href":"https://api.github.com/repos/scipy-conference/scipy_proceedings/issues/214/comments"},"review_comments":{"href":"https://api.github.com/repos/scipy-conference/scipy_proceedings/pulls/214/comments"},"review_comment":{"href":"https://api.github.com/repos/scipy-conference/scipy_proceedings/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/scipy-conference/scipy_proceedings/pulls/214/commits"},"statuses":{"href":"https://api.github.com/repos/scipy-conference/scipy_proceedings/statuses/64c1d9c255f82cca1bb2b932f4994d0e9e666fdd"}}}}
{"actor":{"display_login":"jakevdp"}}