{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Von Mises material with isotropic and kinematic hardening\n",
    "\n",
    "Author(s): Olli Väinölä <olli.vainola@student.oulu.fi>\n",
    "\n",
    "In this notebook is an small tutorial, how to create a Von Mises material with hardening. Equations are formulated into rate depended form. Code may or may not include some bugs.."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Theory section\n",
    "\n",
    "### Continuum equations\n",
    "\n",
    "Stress:\n",
    "\n",
    "$\\sigma = C : \\epsilon^e$\n",
    "\n",
    "$C$ is material tensor and $\\epsilon$ total strain.\n",
    "Total strain is divided into elastic and plastic part:\n",
    "\n",
    "$\\epsilon = \\epsilon^e + \\epsilon^p$\n",
    "\n",
    "now let's define a strain rate, which is strain increment divide with time increment $dt$\n",
    "\n",
    "$\\frac{d\\epsilon}{dt} = \\dot \\epsilon =  \\dot \\epsilon^e + \\dot \\epsilon^p$\n",
    "\n",
    "Now same procedure for stress and substitute $\\dot \\epsilon^e$\n",
    "\n",
    "$\\dot \\sigma = C : \\dot \\epsilon^e =  C : (\\dot \\epsilon - \\dot \\epsilon^p )$\n",
    "\n",
    "Only thing to do is to define yield function. Now we're using Von Mises material:\n",
    "\n",
    "$f(\\sigma - X, R(\\alpha)) = \\sqrt{3J_2(\\sigma-X))} - R(\\alpha)$ = 0\n",
    "\n",
    "$f(\\sigma, \\sigma_y) = \\sqrt{3J_2(\\sigma))} - \\sigma_y$ = 0\n",
    "\n",
    "$J_2 = \\frac{1}{2}s : s$ \n",
    "\n",
    "$s = \\sigma - \\frac{1}{3}\\sigma I$\n",
    "\n",
    "$I = eye(3)$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "# imports\n",
    "using PyPlot\n",
    "using ForwardDiff\n",
    "using NLsolve"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's create a isotropic Hooke material."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "6x6 Array{Float64,2}:\n",
       " 2.69231e5  1.15385e5  1.15385e5  0.0        0.0        0.0      \n",
       " 1.15385e5  2.69231e5  1.15385e5  0.0        0.0        0.0      \n",
       " 1.15385e5  1.15385e5  2.69231e5  0.0        0.0        0.0      \n",
       " 0.0        0.0        0.0        1.53846e5  0.0        0.0      \n",
       " 0.0        0.0        0.0        0.0        1.53846e5  0.0      \n",
       " 0.0        0.0        0.0        0.0        0.0        1.53846e5"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "\n",
    "\n",
    "\"\"\"\n",
    "Create a isotropic Hooke material matrix C \n",
    "\n",
    "More information: # http://www.efunda.com/formulae/solid_mechanics/mat_mechanics/hooke_isotropic.cfm\n",
    "\n",
    "Parameters\n",
    "----------\n",
    "    E: Float\n",
    "        Elastic modulus\n",
    "    ν: Float\n",
    "        Poisson constant\n",
    "\n",
    "Returns\n",
    "-------\n",
    "    Array{Float64, (6,6)}\n",
    "\"\"\"\n",
    "function hookeStiffnessTensor(E, ν)\n",
    "    a = 1 - ν\n",
    "    b = 1 - 2*ν\n",
    "    c = 1 + ν\n",
    "    multiplier = E / (b * c)\n",
    "    return Float64[a ν ν 0 0 0;\n",
    "                   ν a ν 0 0 0;\n",
    "                   ν ν a 0 0 0;\n",
    "                   0 0 0 b 0 0;\n",
    "                   0 0 0 0 b 0;\n",
    "                   0 0 0 0 0 b].*multiplier\n",
    "end\n",
    "\n",
    "# Pick material values\n",
    "E = 200.0e3\n",
    "ν =  0.3\n",
    "C = hookeStiffnessTensor(E, ν)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "# Defining equations for the calculation\n",
    "\n",
    "Functions are defined for strain controller simulation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 178,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "calculate_stress (generic function with 1 method)"
      ]
     },
     "execution_count": 178,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# using vectors with double contradiction\n",
    "# http://www-2.unipv.it/compmech/teaching/available/const_mod/const_mod_mat-review_notation.pdf\n",
    "M = [1 0 0 0 0 0;\n",
    "     0 1 0 0 0 0;\n",
    "     0 0 1 0 0 0;\n",
    "     0 0 0 2 0 0;\n",
    "     0 0 0 0 2 0;\n",
    "     0 0 0 0 0 2;]\n",
    "\n",
    "\"\"\"\n",
    "Equivalent tensile stress. \n",
    "\n",
    "More info can be found from: https://en.wikipedia.org/wiki/Von_Mises_yield_criterion\n",
    "    Section: Reduced von Mises equation for different stress conditions\n",
    "\n",
    "Parameters\n",
    "----------\n",
    "    σ: Array{Float64, 6}\n",
    "        Stress in Voigt notation\n",
    "\n",
    "Returns\n",
    "-------\n",
    "    Float\n",
    "\"\"\"\n",
    "function σₑ(σ)\n",
    "    s = σ[1:6] - 1/3 * sum([σ[1], σ[2], σ[3]]) * [1 1 1 0 0 0]'\n",
    "    return sqrt(3/2 * s' * M * s)[1]\n",
    "end\n",
    "\n",
    "# Some extra data for testing purposes ...\n",
    "#ss = σ[1]\n",
    "#return sqrt(sum(ss.^2))\n",
    "\n",
    "\"\"\"\n",
    "Von Mises Yield criterion\n",
    "\n",
    "More info can be found from: http://csm.mech.utah.edu/content/wp-content/uploads/2011/10/9tutorialOnJ2Plasticity.pdf\n",
    "\n",
    "Parameters\n",
    "----------\n",
    "    σ: Array{Float64, 6}\n",
    "        Stress in Voigt notation\n",
    "    k: Float64\n",
    "        Material constant, Yield limit\n",
    "\n",
    "Returns\n",
    "-------\n",
    "    Float\n",
    "\"\"\"\n",
    "function vonMisesYield(σ, k)\n",
    "    σₑ(σ) - k\n",
    "end\n",
    "\n",
    "\n",
    "\"\"\"\n",
    "Function for NLsolve. Inside this function are the functions where we want to find root.\n",
    "Ψ is the yield function below. Functions defined here:\n",
    "\n",
    "    dσ - C (dϵ - dλ*dΨ/dσ) = 0\n",
    "                 σₑ(σ) - k = 0\n",
    "\n",
    "Parameters\n",
    "----------\n",
    "    params: Array{Float64, 7}\n",
    "        Array containing values from solver\n",
    "    dϵ: Array{Float64, 6}\n",
    "        Strain rate vector in Voigt notation\n",
    "    C: Array{Float64, (6, 6)}\n",
    "        Material tensor\n",
    "    k: Float\n",
    "        Material constant, yield limit\n",
    "    Δt: Float\n",
    "        time increment\n",
    "    σ_begin:Array{Float64, 6}\n",
    "        Stress vector in Voigt notation\n",
    "    p: Float\n",
    "        Accumulated plastic strain\n",
    "    X: Array{Float64, 6}\n",
    "        Kinematic hardening tensor\n",
    "    R: Function\n",
    "        Calculates isotropic hardening as a function of accumulated plastic strain\n",
    "    α:Float\n",
    "        Accumulated kinematic evolution variable\n",
    "    dα: Function\n",
    "        Calculates kinematic evolution variables rate as a function of accumulated plastic slip rate\n",
    "    fX: Function\n",
    "        Calculates the kinematic \n",
    "\n",
    "Returns\n",
    "-------\n",
    "    Array{Float64, 7}, return values for solver\n",
    "\"\"\"\n",
    "function G(params, dϵ, C, σ_begin, p, X, R, fX, ϵp)\n",
    "    \n",
    "    # Initializing wrapper\n",
    "    yield(pars) = vonMisesYield(pars, R(p))\n",
    "    dfdσ        = ForwardDiff.gradient(yield)\n",
    "    \n",
    "    # Initializing variables\n",
    "    dσ = params[1:6]\n",
    "    dλ = params[end]\n",
    "\n",
    "    # Total strain\n",
    "    σ_new = σ_begin + dσ\n",
    "    σ_shifted = vec(σ_new - X)\n",
    "    \n",
    "    # Creating wrapper for gradient\n",
    "    dΨdσ = dfdσ(σ_shifted)[1:6]\n",
    "\n",
    "    # Calculating plastic strain rate\n",
    "    dϵp = dλ * dΨdσ\n",
    "\n",
    "    # Updating material parameters\n",
    "    ϵp_new = ϵp + dϵp\n",
    "    dp = sqrt(2 / 3 * double_contraction(dϵp))\n",
    "    p_test = p + dp\n",
    "    \n",
    "    # new hardening variables\n",
    "    R_ = R(p_test)\n",
    "    X_ = fX(ϵp_new)\n",
    "\n",
    "    # Evaluating equations\n",
    "    function_1 = dσ - C * (dϵ - dϵp)\n",
    "    function_2 = vonMisesYield(σ_new - X_, R_)\n",
    "    [vec(function_1); function_2]\n",
    "end\n",
    "\n",
    "\n",
    "\"\"\"\n",
    "This is a novice implementation for double contraction, a=b:c\n",
    "\n",
    "Parameters\n",
    "----------\n",
    "    a: Array{Float64, 6}\n",
    "\n",
    "Returns\n",
    "-------\n",
    "    Float\n",
    "\"\"\"\n",
    "function double_contraction(a; b=a)\n",
    "    indexes = [1, 2, 3, 4, 5, 6, 4, 5, 6]\n",
    "    summation = 0\n",
    "    for i in indexes\n",
    "        summation += a[i]*b[i]\n",
    "    end\n",
    "    summation\n",
    "end\n",
    "\n",
    "\"\"\"\n",
    "Function which calculates the stress. Also handles if any yielding happens\n",
    "\n",
    "Parameters\n",
    "----------\n",
    "    dϵ: Array{Float64, 6}\n",
    "        Strain rate vector in Voigt notation\n",
    "    Δt: Float\n",
    "        time increment\n",
    "    σ: Array{Float64, 6}\n",
    "        Last stress vector in Voigt notation\n",
    "    C: Array{Float64, (6, 6)}\n",
    "        Material tensor\n",
    "    p: Float\n",
    "        Accumulated plastic strain\n",
    "    X: Array{Float64, 6}\n",
    "        Kinematic hardening tensor\n",
    "    R: Function\n",
    "        Calculates isotropic hardening as a function of accumulated plastic strain\n",
    "    α:Float\n",
    "        Accumulated kinematic evolution variable\n",
    "    dα: Function\n",
    "        Calculates kinematic evolution variables rate as a function of accumulated plastic slip rate\n",
    "    fX: Function\n",
    "        Calculates the kinematic \n",
    "\n",
    "Returns\n",
    "-------\n",
    "    Tuple\n",
    "        returns following parameters: p, X, α, σ. See Parameters for definitions\n",
    "\"\"\"\n",
    "function calculate_stress(dϵ, σ, C, p, X, R, fX, ϵp)\n",
    "\n",
    "    # Test stress\n",
    "    σ_tria = σ + C * dϵ\n",
    "\n",
    "    # Calculating yield\n",
    "    yield = vonMisesYield(σ_tria - X, R(p))\n",
    "\n",
    "    if yield > 0\n",
    "        # Yielding happened\n",
    "        # Creating functions for newton: xₙ₊₁ = xₙ - df⁻¹ * f and initial values\n",
    "        initial_guess = [vec(σ_tria - σ); 0.1]\n",
    "        f(σ_)  = G(σ_, dϵ, C, σ, p, X, R, fX, ϵp)\n",
    "        df     = ForwardDiff.jacobian(f)\n",
    "        \n",
    "        # Calculating root \n",
    "        result = nlsolve(not_in_place(f, df), initial_guess).zero\n",
    "        \n",
    "        # Extracting values\n",
    "        σ  += result[1:6]        \n",
    "        dλ  = result[end]\n",
    "        # Wrapper for gradient\n",
    "        yield_f(σ_) = vonMisesYield(σ_, R(p))\n",
    "        dfdσ_ = ForwardDiff.gradient(yield_f)\n",
    "        dΨdσ = dfdσ_(vec(σ)-X)\n",
    "        \n",
    "        # Stress rate and plastic strain rate\n",
    "        dϵᵖ = dλ * dΨdσ\n",
    "        ϵp = ϵp + dϵᵖ\n",
    "        \n",
    "        # Updating matrial parameters\n",
    "        dp = sqrt(2/3 * double_contraction(dϵᵖ))\n",
    "        p += dp\n",
    "        X = fX(ϵp)\n",
    "    else\n",
    "        σ = σ_tria\n",
    "    end\n",
    "    return (p, X, σ, ϵp)\n",
    "end"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Defining strain history\n",
    "\n",
    "In the ideal plastic example, we only had tension stress. In this example we'll take it a bit further and calculate the cyclic strain"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 192,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Done\n"
     ]
    }
   ],
   "source": [
    "steps = 1000\n",
    "strain_max = 0.003\n",
    "num_cycles = 5\n",
    "\n",
    "ϵ_tot = zeros(Float64, (steps, 6))\n",
    "ϵ_tot2 = zeros(Float64, (steps, 6))\n",
    "ϵ_tot3 = zeros(Float64, (steps, 6))\n",
    "\n",
    "# Adding only strain in x-axis and counting for the poisson effect\n",
    "ϵ_tot[:, 1] = strain_max * sin(2 * pi * linspace(0, num_cycles, steps))\n",
    "ϵ_tot[:, 2] = strain_max * sin(2 * pi * linspace(0, num_cycles, steps)).*-ν\n",
    "ϵ_tot[:, 3] = strain_max * sin(2 * pi * linspace(0, num_cycles, steps)).*-ν\n",
    "println(\"Done\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Hardening evolution equations\n",
    "\n",
    "Followig equations are in charge of evolution of isotropic and kinematic parameters"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 193,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "kinematic_hardening (generic function with 1 method)"
      ]
     },
     "execution_count": 193,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "function isotropic_hardening(ϵp_cum, R0, Q)\n",
    "    return R0 + Q * ϵp_cum\n",
    "end\n",
    "\n",
    "function kinematic_hardening(ϵp, C)\n",
    "    return 2/3 * C * ϵp\n",
    "end"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Simulation\n",
    "\n",
    "Ok, we're good to go! Now we just need to define yield limit and the main loop.\n",
    "\n",
    "This simulation is not time dependent, but since it's already defined in the equations we'll give it value 1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 195,
   "metadata": {
    "collapsed": false,
    "scrolled": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 58404 characters]",
      "text/plain": [
       "PyPlot.Figure(PyObject <matplotlib.figure.Figure object at 0x7f7eba87ab10>)"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "ϵ_last = zeros(Float64, (6)) # Last total strain\n",
    "ϵᵖ = zeros(Float64, (6))  # Plastic strain\n",
    "σ = zeros(Float64, (6, 1)) # Stress\n",
    "\n",
    "ss = Float64[] # plotting stress\n",
    "ee = Float64[] # plotting strain\n",
    "pp = Float64[] # plotting strain\n",
    "\n",
    "# Isotropic hardening\n",
    "σy =  200.0 # yield limit\n",
    "\n",
    "# Kinematic hardening\n",
    "Ck = 2000.0  # saturation hardening C/D\n",
    "X = zeros(Float64, 6) # kinematic hardening tensor\n",
    "\n",
    "p = 0.0 # Accumulated plastic strain\n",
    "\n",
    "# Isotropic hardening\n",
    "R(ϵp_cum) = isotropic_hardening(ϵp_cum, σy, σy)\n",
    "\n",
    "# kinematic hardening\n",
    "fX(ϵp) = kinematic_hardening(ϵp, Ck)\n",
    "\n",
    "for i=1:steps\n",
    "    # Actual calculation\n",
    "    dϵ = reshape(ϵ_tot[i, :, :], (6, 1)) - ϵ_last\n",
    "    p, X, σ, ϵᵖ = calculate_stress(dϵ, σ, C, p, X, R, fX, ϵᵖ)\n",
    "    ϵ_last += dϵ\n",
    "    push!(ss, σ[1])\n",
    "    push!(ee, ϵ_last[1])\n",
    "    push!(pp, p)\n",
    "end\n",
    "PyPlot.plot(ee, ss)\n",
    "PyPlot.title(\"Stress-Strain curve\")\n",
    "PyPlot.xlabel(\"Strain\")\n",
    "PyPlot.ylabel(\"Stress\")\n",
    "PyPlot.grid()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Julia 0.4.1-pre",
   "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
}
