{
 "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": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "┌ Warning: `haskey(::TargetIterator, name::String)` is deprecated, use `Target(; name=name) !== nothing` instead.\n",
      "│   caller = llvm_support(::VersionNumber) at CUDAnative.jl:158\n",
      "└ @ CUDAnative C:\\Users\\amira\\.julia\\packages\\CUDAnative\\Phjco\\src\\CUDAnative.jl:158\n"
     ]
    }
   ],
   "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": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "\"tutorial\""
      ]
     },
     "execution_count": 4,
     "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",
    "\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",
    "\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": [
    "## 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": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "updateTemperature (generic function with 1 method)"
      ]
     },
     "execution_count": 15,
     "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",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "axialStrain (generic function with 1 method)"
      ]
     },
     "execution_count": 16,
     "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",
    "# 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",
    "\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",
   "execution_count": 1339,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "816"
      ]
     },
     "execution_count": 1339,
     "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",
   "execution_count": 1340,
   "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))"
      ]
     },
     "execution_count": 1340,
     "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",
   "execution_count": 1341,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "dt: 5.032921456503954e-6\n",
      "first timestep took 35.939026399 seconds\n",
      "ran 28 nodes and 52 edges for 5000 time steps took 6.7358468 seconds\n"
     ]
    }
   ],
   "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": 440,
   "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"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "events.js:288\r\n",
      "      throw er; // Unhandled 'error' event\r\n",
      "      ^\r\n",
      "\r\n",
      "Error: listen EADDRINUSE: address already in use :::8080\r\n",
      "    at Server.setupListenHandle [as _listen2] (net.js:1309:16)\r\n",
      "    at listenInCluster (net.js:1357:12)\r\n",
      "    at Server.listen (net.js:1445:7)\r\n",
      "    at Object.<anonymous> (C:\\Users\\amira\\Dropbox (MIT)\\CBA\\MetaVoxels\\metavoxels\\01_Code\\191115_NodeJsJulia\\serve.js:20:8)\r\n",
      "    at Module._compile (internal/modules/cjs/loader.js:1158:30)\r\n",
      "    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1178:10)\r\n",
      "    at Module.load (internal/modules/cjs/loader.js:1002:32)\r\n",
      "    at Function.Module._load (internal/modules/cjs/loader.js:901:14)\r\n",
      "    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:74:12)\r\n",
      "    at internal/main/run_main_module.js:18:47\r\n",
      "Emitted 'error' event on Server instance at:\r\n",
      "    at emitErrorNT (net.js:1336:8)\r\n",
      "    at processTicksAndRejections (internal/process/task_queues.js:84:21) {\r\n",
      "  code: 'EADDRINUSE',\r\n",
      "  errno: 'EADDRINUSE',\r\n",
      "  syscall: 'listen',\r\n",
      "  address: '::',\r\n",
      "  port: 8080\r\n",
      "}\r\n"
     ]
    },
    {
     "ename": "ProcessFailedException",
     "evalue": "failed process: Process(`node serve.js tutorial`, ProcessExited(1)) [1]\n",
     "output_type": "error",
     "traceback": [
      "failed process: Process(`node serve.js tutorial`, ProcessExited(1)) [1]\n",
      "",
      "Stacktrace:",
      " [1] pipeline_error at .\\process.jl:813 [inlined]",
      " [2] #run#536(::Bool, ::typeof(run), ::Cmd) at .\\process.jl:728",
      " [3] run(::Cmd) at .\\process.jl:726",
      " [4] top-level scope at In[440]:3"
     ]
    }
   ],
   "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
}