Skip to content
Snippets Groups Projects
MetaVoxel_Tutorial.ipynb 17.8 KiB
Newer Older
  • Learn to ignore specific revisions
  • Amira Abdel-Rahman's avatar
    Amira Abdel-Rahman committed
    {
     "cells": [
      {
       "cell_type": "markdown",
       "metadata": {},
       "source": [
        "# MetaVoxel Tutorial"
       ]
      },
      {
       "cell_type": "code",
       "execution_count": 1,
       "metadata": {},
       "outputs": [],
       "source": [
        "# Amira Abdel-Rahman\n",
        "# (c) Massachusetts Institute of Technology 2020\n",
        "\n",
        "# tested using julia 1.2.0 and windows Nvidia geforce gtx 1070 Ti"
       ]
      },
      {
       "cell_type": "markdown",
       "metadata": {},
       "source": [
        "### Import Julia Libraries"
       ]
      },
      {
       "cell_type": "code",
    
       "execution_count": 23,
    
    Amira Abdel-Rahman's avatar
    Amira Abdel-Rahman committed
       "metadata": {},
    
       "outputs": [],
    
    Amira Abdel-Rahman's avatar
    Amira Abdel-Rahman committed
       "source": [
        "using LinearAlgebra\n",
        "# using Plots\n",
        "import JSON\n",
        "using StaticArrays, BenchmarkTools\n",
        "using Base.Threads\n",
        "using CUDAnative\n",
        "using CuArrays,CUDAdrv \n",
        "import Base: +, * , -, ^"
       ]
      },
      {
       "cell_type": "code",
       "execution_count": 3,
       "metadata": {},
       "outputs": [
        {
         "data": {
          "text/plain": [
           "axialStrain (generic function with 1 method)"
          ]
         },
         "execution_count": 3,
         "metadata": {},
         "output_type": "execute_result"
        }
       ],
       "source": [
        "include(\"./julia/include/vector.jl\") #utils for vectors and quaternions\n",
        "include(\"./julia/include/material.jl\") #utils for node and edge material\n",
        "include(\"./julia/include/export.jl\") #export simulation data to json\n",
        "include(\"./julia/include/run.jl\") #turn setup to cuda arrays and run simulation\n",
        "include(\"./julia/include/updateEdges.jl\") #edges properties update\n",
        "include(\"./julia/include/externalForces.jl\") #external forces applied to the system\n",
        "include(\"./julia/include/forces.jl\") #force integration\n",
        "include(\"./julia/include/updateNodes.jl\") #nodes properties update"
       ]
      },
      {
       "cell_type": "markdown",
       "metadata": {},
       "source": [
        "## 1. Voxel Design"
       ]
      },
      {
       "cell_type": "code",
    
       "execution_count": 34,
    
    Amira Abdel-Rahman's avatar
    Amira Abdel-Rahman committed
       "metadata": {},
       "outputs": [
        {
         "data": {
          "text/plain": [
           "\"tutorial\""
          ]
         },
    
         "execution_count": 34,
    
    Amira Abdel-Rahman's avatar
    Amira Abdel-Rahman committed
         "metadata": {},
         "output_type": "execute_result"
        }
       ],
       "source": [
        "# set name for simulation\n",
        "name= \"tutorial\""
       ]
      },
      {
       "cell_type": "markdown",
       "metadata": {},
       "source": [
        "### 1.a. Import lines from Rhino (.3dm)"
       ]
      },
      {
       "cell_type": "code",
       "execution_count": 5,
       "metadata": {},
       "outputs": [],
       "source": [
        "rhino=true\n",
        "\n",
        "setup = Dict()\n",
        "\n",
        "setup[\"rhino\"]=rhino\n",
        "setup[\"rhinoFileName\"]=\"./julia/examples/trial.3dm\";\n",
        "setup[\"layerIndex\"]=\"1\"; #layer index to import, only lines from these layer will get imported\n",
    
        "setup[\"voxelList\"]=false\n",
    
    Amira Abdel-Rahman's avatar
    Amira Abdel-Rahman committed
        "\n",
        "# make sure to divide curves into smaller lines, it will only add nodes at the start and end of each line/curve\n",
        "\n",
        "voxelSize=75 #in case you want to array the base rhino curve, what is the size of the voxel\n",
        "latticeSizeX=2 # if you don't want to copy/array make this 1\n",
        "latticeSizeY=2 # if you don't want to copy/array make this 1\n",
        "latticeSizeZ=2 # if you don't want to copy/array make this 1\n",
        "\n",
        "setup[\"latticeSizeX\"]=latticeSizeX\n",
        "setup[\"latticeSizeY\"]=latticeSizeY\n",
        "setup[\"latticeSizeZ\"]=latticeSizeZ\n",
        "\n",
        "gridSize=10 #lattice size\n",
        "setup[\"gridSize\"]=gridSize\n",
        "\n",
        "#scaling params\n",
        "setup[\"voxelSize\"]=voxelSize; #voxel size\n",
        "setup[\"scale\"]=1e4; #scale for visualization\n",
        "setup[\"hierarchical\"]=false; #hierachical simualtion\n"
       ]
      },
      {
       "cell_type": "markdown",
       "metadata": {},
       "source": [
        "### 1.b Draw Lattice"
       ]
      },
      {
       "cell_type": "code",
       "execution_count": 6,
       "metadata": {},
       "outputs": [],
       "source": [
        "rhino=false\n",
        "\n",
        "voxelSize=0.001\n",
        "latticeSizeX=7\n",
        "latticeSizeY=2\n",
        "latticeSizeZ=2\n",
        "\n",
        "gridSize=10\n",
        "\n",
        "setup = Dict()\n",
        "setup[\"gridSize\"]=gridSize\n",
        "\n",
        "setup[\"rhino\"]=false\n",
    
        "setup[\"voxelList\"]=false\n",
        "\n",
    
    Amira Abdel-Rahman's avatar
    Amira Abdel-Rahman committed
        "\n",
        "setup[\"latticeSizeX\"]=latticeSizeX\n",
        "setup[\"latticeSizeY\"]=latticeSizeY\n",
        "setup[\"latticeSizeZ\"]=latticeSizeZ\n",
        "\n",
        "#scaling params\n",
        "setup[\"voxelSize\"]=voxelSize; #voxel size\n",
        "setup[\"scale\"]=1e4; #scale for visualization\n",
        "setup[\"hierarchical\"]=true; #hierachical simualtion \n",
        "# if setup[\"hierarchical\"] is true it will assume each voxel is one node, \n",
        "# else it will assume each voxel is a detailed cuboct"
       ]
      },
      {
       "cell_type": "markdown",
       "metadata": {},
       "source": [
        "### 1.c. Fill mesh with voxels (wip)"
       ]
      },
      {
       "cell_type": "code",
       "execution_count": 7,
       "metadata": {},
       "outputs": [],
       "source": [
        "# rhino=false\n",
        "# rhinoFileName= \"./trial.stl\"\n",
        "# voxelSize=5.0"
       ]
      },
    
      {
       "cell_type": "markdown",
       "metadata": {},
       "source": [
        "### 1.c. Draw from voxel list"
       ]
      },
      {
       "cell_type": "code",
       "execution_count": null,
       "metadata": {},
       "outputs": [],
       "source": [
        "setup[\"useVoxelList\"]=false\n"
       ]
      },
    
    Amira Abdel-Rahman's avatar
    Amira Abdel-Rahman committed
      {
       "cell_type": "markdown",
       "metadata": {},
       "source": [
        "## 2. Boundary Conditions"
       ]
      },
      {
       "cell_type": "markdown",
       "metadata": {},
       "source": [
        "### 2.a. Global Settings"
       ]
      },
      {
       "cell_type": "code",
       "execution_count": 8,
       "metadata": {},
       "outputs": [],
       "source": [
        "#simulation params\n",
        "setup[\"numTimeSteps\"]=5000; #num of saved timesteps for simulation\n",
        "\n",
        "setup[\"poisson\"]=false; # account for poisson ration (only for hierarchical)\n",
        "setup[\"linear\"]=true; # linear vs non-linear\n",
        "setup[\"thermal\"]=true; #if there is change in temperature\n",
        "setup[\"globalDamping\"]=0.15; # (usually from 0.1 to 0.4)\n",
        "\n",
        "\n",
        "#visualization params\n",
        "setup[\"maxNumFiles\"]=200; #num of saved timesteps for visualization, make sure it's bigger than numTimeSteps\n"
       ]
      },
      {
       "cell_type": "markdown",
       "metadata": {},
       "source": [
        "### 2.b. Materials"
       ]
      },
      {
       "cell_type": "code",
       "execution_count": 9,
       "metadata": {},
       "outputs": [],
       "source": [
        "#default material\n",
        "material1= Dict()\n",
        "material1[\"area\"]=voxelSize*voxelSize\n",
        "material1[\"density\"]=1e3\n",
        "material1[\"stiffness\"]=1e6\n",
        "material1[\"poissonRatio\"]=0.0\n",
        "material1[\"cTE\"]=0.0 #coefficient of thermal expansion\n",
        "\n",
        "#large bounding box for default material\n",
        "boundingBoxMaterial1=Dict()\n",
        "boundingBoxMaterial1[\"min\"]=Dict()\n",
        "boundingBoxMaterial1[\"max\"]=Dict()\n",
        "\n",
        "boundingBoxMaterial1[\"min\"][\"x\"]=-voxelSize*gridSize;\n",
        "boundingBoxMaterial1[\"min\"][\"y\"]=-voxelSize*gridSize;\n",
        "boundingBoxMaterial1[\"min\"][\"z\"]=-voxelSize*gridSize;\n",
        "\n",
        "boundingBoxMaterial1[\"max\"][\"x\"]= voxelSize*gridSize;\n",
        "boundingBoxMaterial1[\"max\"][\"y\"]= voxelSize*gridSize;\n",
        "boundingBoxMaterial1[\"max\"][\"z\"]= voxelSize*gridSize;"
       ]
      },
      {
       "cell_type": "code",
       "execution_count": 10,
       "metadata": {},
       "outputs": [],
       "source": [
        "#second material\n",
        "material2= Dict()\n",
        "material2[\"area\"]=voxelSize*voxelSize\n",
        "material2[\"density\"]=1e3\n",
        "material2[\"stiffness\"]=1e6\n",
        "material2[\"poissonRatio\"]=0.0\n",
        "material2[\"cTE\"]=0.1 #coefficient of thermal expansion\n",
        "\n",
        "#bounding box material 2\n",
        "boundingBoxMaterial2=Dict()\n",
        "boundingBoxMaterial2[\"min\"]=Dict()\n",
        "boundingBoxMaterial2[\"max\"]=Dict()\n",
        "\n",
        "\n",
        "boundingBoxMaterial2[\"min\"][\"x\"]=0;\n",
        "boundingBoxMaterial2[\"min\"][\"y\"]=voxelSize;\n",
        "boundingBoxMaterial2[\"min\"][\"z\"]=0;\n",
        "\n",
        "boundingBoxMaterial2[\"max\"][\"x\"]= voxelSize*(latticeSizeX);\n",
        "boundingBoxMaterial2[\"max\"][\"y\"]= voxelSize*(latticeSizeY);\n",
        "boundingBoxMaterial2[\"max\"][\"z\"]= voxelSize*(latticeSizeZ);"
       ]
      },
      {
       "cell_type": "code",
       "execution_count": 11,
       "metadata": {},
       "outputs": [],
       "source": [
        "setup[\"materials\"]=[\n",
        "    [boundingBoxMaterial1,material1],\n",
        "    [boundingBoxMaterial2,material2]\n",
        "];"
       ]
      },
      {
       "cell_type": "markdown",
       "metadata": {},
       "source": [
        "### 2.c. Supports"
       ]
      },
      {
       "cell_type": "code",
       "execution_count": 12,
       "metadata": {},
       "outputs": [],
       "source": [
        "#x,y,z,rx,ry,rz (default is pinned joing i.e [false,false,false,true,true,true])\n",
        "dof=[true,true,true,true,true,true]\n",
        "\n",
        "boundingBoxSupport1=Dict()\n",
        "boundingBoxSupport1[\"min\"]=Dict()\n",
        "boundingBoxSupport1[\"max\"]=Dict()\n",
        "\n",
        "\n",
        "boundingBoxSupport1[\"min\"][\"x\"]= 0;\n",
        "boundingBoxSupport1[\"min\"][\"y\"]= 0;\n",
        "boundingBoxSupport1[\"min\"][\"z\"]= 0;\n",
        "\n",
        "boundingBoxSupport1[\"max\"][\"x\"]= voxelSize;\n",
        "boundingBoxSupport1[\"max\"][\"y\"]= voxelSize*(latticeSizeY);\n",
        "boundingBoxSupport1[\"max\"][\"z\"]= voxelSize*(latticeSizeZ);\n",
        "\n",
        "setup[\"supports\"]=[\n",
        "        [boundingBoxSupport1,dof]\n",
        "    ];"
       ]
      },
      {
       "cell_type": "markdown",
       "metadata": {},
       "source": [
        "### 2.d. Loads"
       ]
      },
      {
       "cell_type": "markdown",
       "metadata": {},
       "source": [
        "#### 2.d.1 Static Loads"
       ]
      },
      {
       "cell_type": "code",
       "execution_count": 13,
       "metadata": {},
       "outputs": [],
       "source": [
        "load1=Dict()\n",
        "load1[\"x\"]=0.0\n",
        "load1[\"y\"]=0.0\n",
        "load1[\"z\"]=0.0\n",
        "\n",
        "boundingBoxLoad1=Dict()\n",
        "boundingBoxLoad1[\"min\"]=Dict()\n",
        "boundingBoxLoad1[\"max\"]=Dict()\n",
        "\n",
        "boundingBoxLoad1[\"min\"][\"x\"]=voxelSize*(latticeSizeX-1);\n",
        "boundingBoxLoad1[\"min\"][\"y\"]=0;\n",
        "boundingBoxLoad1[\"min\"][\"z\"]=0;\n",
        "\n",
        "boundingBoxLoad1[\"max\"][\"x\"]=voxelSize*(latticeSizeX);\n",
        "boundingBoxLoad1[\"max\"][\"y\"]=voxelSize*(latticeSizeY);\n",
        "boundingBoxLoad1[\"max\"][\"z\"]=voxelSize*(latticeSizeZ);\n",
        "\n",
        "\n",
        "setup[\"loads\"]=[\n",
        "        [boundingBoxLoad1,load1]\n",
        "    ];"
       ]
      },
      {
       "cell_type": "markdown",
       "metadata": {},
       "source": [
        "#### 2.d.2 Fixed Displacements"
       ]
      },
      {
       "cell_type": "code",
       "execution_count": 14,
       "metadata": {},
       "outputs": [],
       "source": [
        "setup[\"fixedDisplacements\"]=[];"
       ]
      },
      {
       "cell_type": "markdown",
       "metadata": {},
       "source": [
        "#### 2.d.3 Dynamic Loads"
       ]
      },
      {
       "cell_type": "code",
    
       "execution_count": 73,
    
    Amira Abdel-Rahman's avatar
    Amira Abdel-Rahman committed
       "metadata": {},
       "outputs": [
        {
         "data": {
          "text/plain": [
           "updateTemperature (generic function with 1 method)"
          ]
         },
    
         "execution_count": 73,
    
    Amira Abdel-Rahman's avatar
    Amira Abdel-Rahman committed
         "metadata": {},
         "output_type": "execute_result"
        }
       ],
       "source": [
        "function floorEnabled()\n",
        "    return false\n",
        "end\n",
        "\n",
        "function gravityEnabled()\n",
        "    return false\n",
        "end\n",
        "\n",
        "function externalDisplacement(currentTimeStep,N_position,N_fixedDisplacement)\n",
        "    return N_fixedDisplacement\n",
        "end\n",
        "\n",
        "function externalForce(currentTimeStep,N_position,N_force)\n",
        "    if currentTimeStep>1000\n",
        "        return Vector3(0,0,0)\n",
        "    else\n",
        "        return N_force\n",
        "    end\n",
        "end\n",
        "\n",
        "# if no temperature:\n",
        "# function updateTemperature(currentRestLength,currentTimeStep,mat)\n",
        "#     return currentRestLength\n",
        "# end\n",
        "\n",
        "function updateTemperature(currentRestLength,currentTimeStep,mat)\n",
        "    if currentTimeStep<1000\n",
        "        temp=-5.0*currentTimeStep/1000\n",
        "        currentRestLength=0.5*mat.L*(2.0+temp*mat.cTE)\n",
        "    elseif currentTimeStep==2500\n",
        "        temp=0\n",
        "        currentRestLength=0.5*mat.L*(2.0+temp*mat.cTE)\n",
        "    end\n",
        "    return currentRestLength\n",
        "end"
       ]
      },
      {
       "cell_type": "markdown",
       "metadata": {},
       "source": [
        "## 3. Export setup to json"
       ]
      },
      {
       "cell_type": "code",
    
    Amira Abdel-Rahman's avatar
    Amira Abdel-Rahman committed
       "execution_count": 234,
    
    Amira Abdel-Rahman's avatar
    Amira Abdel-Rahman committed
       "metadata": {},
       "outputs": [
        {
         "data": {
          "text/plain": [
           "axialStrain (generic function with 1 method)"
          ]
         },
    
    Amira Abdel-Rahman's avatar
    Amira Abdel-Rahman committed
         "execution_count": 234,
    
    Amira Abdel-Rahman's avatar
    Amira Abdel-Rahman committed
         "metadata": {},
         "output_type": "execute_result"
        }
       ],
       "source": [
        "# alternativly you can get a saved setup from an external julia file\n",
    
        "# include(\"./julia/examples/thermalTest.jl\") #template for multimaterial hierarchical voxels with different thermal coefficient of thermal expansion \n",
    
    Amira Abdel-Rahman's avatar
    Amira Abdel-Rahman committed
        "# include(\"./julia/examples/poissonTest.jl\") #template for hierarchical voxels with global poisson ratio\n",
        "# include(\"./julia/examples/latticeTest.jl\") #template for lattice voxel (small scale)\n",
        "# include(\"./julia/examples/latticeTest2.jl\") #template for lattice voxel (big scale with real params)\n",
        "# include(\"./julia/examples/rhinoTest.jl\") #template for importing geometry from rhino\n",
        "# include(\"./julia/examples/rhinoTestChiral.jl\") #template for importing chiral array\n",
    
        "include(\"./julia/examples/rover.jl\") #template for importing chiral array\n",
    
    Amira Abdel-Rahman's avatar
    Amira Abdel-Rahman committed
        "\n",
        "\n",
        "## rerun these just for sanity check for dynamic loads\n",
        "include(\"./julia/include/run.jl\") #turn setup to cuda arrays and run simulation\n",
        "include(\"./julia/include/updateEdges.jl\") #edges properties update\n",
        "include(\"./julia/include/forces.jl\") #force integration\n",
        "include(\"./julia/include/updateNodes.jl\") #nodes properties update"
       ]
      },
      {
       "cell_type": "code",
    
    Amira Abdel-Rahman's avatar
    Amira Abdel-Rahman committed
       "execution_count": 235,
    
    Amira Abdel-Rahman's avatar
    Amira Abdel-Rahman committed
       "metadata": {},
       "outputs": [
        {
         "data": {
          "text/plain": [
    
           "2097"
    
    Amira Abdel-Rahman's avatar
    Amira Abdel-Rahman committed
          ]
         },
    
    Amira Abdel-Rahman's avatar
    Amira Abdel-Rahman committed
         "execution_count": 235,
    
    Amira Abdel-Rahman's avatar
    Amira Abdel-Rahman committed
         "metadata": {},
         "output_type": "execute_result"
        }
       ],
       "source": [
        "#export prev. settings to json\n",
        "fileName=\"./json/$(name)Init.json\"\n",
        "setup1=Dict()\n",
        "setup1[\"setup\"]=setup\n",
        "stringdata = JSON.json(setup1)\n",
        "open(fileName, \"w\") do f\n",
        "        write(f, stringdata)\n",
        "end"
       ]
      },
      {
       "cell_type": "code",
    
    Amira Abdel-Rahman's avatar
    Amira Abdel-Rahman committed
       "execution_count": 236,
    
    Amira Abdel-Rahman's avatar
    Amira Abdel-Rahman committed
       "metadata": {},
       "outputs": [
        {
         "name": "stdout",
         "output_type": "stream",
         "text": [
          "Loaded rhino3dm.\n",
          "Success!\n"
         ]
        },
        {
         "data": {
          "text/plain": [
           "Process(`\u001b[4mnode\u001b[24m \u001b[4mapp1.js\u001b[24m \u001b[4mtutorial\u001b[24m`, ProcessExited(0))"
          ]
         },
    
    Amira Abdel-Rahman's avatar
    Amira Abdel-Rahman committed
         "execution_count": 236,
    
    Amira Abdel-Rahman's avatar
    Amira Abdel-Rahman committed
         "metadata": {},
         "output_type": "execute_result"
        }
       ],
       "source": [
        "#run node.js to draw the gerometry using rhino3dm\n",
        "mycommand = `node app1.js $(name)`\n",
        "run(mycommand)\n"
       ]
      },
      {
       "cell_type": "markdown",
       "metadata": {},
       "source": [
        "## 4. Run Simulation"
       ]
      },
      {
       "cell_type": "code",
    
    Amira Abdel-Rahman's avatar
    Amira Abdel-Rahman committed
       "execution_count": 237,
    
    Amira Abdel-Rahman's avatar
    Amira Abdel-Rahman committed
       "metadata": {},
       "outputs": [
        {
         "name": "stdout",
         "output_type": "stream",
         "text": [
    
          "dt: 5.032921456503956e-6\n",
    
    Amira Abdel-Rahman's avatar
    Amira Abdel-Rahman committed
          "first timestep took 29.999862 seconds\n",
          "ran 17 nodes and 16 edges for 10000 time steps took 8.470833901 seconds\n"
    
    Amira Abdel-Rahman's avatar
    Amira Abdel-Rahman committed
         ]
        }
       ],
       "source": [
        "folderPath=\"./json/tutorial/\" # make sure this folder exists\n",
        "setupSim=getSetup(name);\n",
        "runMetavoxelGPULive!(setupSim,folderPath)"
       ]
      },
      {
       "cell_type": "markdown",
       "metadata": {},
       "source": [
        "## 5. Visualize \n",
        "(only need to run it once to open the server then press stop, the server will keep running and other changes will update automatically.. will change later)"
       ]
      },
      {
       "cell_type": "code",
    
       "execution_count": 49,
    
    Amira Abdel-Rahman's avatar
    Amira Abdel-Rahman committed
       "metadata": {},
       "outputs": [
        {
         "name": "stdout",
         "output_type": "stream",
         "text": [
          "Server listening on port 8080\n",
          "Open http://localhost:8080/demos/indexTutorial.html in your browser\n"
         ]
        },
        {
    
         "ename": "InterruptException",
         "evalue": "InterruptException:",
    
    Amira Abdel-Rahman's avatar
    Amira Abdel-Rahman committed
         "output_type": "error",
         "traceback": [
    
          "InterruptException:",
    
    Amira Abdel-Rahman's avatar
    Amira Abdel-Rahman committed
          "",
          "Stacktrace:",
    
          " [1] try_yieldto(::typeof(Base.ensure_rescheduled), ::Base.RefValue{Task}) at .\\task.jl:517",
          " [2] wait() at .\\task.jl:592",
          " [3] wait(::Base.GenericCondition{Base.AlwaysLockedST}) at .\\condition.jl:104",
          " [4] stream_wait(::Base.Process, ::Base.GenericCondition{Base.AlwaysLockedST}) at .\\stream.jl:47",
          " [5] wait at .\\process.jl:956 [inlined]",
          " [6] success at .\\process.jl:771 [inlined]",
          " [7] #run#536(::Bool, ::typeof(run), ::Cmd) at .\\process.jl:728",
          " [8] run(::Cmd) at .\\process.jl:726",
          " [9] top-level scope at In[49]:3"
    
    Amira Abdel-Rahman's avatar
    Amira Abdel-Rahman committed
         ]
        }
       ],
       "source": [
        "#run node.js to serve the indexTutorial.html for visualizarion\n",
        "mycommand = `node serve.js $(name)`\n",
        "run(mycommand)\n",
        "\n",
        "# vis 1 stable\n",
        "# http://localhost:8080/demos/indexTutorial.html\n",
        "\n",
        "# vis 2 faster for larger simulations\n",
        "# http://localhost:8080/demos/indexTutorialGraph.html\n",
        "\n",
        "# vis 3 (GPU Shaders) even faster (max 40 timesteps)"
       ]
      },
      {
       "cell_type": "code",
       "execution_count": null,
       "metadata": {},
       "outputs": [],
       "source": []
      }
     ],
     "metadata": {
      "@webio": {
       "lastCommId": null,
       "lastKernelId": null
      },
      "kernelspec": {
       "display_name": "Julia 1.2.0",
       "language": "julia",
       "name": "julia-1.2"
      },
      "language_info": {
       "file_extension": ".jl",
       "mimetype": "application/julia",
       "name": "julia",
       "version": "1.2.0"
      }
     },
     "nbformat": 4,
     "nbformat_minor": 4
    }