{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 2d contacts\n",
    "\n",
    "Author: Jukka Aho\n",
    "\n",
    "Abstract: 2d tie contact.\n",
    "\n",
    "## Model 1: three body tie contact\n",
    "\n",
    "\n",
    "![model](http://s4.postimg.org/u9yqeryul/Screenshot_from_2015_11_24_02_00_00.png)\n",
    "\n",
    "Each element is modelled as own \"body\" and they are connected using tie contacts. Segments 5-6 and 9-10 and 6-7 are slave surfaces, so node 6 or 9 is on at least two tie contacts as slave node. Moreover this model has dirichlet boundary $y=0$ at bottom of body 1 and $x=0$ on left. To get the accurate solution one needs to minimize \n",
    "\\begin{equation}\n",
    "\\frac{15}{2}u_1^4 + 60 u_1^3 + \\frac{15}{4}u_1^2 u_2^2 + 15 u_1^2 u_2 + 120 u_1^2 + 15 u_1 u_2^2 + 60 u_1 u_2 + \\frac{15}{2}u_2^4 + 60 u_2^3 + 120 u_2^2 + 50 u_2,\n",
    "\\end{equation}\n",
    "which gives approximate $u_1 = 0.0634862$ and $u_2 = -0.277183$ for the displacement of upper right corner.\n",
    "[Wolfram](http://www.wolframalpha.com/input/?i=local+minimum+15*x^4%2F2+%2B+60*x^3+%2B+15*x^2*y^2%2F4+%2B+15*x^2*y+%2B+120*x^2+%2B+15*x*y^2+%2B+60*x*y+%2B+15*y^4%2F2+%2B+60*y^3+%2B+120*y^2+%2B+50*y)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "using JuliaFEM.Core: Element, Seg2, Quad4, PlaneStressElasticityProblem,\n",
    "                     DirichletProblem, MortarProblem, DirectSolver"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "nodes = Dict{Int64, Vector{Float64}}(\n",
    "    1 => [0.0, 0.0], 2 => [2.0, 0.0],\n",
    "    3 => [2.0, 1.0], 4 => [0.0, 1.0],\n",
    "    5 => [0.0, 1.0], 6 => [1.0, 1.0],\n",
    "    7 => [1.0, 2.0], 8 => [0.0, 2.0],\n",
    "    9 => [1.0, 1.0], 10 => [2.0, 1.0],\n",
    "    11 => [2.0, 2.0], 12 => [1.0, 2.0]);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "connectivity = Dict{Int64, Vector{Int64}}(\n",
    "    1 => [1, 2, 3, 4],\n",
    "    2 => [5, 6, 7, 8],\n",
    "    3 => [9, 10, 11, 12]);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO: number of elements: 3\n"
     ]
    }
   ],
   "source": [
    "elements = Element[]\n",
    "for c in values(connectivity)\n",
    "    element = Quad4(c)\n",
    "    element[\"geometry\"] = Vector{Float64}[nodes[i] for i in c]\n",
    "    element[\"youngs modulus\"] = 900.0\n",
    "    element[\"poissons ratio\"] = 0.25\n",
    "    push!(elements, element)\n",
    "end\n",
    "nelements = length(elements)\n",
    "info(\"number of elements: $nelements\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Create three bodies, each containing one element."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "body1 = PlaneStressElasticityProblem(\"body 1\")\n",
    "body2 = PlaneStressElasticityProblem(\"body 2\")\n",
    "body3 = PlaneStressElasticityProblem(\"body 3\")\n",
    "push!(body1, elements[1])\n",
    "push!(body2, elements[2])\n",
    "push!(body3, elements[3]);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Surface traction to the top of bodies 2 and 3:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "t2 = Seg2([8, 7])\n",
    "t2[\"geometry\"] = Vector{Float64}[nodes[8], nodes[7]]\n",
    "t2[\"displacement traction force\"] = Vector{Float64}[[0.0, -100.0], [0.0, -100.0]]\n",
    "t3 = Seg2([12, 11])\n",
    "t3[\"geometry\"] = Vector{Float64}[nodes[12], nodes[11]]\n",
    "t3[\"displacement traction force\"] = Vector{Float64}[[0.0, -100.0], [0.0, -100.0]]\n",
    "push!(body2, t2)\n",
    "push!(body3, t3);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Boundary conditions: $x=0$ for left boundary."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "dx1 = Seg2([1, 4])\n",
    "dx1[\"geometry\"] = Vector[nodes[1], nodes[4]]\n",
    "dx1[\"displacement 1\"] = 0.0\n",
    "dx2 = Seg2([5, 8])\n",
    "dx2[\"geometry\"] = Vector[nodes[5], nodes[8]]\n",
    "dx2[\"displacement 1\"] = 0.0\n",
    "bc1 = DirichletProblem(\"displacement\", 2)\n",
    "push!(bc1, dx1)\n",
    "push!(bc1, dx2);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "$y=0$ for bottom of model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "dy1 = Seg2([1, 2])\n",
    "dy1[\"geometry\"] = Vector[nodes[1], nodes[2]]\n",
    "dy1[\"displacement 2\"] = 0.0\n",
    "bc2 = DirichletProblem(\"displacement\", 2)\n",
    "push!(bc2, dy1);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Mortar boundary conditions: tie contact between body 1 and body 2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "#rotation_matrix(phi) = [cos(phi) -sin(phi); sin(phi) cos(phi)]\n",
    "using JuliaFEM.Core: calculate_normal_tangential_coordinates!\n",
    "\n",
    "master1 = Seg2([4, 3])\n",
    "master1[\"geometry\"] = Vector[nodes[4], nodes[3]]\n",
    "slave1 = Seg2([5, 6])\n",
    "slave1[\"geometry\"] = Vector[nodes[5], nodes[6]]\n",
    "slave1[\"master elements\"] = Element[master1]\n",
    "calculate_normal_tangential_coordinates!(slave1, 0.0)\n",
    "\n",
    "#slave1[\"nodal ntsys\"] = Matrix[rotation_matrix(-pi/2), rotation_matrix(-pi/2)]\n",
    "contact1 = MortarProblem(\"displacement\", 2)\n",
    "push!(contact1, slave1);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Tie contact between body 1 and body 3"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "slave2 = Seg2([9, 10])\n",
    "slave2[\"geometry\"] = Vector[nodes[9], nodes[10]]\n",
    "#slave2[\"nodal ntsys\"] = Matrix[rotation_matrix(-pi/2), rotation_matrix(-pi/2)]\n",
    "slave2[\"master elements\"] = Element[master1]\n",
    "calculate_normal_tangential_coordinates!(slave2, 0.0)\n",
    "contact2 = MortarProblem(\"displacement\", 2)\n",
    "push!(contact2, slave2);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Tie contact between body 2 and body 3"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "master2 = Seg2([6, 7])\n",
    "master2[\"geometry\"] = Vector[nodes[6], nodes[7]]\n",
    "slave3 = Seg2([9, 12])\n",
    "slave3[\"geometry\"] = Vector[nodes[9], nodes[12]]\n",
    "#slave3[\"nodal ntsys\"] = Matrix[rotation_matrix(0.0), rotation_matrix(0.0)]\n",
    "calculate_normal_tangential_coordinates!(slave3, 0.0)\n",
    "slave3[\"master elements\"] = Element[master2]\n",
    "contact3 = MortarProblem(\"displacement\", 2)\n",
    "push!(contact3, slave3);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "All defined. Solve it."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "solver = DirectSolver()\n",
    "push!(solver, body1)\n",
    "push!(solver, body2)\n",
    "push!(solver, body3)\n",
    "push!(solver, bc1)\n",
    "push!(solver, bc2)\n",
    "push!(solver, contact1)\n",
    "push!(solver, contact2)\n",
    "push!(solver, contact3);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO: Starting solver DirectSolver\n",
      "INFO: # of field problems: 3\n",
      "INFO: # of boundary problems: 5\n",
      "INFO: Starting iteration 1\n",
      "INFO: Assembling field problems...\n",
      "INFO: Assembling body 1: body 1\n",
      "INFO: Assembling body 2: body 2\n",
      "INFO: Assembling body 3: body 3\n",
      "INFO: dim = 24\n",
      "INFO: Assembling boundary problems...\n",
      "INFO: Assembling boundary 1: dirichlet boundary\n",
      "INFO: Assembling boundary 2: dirichlet boundary\n",
      "INFO: Assembling boundary 3: mortar problem\n",
      "INFO: Assembling boundary 4: mortar problem\n",
      "INFO: Assembling boundary 5: mortar problem\n",
      "INFO: Solving system\n",
      "INFO: UMFPACK: solved in 0.3197059631347656 seconds. norm = 0.5357583756107197\n",
      "INFO: timing info for iteration:\n",
      "INFO: boundary assembly       : 0.3747282028198242\n",
      "INFO: field assembly          : 2.646785020828247\n",
      "INFO: dump matrices to disk   : 9.5367431640625e-7\n",
      "INFO: solve problem           : 0.4615659713745117\n",
      "INFO: update element data     : 0.02040410041809082\n",
      "INFO: non-linear iteration    : 3.5035040378570557\n",
      "INFO: Starting iteration 2\n",
      "INFO: Assembling field problems...\n",
      "INFO: Assembling body 1: body 1\n",
      "INFO: Assembling body 2: body 2\n",
      "INFO: Assembling body 3: body 3\n",
      "INFO: dim = 24\n",
      "INFO: Assembling boundary problems...\n",
      "INFO: Assembling boundary 1: dirichlet boundary\n",
      "INFO: Assembling boundary 2: dirichlet boundary\n",
      "INFO: Assembling boundary 3: mortar problem\n",
      "INFO: Assembling boundary 4: mortar problem\n",
      "INFO: Assembling boundary 5: mortar problem\n",
      "INFO: Solving system\n",
      "INFO: UMFPACK: solved in 0.0003139972686767578 seconds. norm = 0.12311066326855769\n",
      "INFO: timing info for iteration:\n",
      "INFO: boundary assembly       : 0.036119937896728516\n",
      "INFO: field assembly          : 0.0374150276184082\n",
      "INFO: dump matrices to disk   : 9.5367431640625e-7\n",
      "INFO: solve problem           : 0.06756091117858887\n",
      "INFO: update element data     : 0.00013899803161621094\n",
      "INFO: non-linear iteration    : 0.1412510871887207\n",
      "INFO: Starting iteration 3\n",
      "INFO: Assembling field problems...\n",
      "INFO: Assembling body 1: body 1\n",
      "INFO: Assembling body 2: body 2\n",
      "INFO: Assembling body 3: body 3\n",
      "INFO: dim = 24\n",
      "INFO: Assembling boundary problems...\n",
      "INFO: Assembling boundary 1: dirichlet boundary\n",
      "INFO: Assembling boundary 2: dirichlet boundary\n",
      "INFO: Assembling boundary 3: mortar problem\n",
      "INFO: Assembling boundary 4: mortar problem\n",
      "INFO: Assembling boundary 5: mortar problem\n",
      "INFO: Solving system\n",
      "INFO: UMFPACK: solved in 0.0003230571746826172 seconds. norm = 0.006976385449837204\n",
      "INFO: timing info for iteration:\n",
      "INFO: boundary assembly       : 0.03788185119628906\n",
      "INFO: field assembly          : 0.036910057067871094\n",
      "INFO: dump matrices to disk   : 0.0\n",
      "INFO: solve problem           : 0.07133316993713379\n",
      "INFO: update element data     : 0.0001468658447265625\n",
      "INFO: non-linear iteration    : 0.1462879180908203\n",
      "INFO: Starting iteration 4\n",
      "INFO: Assembling field problems...\n",
      "INFO: Assembling body 1: body 1\n",
      "INFO: Assembling body 2: body 2\n",
      "INFO: Assembling body 3: body 3\n",
      "INFO: dim = 24\n",
      "INFO: Assembling boundary problems...\n",
      "INFO: Assembling boundary 1: dirichlet boundary\n",
      "INFO: Assembling boundary 2: dirichlet boundary\n",
      "INFO: Assembling boundary 3: mortar problem\n",
      "INFO: Assembling boundary 4: mortar problem\n",
      "INFO: Assembling boundary 5: mortar problem\n",
      "INFO: Solving system\n",
      "INFO: UMFPACK: solved in 0.00030493736267089844 seconds. norm = 2.1945519744339283e-5\n",
      "INFO: timing info for iteration:\n",
      "INFO: boundary assembly       : 0.037918806076049805\n",
      "INFO: field assembly          : 0.036936044692993164\n",
      "INFO: dump matrices to disk   : 9.5367431640625e-7\n",
      "INFO: solve problem           : 0.07027888298034668\n",
      "INFO: update element data     : 0.00013899803161621094\n",
      "INFO: non-linear iteration    : 0.14528894424438477\n",
      "INFO: Starting iteration 5\n",
      "INFO: Assembling field problems...\n",
      "INFO: Assembling body 1: body 1\n",
      "INFO: Assembling body 2: body 2\n",
      "INFO: Assembling body 3: body 3\n",
      "INFO: dim = 24\n",
      "INFO: Assembling boundary problems...\n",
      "INFO: Assembling boundary 1: dirichlet boundary\n",
      "INFO: Assembling boundary 2: dirichlet boundary\n",
      "INFO: Assembling boundary 3: mortar problem\n",
      "INFO: Assembling boundary 4: mortar problem\n",
      "INFO: Assembling boundary 5: mortar problem\n",
      "INFO: Solving system\n",
      "INFO: UMFPACK: solved in 0.0003120899200439453 seconds. norm = 2.172141514107444e-10\n",
      "INFO: timing info for iteration:\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "(5,true)"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO: boundary assembly       : 0.035440921783447266\n",
      "INFO: field assembly          : 0.0363919734954834\n",
      "INFO: dump matrices to disk   : 0.0\n",
      "INFO: solve problem           : 0.06740903854370117\n",
      "INFO: update element data     : 0.0001380443572998047\n",
      "INFO: non-linear iteration    : 0.13939404487609863\n",
      "INFO: solver finished in 4.193101167678833 seconds.\n"
     ]
    }
   ],
   "source": [
    "iterations, converged = call(solver, 0.0)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO: displacement at [2.0,2.0] = [0.06348623177789363,-0.2771830378556528]\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "Test Passed\n",
       "  Expression: isapprox(u,[0.0634862,-0.277183],atol=1.0e-5)"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "using JuliaFEM.Test\n",
    "\n",
    "@test converged\n",
    "\n",
    "X = elements[2](\"geometry\", [1.0, 1.0], 0.0)\n",
    "u = elements[2](\"displacement\", [1.0, 1.0], 0.0)\n",
    "info(\"displacement at $X = $u\")\n",
    "@test isapprox(u, [0.0634862, -0.277183], atol=1.0e-5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Model 2: splitted beam, tie contact\n",
    "\n",
    "<img src=\"http://results.juliafem.org/splitted-2d-beam/2015-12-22-splitted-beam-mesh.png\">\n",
    "\n",
    "Put some load on the top, dx=dy=0 on left boundary."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "using JuliaFEM.Preprocess: parse_aster_med_file\n",
    "using JuliaFEM.Core: PlaneStressLinearElasticityProblem, DirichletProblem,\n",
    "                     get_connectivity, Quad4, Tri3, Seg2, LinearSolver,\n",
    "                     update!, get_elements"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "create_problems (generic function with 1 method)"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "using JuliaFEM.Core: Element, MortarProblem, calculate_normal_tangential_coordinates!\n",
    "\n",
    "phi = 0\n",
    "rmat = [cos(phi) -sin(phi); sin(phi) cos(phi)]\n",
    "\n",
    "function create_problems()\n",
    "\n",
    "    mesh = parse_aster_med_file(Pkg.dir(\"JuliaFEM\")*\"/geometry/2d_beam/BEAM.med\")\n",
    "    \n",
    "    for (k, v) in mesh[\"nodes\"]\n",
    "        mesh[\"nodes\"][k] = rmat*v\n",
    "    end\n",
    "    \n",
    "    field_problem = PlaneStressLinearElasticityProblem()\n",
    "\n",
    "    # field problems\n",
    "    joo = Dict(:QU4 => Quad4, :TR3 => Tri3)\n",
    "    for (elid, (eltype, elset, elcon)) in mesh[\"connectivity\"]\n",
    "        eltype in keys(joo) || continue\n",
    "        element = joo[eltype](elcon)\n",
    "        update!(element, \"geometry\", mesh[\"nodes\"])\n",
    "        element[\"youngs modulus\"] = 900.0\n",
    "        element[\"poissons ratio\"] = 0.25\n",
    "        push!(field_problem, element)\n",
    "    end\n",
    "\n",
    "    # neumann boundary condition -1 on y direction\n",
    "    for (elid, (eltype, elset, elcon)) in mesh[\"connectivity\"]\n",
    "        eltype == :SE2 || continue\n",
    "        elset == :LOAD || continue\n",
    "        element = Seg2(elcon)\n",
    "        update!(element, \"geometry\", mesh[\"nodes\"])\n",
    "        # FIXME\n",
    "        # element[\"displacement traction force\"] = rmat*[0.0, -0.01]\n",
    "        f = rmat*[0.0, -0.01]\n",
    "        element[\"displacement traction force\"] = Vector{Float64}[f, f]\n",
    "        push!(field_problem, element)\n",
    "    end\n",
    "\n",
    "    # boundary conditions\n",
    "    boundary_problem = DirichletProblem(\"displacement\", 2)\n",
    "\n",
    "    for (elid, (eltype, elset, elcon)) in mesh[\"connectivity\"]\n",
    "        eltype == :SE2 || continue\n",
    "        elset == :LEFT || continue\n",
    "        element = Seg2(elcon)\n",
    "        update!(element, \"geometry\", mesh[\"nodes\"])\n",
    "        # FIXME\n",
    "        element[\"displacement\"] = (0.0 => Vector{Float64}[[0.0, 0.0], [0.0, 0.0]])\n",
    "        push!(boundary_problem, element)\n",
    "    end\n",
    "\n",
    "    info(\"created $(length(get_elements(field_problem))) field elements.\")\n",
    "    info(\"created $(length(get_elements(boundary_problem))) boundary elements.\")\n",
    "\n",
    "    # Contact definition: contact pair is `LOWER_TO_UPPER <--> UPPER_TO_LOWER`:\n",
    "\n",
    "    slave_surface = :LOWER_TO_UPPER\n",
    "    master_surface = :UPPER_TO_LOWER\n",
    "\n",
    "    contact_problem = MortarProblem(\"displacement\", 2)\n",
    "    master_elements = JuliaFEM.Core.Element[]\n",
    "    for (elid, (eltype, elset, elcon)) in mesh[\"connectivity\"]\n",
    "        eltype == :SE2 || continue\n",
    "        elset == master_surface || continue\n",
    "        element = Seg2(elcon)\n",
    "        update!(element, \"geometry\", mesh[\"nodes\"])\n",
    "        element[\"displacement\"] = (0.0 => Vector{Float64}[[0.0, 0.0], [0.0, 0.0]])\n",
    "        push!(master_elements, element)\n",
    "        push!(contact_problem, element)\n",
    "    end\n",
    "\n",
    "    for (elid, (eltype, elset, elcon)) in mesh[\"connectivity\"]\n",
    "        eltype == :SE2 || continue\n",
    "        elset == slave_surface || continue\n",
    "        element = Seg2(elcon)\n",
    "        update!(element, \"geometry\", mesh[\"nodes\"])\n",
    "        element[\"master elements\"] = master_elements\n",
    "        calculate_normal_tangential_coordinates!(element, 0.0)\n",
    "        element[\"displacement\"] = (0.0 => Vector{Float64}[[0.0, 0.0], [0.0, 0.0]])\n",
    "        push!(contact_problem, element)\n",
    "    end\n",
    "\n",
    "    info(\"# of master elements: $(length(master_elements))\")\n",
    "    info(\"# of slave elements: $(length(contact_problem.elements))\")\n",
    "\n",
    "    return field_problem, boundary_problem, contact_problem\n",
    "\n",
    "end"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO: Found 5 element sets: LOWER_TO_UPPER, RIGHT, UPPER_TO_LOWER, LEFT, LOAD\n",
      "INFO: created 104 field elements.\n",
      "INFO: created 4 boundary elements.\n",
      "INFO: # of master elements: 14\n",
      "INFO: # of slave elements: 34\n",
      "INFO: Starting solver divided_beam\n",
      "INFO: # of field problems: 1\n",
      "INFO: # of boundary problems: 2\n",
      "INFO: Starting iteration 1\n",
      "INFO: Assembling field problems...\n",
      "INFO: Assembling body 1: plane stress linear elasticity\n",
      "INFO: Assembly: 10.0 % done. \n",
      "INFO: Assembly: 20.0 % done. \n",
      "INFO: Assembly: 30.0 % done. \n",
      "INFO: Assembly: 40.0 % done. \n",
      "INFO: Assembly: 50.0 % done. \n",
      "INFO: Assembly: 60.0 % done. \n",
      "INFO: Assembly: 70.0 % done. \n",
      "INFO: Assembly: 80.0 % done. \n",
      "INFO: Assembly: 90.0 % done. \n",
      "INFO: Assembly: 100.0 % done. \n",
      "INFO: dim = 210\n",
      "INFO: Assembling boundary problems...\n",
      "INFO: Assembling boundary 1: dirichlet boundary\n",
      "INFO: Assembling boundary 2: mortar problem\n",
      "INFO: Solving system\n",
      "INFO: UMFPACK: solved in 0.3899998664855957 seconds. norm = 1.049388621344361\n",
      "INFO: timing info for iteration:\n",
      "INFO: boundary assembly       : 2.3869998455047607\n",
      "INFO: field assembly          : 1.4670000076293945\n",
      "INFO: dump matrices to disk   : 0.0\n",
      "INFO: solve problem           : 0.49900007247924805\n",
      "INFO: update element data     : 0.014999866485595703\n",
      "INFO: non-linear iteration    : 4.367999792098999\n",
      "INFO: Starting iteration 2\n",
      "INFO: Assembling field problems...\n",
      "INFO: Assembling body 1: plane stress linear elasticity\n",
      "INFO: Assembly: 10.0 % done. \n",
      "INFO: Assembly: 20.0 % done. \n",
      "INFO: Assembly: 30.0 % done. \n",
      "INFO: Assembly: 40.0 % done. \n",
      "INFO: Assembly: 50.0 % done. \n",
      "INFO: Assembly: 60.0 % done. \n",
      "INFO: Assembly: 70.0 % done. \n",
      "INFO: Assembly: 80.0 % done. \n",
      "INFO: Assembly: 90.0 % done. \n",
      "INFO: Assembly: 100.0 % done. \n",
      "INFO: dim = 210\n",
      "INFO: Assembling boundary problems...\n",
      "INFO: Assembling boundary 1: dirichlet boundary\n",
      "INFO: Assembling boundary 2: mortar problem\n",
      "INFO: Solving system\n",
      "INFO: UMFPACK: solved in 0.0 seconds. norm = 1.8501897782230326e-13\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "(2,true)"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "using JuliaFEM.Core: DirectSolver\n",
    "\n",
    "field_problem, boundary_problem, contact_problem = create_problems()\n",
    "\n",
    "solver = DirectSolver()\n",
    "solver.name = \"divided_beam\"\n",
    "solver.method = :UMFPACK\n",
    "solver.max_iterations = 2\n",
    "solver.dump_matrices = false\n",
    "push!(solver, field_problem)\n",
    "push!(solver, boundary_problem)\n",
    "push!(solver, contact_problem)\n",
    "call(solver, 0.0)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "import PyPlot"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO: timing info for iteration:\n",
      "INFO: boundary assembly       : 0.10899996757507324\n",
      "INFO: field assembly          : 0.04700016975402832\n",
      "INFO: dump matrices to disk   : 0.0\n",
      "INFO: solve problem           : 0.06200003623962402\n",
      "INFO: update element data     : 0.015999794006347656\n",
      "INFO: non-linear iteration    : 0.23399996757507324\n",
      "INFO: solver finished in 4.741999864578247 seconds.\n",
      "INFO: displacement at tip: [-0.025032650050967196,-0.1947753325658252]\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 143420 characters]",
      "text/plain": [
       "PyPlot.Figure(PyObject <matplotlib.figure.Figure object at 0x000000002D7D0A58>)"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "function plot(field_problem, scaling_factor=30, time=0.0)\n",
    "\n",
    "    fig = PyPlot.figure(figsize=(15, 4))\n",
    "\n",
    "    g_tip = rmat*[100.0, 0.0]\n",
    "    u_tip = nothing\n",
    "    \n",
    "    for element in field_problem.elements\n",
    "        conn = get_connectivity(element)\n",
    "        X = element(\"geometry\", time)\n",
    "        u = element(\"displacement\", time)\n",
    "        x = X + scaling_factor*u\n",
    "        if isapprox(element(\"geometry\", [1.0, -1.0], time), g_tip)\n",
    "            u_tip = element(\"displacement\", [1.0, -1.0], time)\n",
    "            info(\"displacement at tip: $u_tip\")\n",
    "        end\n",
    "\n",
    "        # undeformed\n",
    "        for i=1:length(X)\n",
    "            px1 = X[i][1]\n",
    "            py1 = X[i][2]\n",
    "            px2 = X[mod(i,length(X))+1][1]\n",
    "            py2 = X[mod(i,length(X))+1][2]\n",
    "            PyPlot.plot([px1, px2], [py1, py2], \"k-\", alpha=0.5)\n",
    "        end\n",
    "\n",
    "        # deformed\n",
    "        for i=1:length(x)\n",
    "            px1 = x[i][1]\n",
    "            py1 = x[i][2]\n",
    "            px2 = x[mod(i,length(x))+1][1]\n",
    "            py2 = x[mod(i,length(x))+1][2]\n",
    "            PyPlot.plot([px1, px2], [py1, py2], \"r--\", alpha=0.5)\n",
    "        end\n",
    "    end\n",
    "\n",
    "    PyPlot.axis(\"equal\")\n",
    "    #PyPlot.axis(\"off\")\n",
    "\n",
    "    return u_tip\n",
    "end\n",
    "\n",
    "u_tip = plot(field_problem, 30.0)\n",
    "#PyPlot.xlim(95, 105)\n",
    "#PyPlot.ylim(-10, 0)\n",
    "PyPlot.grid()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "true"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "isapprox(u_tip, [-0.025032650050963334,-0.19477533256579582]) || warn(\"different result\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "true"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "isapprox(norm(u_tip), 0.19637735038616433)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Model 3: splitted beam, frictionless contact, small sliding\n",
    "\n",
    "Modification to the above: allow beams to slide in tangential direction without a friction. Global assembly operator can be modified by writing own functions `preprocess_assembly!` and `postprocess_assembly!`. Here we simply remove all kinematic constraints in tangential direction to achieve frictionless sliding between bodies."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO: Found 5 element sets: LOWER_TO_UPPER, RIGHT, UPPER_TO_LOWER, LEFT, LOAD\n",
      "INFO: created 104 field elements.\n",
      "INFO: created 4 boundary elements.\n",
      "INFO: # of master elements: 14\n",
      "INFO: # of slave elements: 34\n",
      "INFO: Starting solver divided_beam_small_sliding_contact\n",
      "INFO: # of field problems: 1\n",
      "INFO: # of boundary problems: 2\n",
      "INFO: Starting iteration 1\n",
      "INFO: Assembling field problems...\n",
      "INFO: Assembling body 1: plane stress linear elasticity\n",
      "INFO: Assembly: 10.0 % done. \n",
      "INFO: Assembly: 20.0 % done. \n",
      "INFO: Assembly: 30.0 % done. \n",
      "INFO: Assembly: 40.0 % done. \n",
      "INFO: Assembly: 50.0 % done. \n",
      "INFO: Assembly: 60.0 % done. \n",
      "INFO: Assembly: 70.0 % done. \n",
      "INFO: Assembly: 80.0 % done. \n",
      "INFO: Assembly: 90.0 % done. \n",
      "INFO: Assembly: 100.0 % done. \n",
      "INFO: dim = 210\n",
      "INFO: Assembling boundary problems...\n",
      "INFO: Assembling boundary 1: dirichlet boundary\n",
      "INFO: Assembling boundary 2: mortar problem\n",
      "INFO: assemble: doing postprocess for problem JuliaFEM.Core.BoundaryProblem{JuliaFEM.Core.MortarProblem} assembly\n",
      "INFO: postprocess mortar assembly: remove contraints in tangent direction on boundary.\n",
      "INFO: postprocess mortar assembly: done.\n",
      "INFO: Solving system\n",
      "INFO: UMFPACK: solved in 0.0 seconds. norm = 3.146559185661418\n",
      "INFO: timing info for iteration:\n",
      "INFO: boundary assembly       : 0.7179999351501465\n",
      "INFO: field assembly          : 0.07800006866455078\n",
      "INFO: dump matrices to disk   : 0.0\n",
      "INFO: solve problem           : 0.09299993515014648\n",
      "INFO: update element data     : 0.0\n",
      "INFO: non-linear iteration    : 0.8889999389648438\n",
      "INFO: Starting iteration 2\n",
      "INFO: Assembling field problems...\n",
      "INFO: Assembling body 1: plane stress linear elasticity\n",
      "INFO: Assembly: 10.0 % done. \n",
      "INFO: Assembly: 20.0 % done. \n",
      "INFO: Assembly: 30.0 % done. \n",
      "INFO: Assembly: 40.0 % done. \n",
      "INFO: Assembly: 50.0 % done. \n",
      "INFO: Assembly: 60.0 % done. \n",
      "INFO: Assembly: 70.0 % done. \n",
      "INFO: Assembly: 80.0 % done. \n",
      "INFO: Assembly: 90.0 % done. \n",
      "INFO: Assembly: 100.0 % done. \n",
      "INFO: dim = 210\n",
      "INFO: Assembling boundary problems...\n",
      "INFO: Assembling boundary 1: dirichlet boundary\n",
      "INFO: Assembling boundary 2: mortar problem\n",
      "INFO: assemble: doing postprocess for problem JuliaFEM.Core.BoundaryProblem{JuliaFEM.Core.MortarProblem} assembly\n",
      "INFO: postprocess mortar assembly: remove contraints in tangent direction on boundary.\n",
      "INFO: postprocess mortar assembly: done.\n",
      "INFO: Solving system\n",
      "INFO: UMFPACK: solved in 0.0 seconds. norm = 3.5785351812097138e-12\n",
      "INFO: timing info for iteration:\n",
      "INFO: boundary assembly       : 0.10900020599365234\n",
      "INFO: field assembly          : 0.06299996376037598\n",
      "INFO: dump matrices to disk   : 0.0\n",
      "INFO: solve problem           : 0.09299993515014648\n",
      "INFO: update element data     : 0.0\n",
      "INFO: non-linear iteration    : 0.2650001049041748\n",
      "INFO: solver finished in 1.1540000438690186 seconds.\n",
      "INFO: displacement at tip: [-0.03914822922685343,-0.592340359574268]\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 122232 characters]",
      "text/plain": [
       "PyPlot.Figure(PyObject <matplotlib.figure.Figure object at 0x00000000303F3F98>)"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "true"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "using JuliaFEM.Core: SparseMatrixCOO, Element, get_integration_points, get_jacobian, get_connectivity, add!\n",
    "using JuliaFEM.Core: BoundaryAssembly, BoundaryProblem, get_elements\n",
    "\n",
    "function calculate_normal_tangential_coordinates(elements::Vector{Element}, time::Real)\n",
    "    P = SparseMatrixCOO()\n",
    "    field_dim = 2\n",
    "    for element in elements\n",
    "        haskey(element, \"normal-tangential coordinates\") || continue\n",
    "        for ip in get_integration_points(element, Val{2})\n",
    "            J = get_jacobian(element, ip, time)\n",
    "            w = ip.weight*norm(J)\n",
    "            nt = transpose(element(\"normal-tangential coordinates\", ip, time))\n",
    "            normal = nt[1,:]\n",
    "            tangent = nt[2,:]\n",
    "            for nid in get_connectivity(element)\n",
    "                ndofs = [2*(nid-1)+1, 2*(nid-1)+2]\n",
    "                add!(P, [2*(nid-1)+1], ndofs, normal)\n",
    "                add!(P, [2*(nid-1)+2], ndofs, tangent)\n",
    "            end\n",
    "        end\n",
    "    end\n",
    "    P = sparse(P)\n",
    "    for i=1:size(P,1)\n",
    "        n = norm(P[i,:])\n",
    "        if n > 0.0\n",
    "            P[i,:] = P[i,:] / n\n",
    "        end\n",
    "    end\n",
    "    return P\n",
    "end\n",
    "\n",
    "import JuliaFEM.Core: postprocess_assembly!\n",
    "\n",
    "function JuliaFEM.Core.postprocess_assembly!(\n",
    "    assembly::BoundaryAssembly, problem::BoundaryProblem{MortarProblem},\n",
    "    time::Real)\n",
    "    info(\"postprocess mortar assembly: remove contraints in tangent direction on boundary.\")\n",
    "    C1 = sparse(assembly.C1)\n",
    "    C2 = sparse(assembly.C2)\n",
    "    dim = size(C1, 1)\n",
    "    P = calculate_normal_tangential_coordinates(get_elements(problem), time)\n",
    "    C1 = P*C1\n",
    "    C2 = P*C2\n",
    "    for i=2:2:dim\n",
    "        C1[i,:] = 0\n",
    "        C2[i,:] = 0\n",
    "    end\n",
    "    assembly.C1 = C1\n",
    "    assembly.C2 = C2\n",
    "    info(\"postprocess mortar assembly: done.\")\n",
    "end\n",
    "\n",
    "\n",
    "field_problem, boundary_problem, contact_problem = create_problems()\n",
    "using JuliaFEM.Core: DirectSolver\n",
    "solver = DirectSolver()\n",
    "solver.name = \"divided_beam_small_sliding_contact\"\n",
    "solver.method = :UMFPACK\n",
    "solver.max_iterations = 10\n",
    "solver.dump_matrices = false\n",
    "push!(solver, field_problem)\n",
    "push!(solver, boundary_problem)\n",
    "push!(solver, contact_problem)\n",
    "call(solver, 0.0)\n",
    "\n",
    "u_tip = plot(field_problem, 30)\n",
    "isapprox(u_tip, [-0.03914822922685343,-0.592340359574268]) || warn(\"result changed\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Frictionless 2d contact with active and inactive nodes"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 76,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO: Found 5 element sets: LOWER_TO_UPPER, RIGHT, UPPER_TO_LOWER, LEFT, LOAD\n",
      "INFO: created 104 field elements.\n",
      "INFO: created 4 boundary elements.\n",
      "INFO: # of master elements: 14\n",
      "INFO: # of slave elements: 34\n",
      "INFO: Starting solver divided_beam_inequality_constraints\n",
      "INFO: # of field problems: 1\n",
      "INFO: # of boundary problems: 2\n",
      "INFO: Starting iteration 1\n",
      "INFO: Assembling field problems...\n",
      "INFO: Assembling body 1: plane stress linear elasticity\n",
      "INFO: Assembly: 10.0 % done. \n",
      "INFO: Assembly: 20.0 % done. \n",
      "INFO: Assembly: 30.0 % done. \n",
      "INFO: Assembly: 40.0 % done. \n",
      "INFO: Assembly: 50.0 % done. \n",
      "INFO: Assembly: 60.0 % done. \n",
      "INFO: Assembly: 70.0 % done. \n",
      "INFO: Assembly: 80.0 % done. \n",
      "INFO: Assembly: 90.0 % done. \n",
      "INFO: Assembly: 100.0 % done. \n",
      "INFO: dim = 210\n",
      "INFO: Assembling boundary problems...\n",
      "INFO: Assembling boundary 1: dirichlet boundary\n",
      "INFO: Assembling boundary 2: mortar problem\n",
      "INFO: assemble: doing postprocess for problem JuliaFEM.Core.BoundaryProblem{JuliaFEM.Core.MortarProblem} assembly\n",
      "INFO: postprocess mortar assembly: peforming PDASS.\n",
      "INFO: weighted gap = [0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-0.0,2.5,0.0,0.0,-0.0,2.5,0.0,0.0,0.0,5.0,0.0,0.0,-0.0,5.0,0.0,0.0,-0.0,5.0,0.0,0.0,-0.0,5.0,0.0,0.0,-0.0,5.0,0.0,0.0,-0.0,5.0,0.0,0.0,0.0,5.0,0.0,0.0,0.0,5.0,0.0,5.0,0.0,5.0,-0.0,5.0,-0.0,5.0,-0.0,5.0,0.0,5.0,0.0,5.0,0.0,5.0,0.0,5.0,0.0,5.0,-0.0,5.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0]\n",
      "INFO: lambda = [0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0]\n",
      "INFO: dof 45: normal gap = 2.5, X=[100.0,10.0], C=0.0, la=0.0\n",
      "INFO: contact dof 45 active (C = 0.0)\n",
      "INFO: dof 49: normal gap = 2.5, X=[0.0,10.0], C=-1.0, la=0.0\n",
      "INFO: dof 53: normal gap = 5.0, X=[5.0,10.0], C=0.0, la=0.0\n",
      "INFO: dof 57: normal gap = 5.0, X=[10.0,10.0], C=0.0, la=0.0\n",
      "INFO: dof 61: normal gap = 5.0, X=[15.0,10.0], C=0.0, la=0.0\n",
      "INFO: dof 65: normal gap = 5.0, X=[20.0,10.0], C=0.0, la=0.0\n",
      "INFO: dof 69: normal gap = 5.0, X=[25.0,10.0], C=0.0, la=0.0\n",
      "INFO: dof 73: normal gap = 5.0, X=[30.0,10.0], C=0.0, la=0.0\n",
      "INFO: dof 77: normal gap = 5.0, X=[35.0,10.0], C=0.0, la=0.0\n",
      "INFO: dof 81: normal gap = 5.0, X=[40.0,10.0], C=0.0, la=0.0\n",
      "INFO: dof 83: normal gap = 5.0, X=[95.0,10.0], C=0.0, la=0.0\n",
      "INFO: dof 85: normal gap = 5.0, X=[90.0,10.0], C=0.0, la=0.0\n",
      "INFO: dof 87: normal gap = 5.0, X=[85.0,10.0], C=0.0, la=0.0\n",
      "INFO: dof 89: normal gap = 5.0, X=[80.0,10.0], C=0.0, la=0.0\n",
      "INFO: dof 91: normal gap = 5.0, X=[75.0,10.0], C=0.0, la=0.0\n",
      "INFO: dof 93: normal gap = 5.0, X=[70.0,10.0], C=0.0, la=0.0\n",
      "INFO: dof 95: normal gap = 5.0, X=[65.0,10.0], C=0.0, la=0.0\n",
      "INFO: dof 97: normal gap = 5.0, X=[60.0,10.0], C=0.0, la=0.0\n",
      "INFO: dof 99: normal gap = 5.0, X=[55.0,10.0], C=0.0, la=0.0\n",
      "INFO: dof 101: normal gap = 5.0, X=[50.0,10.0], C=0.0, la=0.0\n",
      "INFO: dof 103: normal gap = 5.0, X=[45.0,10.0], C=0.0, la=0.0\n",
      "INFO: postprocess mortar assembly: done.\n",
      "INFO: Solving system\n",
      "INFO: UMFPACK: solved in 0.0 seconds. norm = 5.426685089269588\n",
      "INFO: timing info for iteration:\n",
      "INFO: boundary assembly       : 0.15599989891052246\n",
      "INFO: field assembly          : 0.06300020217895508\n",
      "INFO: dump matrices to disk   : 0.0\n",
      "INFO: solve problem           : 0.07800006866455078\n",
      "INFO: update element data     : 0.0\n",
      "INFO: non-linear iteration    : 0.2970001697540283\n",
      "INFO: Starting iteration 2\n",
      "INFO: Assembling field problems...\n",
      "INFO: Assembling body 1: plane stress linear elasticity\n",
      "INFO: Assembly: 10.0 % done. \n",
      "INFO: Assembly: 20.0 % done. \n",
      "INFO: Assembly: 30.0 % done. \n",
      "INFO: Assembly: 40.0 % done. \n",
      "INFO: Assembly: 50.0 % done. \n",
      "INFO: Assembly: 60.0 % done. \n",
      "INFO: Assembly: 70.0 % done. \n",
      "INFO: Assembly: 80.0 % done. \n",
      "INFO: Assembly: 90.0 % done. \n",
      "INFO: Assembly: 100.0 % done. \n",
      "INFO: dim = 210\n",
      "INFO: Assembling boundary problems...\n",
      "INFO: Assembling boundary 1: dirichlet boundary\n",
      "INFO: Assembling boundary 2: mortar problem\n",
      "INFO: assemble: doing postprocess for problem JuliaFEM.Core.BoundaryProblem{JuliaFEM.Core.MortarProblem} assembly\n",
      "INFO: postprocess mortar assembly: peforming PDASS.\n",
      "INFO: weighted gap = [0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-0.33,0.0,0.0,0.0,-0.01,2.49,0.0,0.0,-0.07,4.96,0.0,0.0,-0.15,4.89,0.0,0.0,-0.23,4.77,0.0,0.0,-0.3,4.61,0.0,0.0,-0.37,4.41,0.0,0.0,-0.42,4.18,0.0,0.0,-0.47,3.92,0.0,0.0,-0.51,3.65,-0.67,0.2,-0.67,0.51,-0.67,0.82,-0.66,1.14,-0.66,1.45,-0.65,1.77,-0.64,2.09,-0.62,2.42,-0.6,2.73,-0.58,3.05,-0.55,3.35,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0]\n",
      "INFO: lambda = [0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.05,-0.0,0.0,0.0,0.74,-0.19,0.0,0.0,-0.0,0.0,0.0,0.0,0.0,-0.0,0.0,0.0,-0.0,0.0,0.0,0.0,0.0,-0.0,0.0,0.0,-0.0,0.0,0.0,0.0,0.0,-0.0,0.0,0.0,-0.0,0.0,0.0,0.0,0.0,-0.0,-0.0,0.0,0.0,-0.0,-0.0,0.0,0.0,-0.0,-0.0,0.0,0.0,-0.0,-0.0,0.0,0.0,-0.0,-0.0,0.0,0.0,-0.0,-0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-0.0,-0.0,0.0,0.0,-0.0,-0.0,0.0,0.0,-0.0,-0.0,0.0,0.0,-0.0,-0.0,0.0,0.0,-0.0,-0.0,0.0,0.0,-0.0,-0.0,0.0,0.0,-0.0,-0.0,0.0,0.0,-4.87,-4.26]\n",
      "INFO: dof 45: normal gap = 0.0, X=[100.0,10.0], C=0.0, la=-0.0\n",
      "INFO: contact dof 45 active (C = 0.0)\n",
      "INFO: dof 49: normal gap = 2.493, X=[0.0,10.0], C=-1.0, la=-0.18516\n",
      "INFO: dof 53: normal gap = 4.959, X=[5.0,10.0], C=0.0, la=0.0\n",
      "INFO: dof 57: normal gap = 4.885, X=[10.0,10.0], C=0.0, la=-0.0\n",
      "INFO: dof 61: normal gap = 4.768, X=[15.0,10.0], C=0.0, la=0.0\n",
      "INFO: dof 65: normal gap = 4.606, X=[20.0,10.0], C=0.0, la=-0.0\n",
      "INFO: dof 69: normal gap = 4.407, X=[25.0,10.0], C=0.0, la=0.0\n",
      "INFO: dof 73: normal gap = 4.179, X=[30.0,10.0], C=0.0, la=-0.0\n",
      "INFO: dof 77: normal gap = 3.924, X=[35.0,10.0], C=0.0, la=0.0\n",
      "INFO: dof 81: normal gap = 3.646, X=[40.0,10.0], C=0.0, la=-0.0\n",
      "INFO: dof 83: normal gap = 0.205, X=[95.0,10.0], C=0.0, la=0.0\n",
      "INFO: dof 85: normal gap = 0.514, X=[90.0,10.0], C=0.0, la=-0.0\n",
      "INFO: dof 87: normal gap = 0.825, X=[85.0,10.0], C=0.0, la=0.0\n",
      "INFO: dof 89: normal gap = 1.138, X=[80.0,10.0], C=0.0, la=-0.0\n",
      "INFO: dof 91: normal gap = 1.455, X=[75.0,10.0], C=0.0, la=0.0\n",
      "INFO: dof 93: normal gap = 1.774, X=[70.0,10.0], C=0.0, la=-0.0\n",
      "INFO: dof 95: normal gap = 2.094, X=[65.0,10.0], C=0.0, la=0.0\n",
      "INFO: dof 97: normal gap = 2.415, X=[60.0,10.0], C=0.0, la=-0.0\n",
      "INFO: dof 99: normal gap = 2.734, X=[55.0,10.0], C=0.0, la=0.0\n",
      "INFO: dof 101: normal gap = 3.048, X=[50.0,10.0], C=0.0, la=-0.0\n",
      "INFO: dof 103: normal gap = 3.353, X=[45.0,10.0], C=0.0, la=0.0\n",
      "INFO: postprocess mortar assembly: done.\n",
      "INFO: Solving system\n",
      "INFO: UMFPACK: solved in 0.0 seconds. norm = 1.303211404108607e-12\n",
      "INFO: timing info for iteration:\n",
      "INFO: boundary assembly       : 0.1399998664855957\n",
      "INFO: field assembly          : 0.06200003623962402\n",
      "INFO: dump matrices to disk   : 0.0\n",
      "INFO: solve problem           : 0.07800006866455078\n",
      "INFO: update element data     : 0.0\n",
      "INFO: non-linear iteration    : 0.2799999713897705\n",
      "INFO: solver finished in 0.5770001411437988 seconds.\n",
      "INFO: displacement at tip: [-0.036167518632111914,-0.4890644234713899]\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 131360 characters]",
      "text/plain": [
       "PyPlot.Figure(PyObject <matplotlib.figure.Figure object at 0x0000000033496E10>)"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "2-element Array{Float64,1}:\n",
       " -0.0361675\n",
       " -0.489064 "
      ]
     },
     "execution_count": 76,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "function create_problems_2()\n",
    "\n",
    "    mesh = parse_aster_med_file(Pkg.dir(\"JuliaFEM\")*\"/geometry/2d_beam/BEAM.med\")\n",
    "    \n",
    "    upper_body_nodes = Set()\n",
    "    for (elid, (eltype, elset, elcon)) in mesh[\"connectivity\"]\n",
    "        eltype == :TR3 || continue\n",
    "        push!(upper_body_nodes, elcon...)\n",
    "    end\n",
    "    for (nid, coords) in mesh[\"nodes\"]\n",
    "        nid in upper_body_nodes || continue\n",
    "        mesh[\"nodes\"][nid] = mesh[\"nodes\"][nid] + [0.0, 1.0]\n",
    "    end\n",
    "    \n",
    "    field_problem = PlaneStressLinearElasticityProblem()\n",
    "\n",
    "    # field problems\n",
    "    joo = Dict(:QU4 => Quad4, :TR3 => Tri3)\n",
    "    for (elid, (eltype, elset, elcon)) in mesh[\"connectivity\"]\n",
    "        eltype in keys(joo) || continue\n",
    "        element = joo[eltype](elcon)\n",
    "        update!(element, \"geometry\", mesh[\"nodes\"])\n",
    "        element[\"youngs modulus\"] = 900.0\n",
    "        element[\"poissons ratio\"] = 0.25\n",
    "        push!(field_problem, element)\n",
    "    end\n",
    "\n",
    "    # neumann boundary condition -1 on y direction\n",
    "    for (elid, (eltype, elset, elcon)) in mesh[\"connectivity\"]\n",
    "        eltype == :SE2 || continue\n",
    "        elset == :LOAD || continue\n",
    "        element = Seg2(elcon)\n",
    "        update!(element, \"geometry\", mesh[\"nodes\"])\n",
    "        # FIXME\n",
    "        # element[\"displacement traction force\"] = rmat*[0.0, -0.01]\n",
    "        f = rmat*[0.0, -0.0125]*1.5\n",
    "        element[\"displacement traction force\"] = Vector{Float64}[f, f]\n",
    "        push!(field_problem, element)\n",
    "    end\n",
    "\n",
    "    # boundary conditions\n",
    "    boundary_problem = DirichletProblem(\"displacement\", 2)\n",
    "\n",
    "    for (elid, (eltype, elset, elcon)) in mesh[\"connectivity\"]\n",
    "        eltype == :SE2 || continue\n",
    "        elset == :LEFT || continue\n",
    "        element = Seg2(elcon)\n",
    "        update!(element, \"geometry\", mesh[\"nodes\"])\n",
    "        element[\"displacement\"] = (0.0 => Vector{Float64}[[0.0, 0.0], [0.0, 0.0]])\n",
    "        push!(boundary_problem, element)\n",
    "    end\n",
    "\n",
    "    info(\"created $(length(get_elements(field_problem))) field elements.\")\n",
    "    info(\"created $(length(get_elements(boundary_problem))) boundary elements.\")\n",
    "\n",
    "    # Contact definition: contact pair is `LOWER_TO_UPPER <--> UPPER_TO_LOWER`:\n",
    "\n",
    "    master_surface = :UPPER_TO_LOWER\n",
    "    slave_surface = :LOWER_TO_UPPER\n",
    "\n",
    "    contact_problem = MortarProblem(\"displacement\", 2)\n",
    "    master_elements = JuliaFEM.Core.Element[]\n",
    "    for (elid, (eltype, elset, elcon)) in mesh[\"connectivity\"]\n",
    "        eltype == :SE2 || continue\n",
    "        elset == master_surface || continue\n",
    "        element = Seg2(elcon)\n",
    "        update!(element, \"geometry\", mesh[\"nodes\"])\n",
    "        element[\"displacement\"] = (0.0 => Vector{Float64}[[0.0, 0.0], [0.0, 0.0]])\n",
    "        push!(master_elements, element)\n",
    "        push!(contact_problem, element)\n",
    "    end\n",
    "\n",
    "    for (elid, (eltype, elset, elcon)) in mesh[\"connectivity\"]\n",
    "        eltype == :SE2 || continue\n",
    "        elset == slave_surface || continue\n",
    "        element = Seg2(elcon)\n",
    "        update!(element, \"geometry\", mesh[\"nodes\"])\n",
    "        element[\"master elements\"] = master_elements\n",
    "        element[\"displacement\"] = (0.0 => Vector{Float64}[[0.0, 0.0], [0.0, 0.0]])\n",
    "        calculate_normal_tangential_coordinates!(element, 0.0)\n",
    "        push!(contact_problem, element)\n",
    "    end\n",
    "\n",
    "    info(\"# of master elements: $(length(master_elements))\")\n",
    "    info(\"# of slave elements: $(length(contact_problem.elements))\")\n",
    "\n",
    "    return field_problem, boundary_problem, contact_problem\n",
    "\n",
    "end\n",
    "\n",
    "using JuliaFEM.Core: calculate_nodal_vector, Element\n",
    "\n",
    "function JuliaFEM.Core.postprocess_assembly!(\n",
    "    assembly::BoundaryAssembly, problem::BoundaryProblem{MortarProblem},\n",
    "    time::Real)\n",
    "    info(\"postprocess mortar assembly: peforming PDASS.\")\n",
    "    \n",
    "    C1 = sparse(assembly.C1)\n",
    "    C2 = sparse(assembly.C2)\n",
    "    dim = size(C1, 1)\n",
    "    elements = get_elements(problem)\n",
    "    P = -calculate_normal_tangential_coordinates(elements, time)\n",
    "    resize!(C1, 156, 156)\n",
    "    resize!(C2, 156, 156)\n",
    "    resize!(P, 156, 156)\n",
    "    la = calculate_nodal_vector(\"reaction force\", 2, elements, time)\n",
    "    X = calculate_nodal_vector(\"geometry\", 2, elements, time)\n",
    "    u = calculate_nodal_vector(\"displacement\", 2, elements, time)\n",
    "    x = X+u\n",
    "    #info(\"x = \", x)\n",
    "    #info(\"lambda = \", la)\n",
    "    gh = -C1*x  # weighted gap vector\n",
    "    info(\"weighted gap = \", round(gh, 2))\n",
    "    info(\"lambda = \", round(la, 2))\n",
    "\n",
    "    #=\n",
    "    info(\"size P = \", size(P))\n",
    "    info(\"size C1 = \", size(C1))\n",
    "    info(\"size C2 = \", size(C2))\n",
    "    info(\"size la = \", size(la))\n",
    "    info(\"size gh = \", size(gh))\n",
    "    info(\"displacement = \", round(u, 2))\n",
    "    =#\n",
    "\n",
    "    #nz = sort(unique(rowvals(P)))\n",
    "    #dump(round(full(P[nz,nz]), 3))\n",
    "    \n",
    "    C1 = P*C1\n",
    "    C2 = P*C2\n",
    "    gh = P*gh\n",
    "    la = P*la\n",
    "    c = 1.0\n",
    "    # complementarity function\n",
    "    C = la - clamp(la - c*gh, 0, Inf)\n",
    "    C[abs(C) .< 1.0e-12] = 0.0\n",
    "    C[49] = -1 # node in support\n",
    "    #info(\"complementarity function = \", round(C, 2))\n",
    "    X2 = reshape(X, 2, round(Int, length(X)/2))\n",
    "    j = 0\n",
    "    for i=1:2:dim\n",
    "        j += 1\n",
    "        X2[1,j] == 0 && continue\n",
    "        info(\"dof $i: normal gap = $(round(gh[i], 3)), X=$(round(X2[:,j], 2)), C=$(round(C[i], 5)), la=$(round(la[i], 5))\")\n",
    "        #if C[i] > 0\n",
    "        if i == 45\n",
    "            info(\"contact dof $i active (C = $(C[i]))\")\n",
    "            #gh[i] = -gh[i]\n",
    "        else\n",
    "            C1[i, :] = 0\n",
    "            C2[i, :] = 0\n",
    "            gh[i] = 0\n",
    "        end\n",
    "    end\n",
    "    for i=2:2:dim\n",
    "        C1[i, :] = 0\n",
    "        C2[i, :] = 0\n",
    "        gh[i] = 0\n",
    "    end\n",
    "    #assembly.g = g\n",
    "    assembly.C1 = C1\n",
    "    assembly.C2 = C2\n",
    "    assembly.g = sparse(gh)\n",
    "    info(\"postprocess mortar assembly: done.\")\n",
    "end\n",
    "\n",
    "field_problem, boundary_problem, contact_problem = create_problems_2()\n",
    "using JuliaFEM.Core: DirectSolver\n",
    "solver = DirectSolver()\n",
    "solver.name = \"divided_beam_inequality_constraints\"\n",
    "solver.method = :UMFPACK\n",
    "solver.max_iterations = 5\n",
    "solver.dump_matrices = false\n",
    "push!(solver, field_problem)\n",
    "push!(solver, boundary_problem)\n",
    "push!(solver, contact_problem)\n",
    "call(solver, 0.0)\n",
    "\n",
    "u_tip = plot(field_problem, 1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "4x4 Array{Float64,2}:\n",
       " 0.340028  0.654971  0.587056   0.0\n",
       " 0.28579   0.626868  0.0744826  0.0\n",
       " 0.215966  0.159712  0.301785   0.0\n",
       " 0.0       0.0       0.0        0.0"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A = sparse(rand(3, 3))\n",
    "resize!(A, 4, 4)\n",
    "full(A)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2-element Array{Float64,1}:\n",
       " 0.0\n",
       " 0.0"
      ]
     },
     "execution_count": 34,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "contact_problem.elements[5](\"displacement\", [0.0], 0.0)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "4-element Array{Int64,1}:\n",
       " 137\n",
       " 138\n",
       " 139\n",
       " 140"
      ]
     },
     "execution_count": 38,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "JuliaFEM.Core.get_gdofs(contact_problem.elements[5], 2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO: assemble: doing postprocess for problem JuliaFEM.Core.BoundaryProblem{JuliaFEM.Core.MortarProblem} assembly\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Array(Float64,(0,2)) "
     ]
    },
    {
     "data": {
      "text/plain": [
       "4x1 Array{Float64,2}:\n",
       "  0.0\n",
       " -2.0\n",
       "  0.0\n",
       " -2.0"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO: postprocess mortar assembly: remove contraints in tangent direction on boundary.\n",
      "INFO: postprocess mortar assembly: done.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0x2 Array{Float64,2}\n"
     ]
    }
   ],
   "source": [
    "function JuliaFEM.Core.postprocess_assembly!(\n",
    "    assembly::BoundaryAssembly, problem::BoundaryProblem{MortarProblem},\n",
    "    time::Real)\n",
    "    info(\"postprocess mortar assembly: remove contraints in tangent direction on boundary.\")\n",
    "    C1 = sparse(assembly.C1)\n",
    "    C2 = sparse(assembly.C2)\n",
    "    g = sparse(assembly.g)\n",
    "    n = round(Int, length(g)/2)\n",
    "    dump(reshape(full(g), 2, n)[:,60:end]')\n",
    "    dim = size(C1, 1)\n",
    "    P = calculate_normal_tangential_coordinates(get_elements(problem), time)\n",
    "    C1 = P*C1\n",
    "    C2 = P*C2\n",
    "    for i=2:2:dim\n",
    "        C1[i,:] = 0\n",
    "        C2[i,:] = 0\n",
    "    end\n",
    "    assembly.g = g\n",
    "    assembly.C1 = C1\n",
    "    assembly.C2 = C2\n",
    "    info(\"postprocess mortar assembly: done.\")\n",
    "end\n",
    "\n",
    "el1 = Seg2([1, 2])\n",
    "el1[\"geometry\"] = Vector{Float64}[[0.0, 0.0], [4.0, 0.0]]\n",
    "el2 = Seg2([3, 4])\n",
    "el2[\"geometry\"] = Vector{Float64}[[0.0, 1.0], [4.0, 1.0]]\n",
    "el1[\"master elements\"] = Element[el2]\n",
    "JuliaFEM.Core.calculate_normal_tangential_coordinates!(el1, 0.0)\n",
    "p = MortarProblem(\"displacement\", 2)\n",
    "push!(p, el1)\n",
    "a = JuliaFEM.Core.assemble(p, 0.0)\n",
    "full(a.g)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Simple test case, sliding contact"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Dict{Int64,Array{Float64,1}} with 8 entries:\n",
       "  7 => [2.0,0.0]\n",
       "  4 => [2.0,4.0]\n",
       "  2 => [2.0,6.0]\n",
       "  3 => [2.0,2.0]\n",
       "  5 => [0.0,0.0]\n",
       "  8 => [4.0,0.0]\n",
       "  6 => [2.0,0.0]\n",
       "  1 => [0.0,2.0]"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "using JuliaFEM.Core: Seg2, Tri3, Quad4, Node, update!,\n",
    "    calculate_normal_tangential_coordinates!, PlaneStressLinearElasticityProblem, \n",
    "    ContactProblem, SmallSlidingContact, Element\n",
    "using JuliaFEM.Core: PlaneStressLinearElasticityProblem, DirichletProblem,\n",
    "                     get_connectivity, Quad4, Tri3, Seg2, LinearSolver,\n",
    "                     update!, get_elements, BiorthogonalBasis, StandardBasis\n",
    "nodes = Dict{Int64, Node}(\n",
    "    1 => [0.0, 2.0],\n",
    "    2 => [2.0, 6.0],\n",
    "    3 => [2.0, 2.0],\n",
    "    4 => [2.0, 4.0],\n",
    "    5 => [0.0, 0.0],\n",
    "    6 => [2.0, 0.0],\n",
    "    7 => [2.0, 0.0],\n",
    "    8 => [4.0, 0.0])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "basis = StandardBasis\n",
    "\n",
    "el1 = Quad4([5, 6, 3, 1])\n",
    "el2 = Tri3([1, 3, 4])\n",
    "el3 = Tri3([7, 8, 2])\n",
    "del1 = Seg2([5, 6])\n",
    "del2 = Seg2([7, 8])\n",
    "sel1 = Seg2([4, 3])\n",
    "sel2 = Seg2([3, 6])\n",
    "mel1 = Seg2([2, 7])\n",
    "update!([el1, el2, el3, sel1, sel2, mel1, del1, del2], \"geometry\", nodes)\n",
    "for el in [el1, el2, el3]\n",
    "    el[\"youngs modulus\"] = 90.0\n",
    "    el[\"poissons ratio\"] = 0.25\n",
    "end\n",
    "for el in [del1, del2]\n",
    "    el[\"displacement\"] = 0.0\n",
    "end\n",
    "for el in [sel1, sel2]\n",
    "    el[\"master elements\"] = Element[mel1]\n",
    "    calculate_normal_tangential_coordinates!(el, 0.0)\n",
    "end\n",
    "prob = PlaneStressLinearElasticityProblem()\n",
    "push!(prob, el1, el2, el3)\n",
    "bc = DirichletProblem(\"displacement\", 2; basis=basis)\n",
    "push!(bc, del1, del2)\n",
    "con = ContactProblem(\"cont\", \"displacement\", 2;\n",
    "                     contact_type=SmallSlidingContact)\n",
    "push!(con, sel1, sel2);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO: slave dofs of element: [7,8,5,6]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "normal tangential = \n",
      "[1.0 0.0\n",
      " 0.0 -1.0]\n",
      "normal tangential = "
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO: slave dofs of element: [5,6,11,12]\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "JuliaFEM.Core.BoundaryAssembly(JuliaFEM.Core.SparseMatrixCOO([7,5,7,5,7,5,7,5,8,6  …  5,11,6,12,6,12,6,12,6,12],[7,7,5,5,3,3,13,13,8,8  …  13,13,6,6,12,12,4,4,14,14],[0.21522,0.0105929,0.0105929,0.000521371,-0.147011,-0.00723572,-0.0788018,-0.00387854,0.21522,0.0105929  …  -0.0109405,-0.222282,0.000521371,0.0105929,0.0105929,0.21522,-0.00017379,-0.00353096,-0.0109405,-0.222282]),JuliaFEM.Core.SparseMatrixCOO([7,5,7,5,7,5,7,5,8,6  …  5,11,6,12,6,12,6,12,6,12],[7,7,5,5,3,3,13,13,8,8  …  13,13,6,6,12,12,4,4,14,14],[0.21522,0.0105929,0.0105929,0.000521371,-0.147011,-0.00723572,-0.0788018,-0.00387854,0.21522,0.0105929  …  -0.0109405,-0.222282,0.000521371,0.0105929,0.0105929,0.21522,-0.00017379,-0.00353096,-0.0109405,-0.222282]),JuliaFEM.Core.SparseMatrixCOO(Int64[],Int64[],Float64[]),JuliaFEM.Core.SparseMatrixCOO(Int64[],Int64[],Float64[]))"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "[1.0 0.0\n",
      " 0.0 -1.0]\n",
      "normal tangential = \n",
      "[1.0 0.0\n",
      " 0.0 -1.0]\n",
      "normal tangential = \n",
      "[1.0 0.0\n",
      " 0.0 -1.0]\n",
      "normal tangential = \n",
      "[1.0 0.0\n",
      " 0.0 -1.0]\n",
      "normal tangential = \n",
      "[1.0 0.0\n",
      " 0.0 -1.0]\n",
      "normal tangential = \n",
      "[1.0 0.0\n",
      " 0.0 -1.0]\n",
      "normal tangential = \n",
      "[1.0 0.0\n",
      " 0.0 -1.0]\n",
      "normal tangential = \n",
      "[1.0 0.0\n",
      " 0.0 -1.0]\n",
      "normal tangential = \n",
      "[1.0 0.0\n",
      " 0.0 -1.0]\n"
     ]
    }
   ],
   "source": [
    "ass1 = assemble(prob, 0.0)\n",
    "dbc = assemble(bc, 0.0)\n",
    "cbc = assemble(con, 0.0)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "8x8 Array{Float64,2}:\n",
       "  92.0  -15.0  0.0   0.0  -74.0   15.0    0.0  -12.0\n",
       " -15.0   62.0  0.0   0.0   15.0  -14.0  -18.0    0.0\n",
       "   0.0    0.0  6.0   0.0    0.0    0.0    0.0    0.0\n",
       "   0.0    0.0  0.0  16.0    0.0    0.0    0.0    0.0\n",
       " -74.0   15.0  0.0   0.0  110.0  -15.0  -18.0   12.0\n",
       "  15.0  -14.0  0.0   0.0  -15.0  110.0   18.0  -48.0\n",
       "   0.0  -18.0  0.0   0.0  -18.0   18.0   18.0    0.0\n",
       " -12.0    0.0  0.0   0.0   12.0  -48.0    0.0   48.0"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "K = full(ass1.stiffness_matrix)\n",
    "dims = size(K)\n",
    "K[1:8,1:8]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "16x16 Array{Float64,2}:\n",
       " 0.0  0.0    0.0    0.0   0.0   0.0   0.0   0.0  0.0  0.0   0.0   0.0    0.0    0.0  0.0  0.0\n",
       " 0.0  0.0    0.0    0.0   0.0   0.0   0.0   0.0  0.0  0.0   0.0   0.0    0.0    0.0  0.0  0.0\n",
       " 0.0  0.0    0.0    0.0   0.0   0.0   0.0   0.0  0.0  0.0   0.0   0.0    0.0    0.0  0.0  0.0\n",
       " 0.0  0.0    0.0    0.0   0.0   0.0   0.0   0.0  0.0  0.0   0.0   0.0    0.0    0.0  0.0  0.0\n",
       " 0.0  0.0  -12.0    0.0  24.0   0.0   6.0   0.0  0.0  0.0   6.0   0.0  -24.0    0.0  0.0  0.0\n",
       " 0.0  0.0    0.0  -12.0   0.0  24.0   0.0   6.0  0.0  0.0   0.0   6.0    0.0  -24.0  0.0  0.0\n",
       " 0.0  0.0  -10.0    0.0   6.0   0.0  12.0   0.0  0.0  0.0   0.0   0.0   -8.0    0.0  0.0  0.0\n",
       " 0.0  0.0    0.0  -10.0   0.0   6.0   0.0  12.0  0.0  0.0   0.0   0.0    0.0   -8.0  0.0  0.0\n",
       " 0.0  0.0    0.0    0.0   0.0   0.0   0.0   0.0  0.0  0.0   0.0   0.0    0.0    0.0  0.0  0.0\n",
       " 0.0  0.0    0.0    0.0   0.0   0.0   0.0   0.0  0.0  0.0   0.0   0.0    0.0    0.0  0.0  0.0\n",
       " 0.0  0.0   -2.0    0.0   6.0   0.0   0.0   0.0  0.0  0.0  12.0   0.0  -16.0    0.0  0.0  0.0\n",
       " 0.0  0.0    0.0   -2.0   0.0   6.0   0.0   0.0  0.0  0.0   0.0  12.0    0.0  -16.0  0.0  0.0\n",
       " 0.0  0.0    0.0    0.0   0.0   0.0   0.0   0.0  0.0  0.0   0.0   0.0    0.0    0.0  0.0  0.0\n",
       " 0.0  0.0    0.0    0.0   0.0   0.0   0.0   0.0  0.0  0.0   0.0   0.0    0.0    0.0  0.0  0.0\n",
       " 0.0  0.0    0.0    0.0   0.0   0.0   0.0   0.0  0.0  0.0   0.0   0.0    0.0    0.0  0.0  0.0\n",
       " 0.0  0.0    0.0    0.0   0.0   0.0   0.0   0.0  0.0  0.0   0.0   0.0    0.0    0.0  0.0  0.0"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "fixed = [9, 10, 11, 12, 13, 14, 15, 16]\n",
    "ENV[\"COLUMNS\"] = 300\n",
    "C1 = full(cbc.C1, dims...)\n",
    "C1[abs(C1) .< 1.0e-9] = 0\n",
    "#C1[fixed, fixed] = 0\n",
    "C1*18"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "16x16 Array{Float64,2}:\n",
       " 0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0\n",
       " 0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0\n",
       " 0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0\n",
       " 0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0\n",
       " 0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0\n",
       " 0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0\n",
       " 0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0\n",
       " 0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0\n",
       " 0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0\n",
       " 0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0\n",
       " 0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0\n",
       " 0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0\n",
       " 0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0\n",
       " 0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0\n",
       " 0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0\n",
       " 0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "D = full(cbc.D, dims...)\n",
    "D[abs(D) .< 1.0e-9] = 0\n",
    "D[fixed, fixed] = 0\n",
    "D"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "16x16 Array{Float64,2}:\n",
       " 0.0  0.0    0.0  0.0   0.0  0.0   0.0  0.0  0.0  0.0   0.0  0.0    0.0  0.0  0.0  0.0\n",
       " 0.0  0.0    0.0  0.0   0.0  0.0   0.0  0.0  0.0  0.0   0.0  0.0    0.0  0.0  0.0  0.0\n",
       " 0.0  0.0    0.0  0.0   0.0  0.0   0.0  0.0  0.0  0.0   0.0  0.0    0.0  0.0  0.0  0.0\n",
       " 0.0  0.0    0.0  0.0   0.0  0.0   0.0  0.0  0.0  0.0   0.0  0.0    0.0  0.0  0.0  0.0\n",
       " 0.0  0.0  -12.0  0.0  24.0  0.0   6.0  0.0  0.0  0.0   6.0  0.0  -24.0  0.0  0.0  0.0\n",
       " 0.0  0.0    0.0  0.0   0.0  0.0   0.0  0.0  0.0  0.0   0.0  0.0    0.0  0.0  0.0  0.0\n",
       " 0.0  0.0  -10.0  0.0   6.0  0.0  12.0  0.0  0.0  0.0   0.0  0.0   -8.0  0.0  0.0  0.0\n",
       " 0.0  0.0    0.0  0.0   0.0  0.0   0.0  0.0  0.0  0.0   0.0  0.0    0.0  0.0  0.0  0.0\n",
       " 0.0  0.0    0.0  0.0   0.0  0.0   0.0  0.0  0.0  0.0   0.0  0.0    0.0  0.0  0.0  0.0\n",
       " 0.0  0.0    0.0  0.0   0.0  0.0   0.0  0.0  0.0  0.0   0.0  0.0    0.0  0.0  0.0  0.0\n",
       " 0.0  0.0   -2.0  0.0   6.0  0.0   0.0  0.0  0.0  0.0  12.0  0.0  -16.0  0.0  0.0  0.0\n",
       " 0.0  0.0    0.0  0.0   0.0  0.0   0.0  0.0  0.0  0.0   0.0  0.0    0.0  0.0  0.0  0.0\n",
       " 0.0  0.0    0.0  0.0   0.0  0.0   0.0  0.0  0.0  0.0   0.0  0.0    0.0  0.0  0.0  0.0\n",
       " 0.0  0.0    0.0  0.0   0.0  0.0   0.0  0.0  0.0  0.0   0.0  0.0    0.0  0.0  0.0  0.0\n",
       " 0.0  0.0    0.0  0.0   0.0  0.0   0.0  0.0  0.0  0.0   0.0  0.0    0.0  0.0  0.0  0.0\n",
       " 0.0  0.0    0.0  0.0   0.0  0.0   0.0  0.0  0.0  0.0   0.0  0.0    0.0  0.0  0.0  0.0"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "C2 = full(cbc.C2, dims...)\n",
    "C2[abs(C2) .< 1.0e-9] = 0\n",
    "#C2[fixed, :] = 0\n",
    "for i=2:2:16\n",
    "    C1[i,:] = 0\n",
    "    C2[i,:] = 0\n",
    "end\n",
    "C2*18"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "16x16 Array{Float64,2}:\n",
       " 0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0\n",
       " 0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0\n",
       " 0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0\n",
       " 0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0\n",
       " 0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0\n",
       " 0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0\n",
       " 0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0\n",
       " 0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0\n",
       " 0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0\n",
       " 0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0\n",
       " 0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0\n",
       " 0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0\n",
       " 0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0\n",
       " 0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0\n",
       " 0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0\n",
       " 0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "D"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "16x16 Array{Float64,2}:\n",
       " 0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0   0.0   0.0   0.0   0.0   0.0   0.0   0.0   0.0\n",
       " 0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0   0.0   0.0   0.0   0.0   0.0   0.0   0.0   0.0\n",
       " 0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0   0.0   0.0   0.0   0.0   0.0   0.0   0.0   0.0\n",
       " 0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0   0.0   0.0   0.0   0.0   0.0   0.0   0.0   0.0\n",
       " 0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0   0.0   0.0   0.0   0.0   0.0   0.0   0.0   0.0\n",
       " 0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0   0.0   0.0   0.0   0.0   0.0   0.0   0.0   0.0\n",
       " 0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0   0.0   0.0   0.0   0.0   0.0   0.0   0.0   0.0\n",
       " 0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0   0.0   0.0   0.0   0.0   0.0   0.0   0.0   0.0\n",
       " 0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  12.0   0.0   6.0   0.0   0.0   0.0   0.0   0.0\n",
       " 0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0   0.0  12.0   0.0   6.0   0.0   0.0   0.0   0.0\n",
       " 0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0   6.0   0.0  12.0   0.0   0.0   0.0   0.0   0.0\n",
       " 0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0   0.0   6.0   0.0  12.0   0.0   0.0   0.0   0.0\n",
       " 0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0   0.0   0.0   0.0   0.0  12.0   0.0   6.0   0.0\n",
       " 0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0   0.0   0.0   0.0   0.0   0.0  12.0   0.0   6.0\n",
       " 0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0   0.0   0.0   0.0   0.0   6.0   0.0  12.0   0.0\n",
       " 0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0   0.0   0.0   0.0   0.0   0.0   6.0   0.0  12.0"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "DC1 = full(dbc.C1, dims...)\n",
    "DC2 = full(dbc.C2, dims...)\n",
    "DD = full(dbc.D, dims...)\n",
    "DC1*18"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "32-element Array{Float64,1}:\n",
       " 18.0\n",
       "  0.0\n",
       "  0.0\n",
       "  0.0\n",
       "  0.0\n",
       "  0.0\n",
       "  0.0\n",
       "  0.0\n",
       "  0.0\n",
       "  0.0\n",
       "  0.0\n",
       "  0.0\n",
       "  0.0\n",
       "  ⋮  \n",
       "  0.0\n",
       "  0.0\n",
       "  0.0\n",
       "  0.0\n",
       "  0.0\n",
       "  0.0\n",
       "  0.0\n",
       "  0.0\n",
       "  0.0\n",
       "  0.0\n",
       "  0.0\n",
       "  0.0"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "f = zeros(16)\n",
    "f[1] = 18.0\n",
    "g = zeros(16)\n",
    "A = [K (C1+DC1)'; (C2+DC2) (D+DD)]\n",
    "b = [f; g]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO: zero line 17\n",
      "INFO: zero line 18\n",
      "INFO: zero line 19\n",
      "INFO: zero line 20\n",
      "INFO: zero line 22\n",
      "INFO: zero line 24\n"
     ]
    }
   ],
   "source": [
    "for i=1:size(A,1)\n",
    "    if sum(abs(A[i,:])) == 0\n",
    "        info(\"zero line $i\")\n",
    "        A[i,i] = 1.0\n",
    "    end\n",
    "end"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2x8 Array{Float64,2}:\n",
       " 0.406155  0.659049   0.219683   0.439366   0.0  0.0  0.0  0.0\n",
       " 0.149452  0.0       -0.102834  -0.0562157  0.0  0.0  0.0  0.0"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "u = A \\ b\n",
    "u = reshape(u, 2, 16)\n",
    "u[abs(u) .< 1.0e-9] = 0\n",
    "disp = u[:,1:8]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "# tie contact\n",
    "@assert isapprox(norm(vec(disp)), 0.8223229928825583)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "# frictionless sliding\n",
    "@assert isapprox(norm(vec(disp)), 0.9363129098080032)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2x8 Array{Float64,2}:\n",
       " 0.0  2.0  2.0  2.0  0.0  2.0  2.0  4.0\n",
       " 2.0  6.0  2.0  4.0  0.0  0.0  0.0  0.0"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "X = [0.0 2; 2 6; 2 2; 2 4; 0 0; 2 0; 2 0; 4 0]'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2x8 Array{Float64,2}:\n",
       " 0.406155  2.65905  2.21968  2.43937  0.0  2.0  2.0  4.0\n",
       " 2.14945   6.0      1.89717  3.94378  0.0  0.0  0.0  0.0"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x = X + disp"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3-element Array{Int64,1}:\n",
       " 7\n",
       " 8\n",
       " 2"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "eldofs = Dict()\n",
    "eldofs[1] = [5, 6, 3, 1]\n",
    "eldofs[2] = [1, 3, 4]\n",
    "eldofs[3] = [7, 8, 2]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 23900 characters]",
      "text/plain": [
       "PyPlot.Figure(PyObject <matplotlib.figure.Figure object at 0x000000002615EAC8>)"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "(-0.1,6.1)"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "using PyPlot\n",
    "\n",
    "function get_coords(X)\n",
    "    return [X X[:, 1]]\n",
    "end\n",
    "\n",
    "function plot(X, args...; kwargs...)\n",
    "    X1 = X[:, eldofs[1]]\n",
    "    X2 = X[:, eldofs[2]]\n",
    "    X3 = X[:, eldofs[3]]\n",
    "    PyPlot.plot(get_coords(X1)[1,:]', get_coords(X1)[2,:]', args...; kwargs...)\n",
    "    PyPlot.plot(get_coords(X2)[1,:]', get_coords(X2)[2,:]', args...; kwargs...)\n",
    "    PyPlot.plot(get_coords(X3)[1,:]', get_coords(X3)[2,:]', args...; kwargs...)\n",
    "    axis(\"off\")\n",
    "end\n",
    "figure(figsize=(4, 5))\n",
    "plot(X, \"-k\")\n",
    "plot(x, \"--r\")\n",
    "xlim(-0.1, 4.1)\n",
    "ylim(-0.1, 6.1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Julia 0.4.1",
   "language": "julia",
   "name": "julia-0.4"
  },
  "language_info": {
   "file_extension": ".jl",
   "mimetype": "application/julia",
   "name": "julia",
   "version": "0.4.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}
