{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Data structures\n",
    "\n",
    "Author(s): Jukka Aho\n",
    "\n",
    "**Abstract**: Description of basis data structures. In this notebook the concepts of `Increment`, `TimeStep`, `DiscreteField`, `ContinuousField`, `FieldSet`, `SpatialBasis` and `TemporalBasis` are intoduced. By combining these atomic structures one is able to form finite elements and interpolate it's fields in time and spatial domain.\n",
    "\n",
    "- `Increment` is the most atomic structure. It's a vector-like object with 1 dimension. Each element in `Increment` can be scalar, vector or tensor (2 or 4 order). It's easy to extend `Increment` to have other data types too.\n",
    "- `TimeStep` is container for increments in certain time $t$.\n",
    "- `DefaultDiscreteField` is container for timesteps for a single field.\n",
    "- `FieldSet` is container for all fields.\n",
    "- `DefaultContinuousField` can have continuous field variables.\n",
    "\n",
    "## Revision history\n",
    "\n",
    "### 2015-06-14\n",
    "- Initial version.\n",
    "\n",
    "### 2015-09-25\n",
    "- Complete rewrite. The main ideas proposed in earlier version didn't work.\n",
    "\n",
    "### 2015-10-29\n",
    "- Third iteration.\n",
    "\n",
    "### 2015-11-03\n",
    "- Starts to be ready. Introduced symbolic fields."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Data fields on elements\n",
    "\n",
    "Typical element structure so far:\n",
    "\n",
    "    type MyElement <: Element\n",
    "        connectivity :: Array{Int, 1}  # describes how dofs of this element is connected to another elements in global level\n",
    "        basis :: Basis  # describes how to interpolate fields\n",
    "        fields :: ???\n",
    "    end\n",
    "\n",
    "- Fields must be interpolable, in space $\\mathbb{C}^n \\times \\mathbb{R}$, i.e. $f(\\boldsymbol{\\xi}, t) = \\sum_i \\phi_i(\\boldsymbol{\\xi}) f_i(t) = \\sum_i \\phi_i(\\boldsymbol{\\xi}) \\sum_j \\varphi_j(t) f_{ij}$, where $f_{ij}$ is scalar, tensor or vector defined in element area $e$ by some basis functions $\\phi(\\boldsymbol{\\xi})$ and $\\varphi(t)$. Parameter $t$ is normally considered as \"time\" and $\\xi$ is dimensionless coordinate. Parameter $t$ has not necessarily to be time, it could be for example angle $\\alpha \\in [-2\\pi, 2\\pi]$ or similar.\n",
    "- We store mainly three fields, scalar field, vector field, tensor field. Field may or may not be dependent from parameters $\\xi$ or $t$.\n",
    "- $t$ is discretized to several steps $\\{t_0, t_1, \\ldots, t_n\\}$. Each discrete time $t_i$ may contain several iterations until convergence. We want to save and get access to all of this data if needed.\n",
    "- So in practice we have a set of fields $f(\\boldsymbol{\\xi})$ over time domain $t$. Typically some fields, like Geometry, is introduced only in time $t_0$. Some other fields, like boundary load, may be \"active\" only on some time $\\hat{t} \\subset t$. Some care must be taken of how to extrapolate field variables.\n",
    "- In the simplest case (simple nonlinear quasistatic analysis), we have for instance $t \\in [0, 1]$ where boundary conditions are set in $t_0$ and load is set in $t_1$. We may use adaptive strategies to shorten time if convergence issues araises."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Creating discrete fields\n",
    "\n",
    "Here's the strategy in short: each non-linear iteration is `Increment`, what is a vector-like object containing data. Elements of `Increment` can be scalars, vectors or tensors. `Increment` belongs to `TimeStep`. `TimeStep` contains one or more increments. Then we have `Field` which contains all timesteps. And finally we have `FieldSet` which contains all fields.\n",
    "\n",
    "    FieldSet -> Field -> TimeStep -> Increment -> data\n",
    "\n",
    "for example,\n",
    "\n",
    "    FieldSet -> Field -> TimeStep -> Increment -> data\n",
    "    FieldSet -> \"temperature\" -> 0.0 -> 1 -> [1,2,3,4]\n",
    "                                        2 -> [2,3,4,5]\n",
    "    FieldSet -> \"temperature\" -> 1.0 -> 1 -> [3,4,5,6]\n",
    "                                        2 -> [5,6,7,8]\n",
    "\n",
    "and so on. Here we create a `FieldSet` containing one field `temperature` which contains two `TimeStep`s, two `Increment`s in both of them."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Dict{ASCIIString,JuliaFEM.Field} with 1 entry:\n",
       "  \"temperature\" => JuliaFEM.DefaultDiscreteField([JuliaFEM.TimeStep(1.0,JuliaFE…"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "using JuliaFEM: Increment, TimeStep, Field, FieldSet, DefaultDiscreteField\n",
    "\n",
    "fs = FieldSet()\n",
    "i1 = Increment([1, 2, 3])\n",
    "i2 = Increment([2, 3, 4])\n",
    "t1 = TimeStep(1.0, Increment[i1, i2])\n",
    "i3 = Increment([2, 3, 4])\n",
    "i4 = Increment([3, 4, 5])\n",
    "t2 = TimeStep(2.0, Increment[i3, i4])\n",
    "f1 = DefaultDiscreteField(TimeStep[t1, t2])\n",
    "fs[\"temperature\"] = f1\n",
    "fs"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Accessing last increment of last timestep of temperature can be done in the following way:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3-element JuliaFEM.Increment{Int64}:\n",
       " 3\n",
       " 4\n",
       " 5"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "fs[\"temperature\"][2][2]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Because the model is deep and quite heavy to type some \"shortcuts\" are provided, but keep in mind that everything is there in place. Here's the shortened version:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "([1,2,3,4],[1,2,3,4])"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "fs = FieldSet()  # create empty fieldset\n",
    "fs[\"temperature\"] = [1, 2, 3, 4]  # create temperature field with time t=0\n",
    "first(fs[\"temperature\"]), last(fs[\"temperature\"])  # pick first and last timestep of temperature"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This way we can easily create discrete scalar, vector and tensor fields."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Dict{ASCIIString,JuliaFEM.Field} with 5 entries:\n",
       "  \"fourth order tensor fi… => JuliaFEM.DefaultDiscreteField([JuliaFEM.TimeStep(…\n",
       "  \"constant scalar field\"  => JuliaFEM.DefaultDiscreteField([JuliaFEM.TimeStep(…\n",
       "  \"vector field\"           => JuliaFEM.DefaultDiscreteField([JuliaFEM.TimeStep(…\n",
       "  \"scalar field\"           => JuliaFEM.DefaultDiscreteField([JuliaFEM.TimeStep(…\n",
       "  \"second order tensor fi… => JuliaFEM.DefaultDiscreteField([JuliaFEM.TimeStep(…"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "fs2 = FieldSet()\n",
    "fs2[\"constant scalar field\"] = 1\n",
    "fs2[\"scalar field\"] = [1, 2, 3, 4]\n",
    "fs2[\"vector field\"] = reshape(collect(1:8), 2, 4)\n",
    "fs2[\"second order tensor field\"] = reshape(collect(1:3*3*4), 3, 3, 4)\n",
    "fs2[\"fourth order tensor field\"] = reshape(collect(1:3*3*3*3*4), 3, 3, 3, 3, 4)\n",
    "fs2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Or even more compactly:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Dict{ASCIIString,JuliaFEM.Field} with 3 entries:\n",
       "  \"density\"     => JuliaFEM.DefaultDiscreteField([JuliaFEM.TimeStep(0.0,JuliaFE…\n",
       "  \"geometry\"    => JuliaFEM.DefaultDiscreteField([JuliaFEM.TimeStep(0.0,JuliaFE…\n",
       "  \"temperature\" => JuliaFEM.DefaultDiscreteField([JuliaFEM.TimeStep(0.0,JuliaFE…"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "FieldSet(\"geometry\" => [1, 2, 3, 4], \"temperature\" => [0, 0, 0, 0], \"density\" => 7850)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "By default using this \"fast typing\" fields are defined at time $t=0.0$."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.0"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "fs2[\"vector field\"][end].time  # pick last timestep of field \"vector field\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Creating new field with several time steps defined can also be done compactly. Each tuple has time and increment data."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO: time on last timestep: 1.0\n"
     ]
    }
   ],
   "source": [
    "t0 = TimeStep(0.0, Increment([1, 2, 3, 4]))\n",
    "t1 = TimeStep(0.5, Increment([2, 3, 4, 5]))\n",
    "t2 = TimeStep(1.0, Increment([1, 1, 1, 1]))\n",
    "fs2[\"time series\"] = [t0, t1, t2]\n",
    "\n",
    "info(\"time on last timestep: $(fs2[\"time series\"][end].time)\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Or even without explicitly expressing time. In that case time step size is 1 second starting from 0."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "ename": "LoadError",
     "evalue": "LoadError: MethodError: `convert` has no method matching convert(::Type{Float64}, ::Array{Int64,1})\nThis may have arisen from a call to the constructor Float64(...),\nsince type constructors fall back to convert methods.\nClosest candidates are:\n  call{T}(::Type{T}, ::Any)\n  convert(::Type{Float64}, !Matched::Int8)\n  convert(::Type{Float64}, !Matched::Int16)\n  ...\nwhile loading In[8], in expression starting on line 1",
     "output_type": "error",
     "traceback": [
      "LoadError: MethodError: `convert` has no method matching convert(::Type{Float64}, ::Array{Int64,1})\nThis may have arisen from a call to the constructor Float64(...),\nsince type constructors fall back to convert methods.\nClosest candidates are:\n  call{T}(::Type{T}, ::Any)\n  convert(::Type{Float64}, !Matched::Int8)\n  convert(::Type{Float64}, !Matched::Int16)\n  ...\nwhile loading In[8], in expression starting on line 1",
      "",
      " in convert at /home/jukka/.julia/v0.4/JuliaFEM/src/fields.jl:233",
      " in call at essentials.jl:56",
      " in setindex! at dict.jl:641"
     ]
    }
   ],
   "source": [
    "fs2[\"time series 2\"] = [1, 2], [2, 3], [3, 4]\n",
    "fs2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Adding another field, with different time."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2-element Array{JuliaFEM.TimeStep,1}:\n",
       " JuliaFEM.TimeStep(0.0,JuliaFEM.Increment[[1,2,3,4]])\n",
       " JuliaFEM.TimeStep(1.0,JuliaFEM.Increment[[2,3,4,5]])"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "T0 = first(fs[\"temperature\"])  # pick first timestep (or to be spesific, last increment of first timestep)\n",
    "T1 = Increment(T0 + 1)  # create new increment from old one\n",
    "timestep = TimeStep(1.0, T1)  # create new timestep at t=1.0\n",
    "push!(fs[\"temperature\"], timestep)  # push to field"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Normal stuff like dot product works:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(20,20,20)"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "S1 = Increment([1, 2, 3])\n",
    "S2 = Increment([2, 3, 4])\n",
    "dot(S1, S2), dot([1, 2, 3], S2), dot(S1, [2, 3, 4])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3-element Array{Float64,1}:\n",
       " 1.5\n",
       " 2.5\n",
       " 3.5"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "1/2*(S1 + S2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3-element Array{Int64,1}:\n",
       "  5\n",
       "  8\n",
       " 11"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "dot([1, 2], Increment[S1, S2])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Creating empty `Increment`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "4-element JuliaFEM.Increment{Array{Float64,1}}:\n",
       " [0.0,0.0]\n",
       " [0.0,0.0]\n",
       " [0.0,0.0]\n",
       " [0.0,0.0]"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "f = zeros(Increment, 2, 4)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Create similar increment with new data:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "4-element JuliaFEM.Increment{Array{Float64,1}}:\n",
       " [1.0,1.0]\n",
       " [1.0,1.0]\n",
       " [1.0,1.0]\n",
       " [1.0,1.0]"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "g = similar(f, ones(8))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Creating continuous (and discrete) fields\n",
    "\n",
    "In previous section the concept of discrete fields was demonstrated. `DefaultDiscreteField` is subtype of `DiscreteField` which is subtype of `Field`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "true"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "using JuliaFEM: DefaultDiscreteField, DiscreteField, Field\n",
    "DefaultDiscreteField <: DiscreteField <: Field"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "There exists another type of fields too: `ContinuousField`s. Like the name already suggests it stores continuous time and spatial domain and it can be used to write custom fields. It needs to be callable. In this example `ContinuousField` is created which returns 1x4 dimensional array defined in $\\boldsymbol\\xi \\in [-1,1]^2, t\\in[0,1]$:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "using JuliaFEM: FieldSet, DefaultContinuousField, ContinuousField"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "MyContinuousField()"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "type MyContinuousField <: ContinuousField\n",
    "end\n",
    "function Base.call(f::MyContinuousField, xi::Vector, time::Number)\n",
    "    time/4*[(1-xi[1])*(1-xi[2]) (1+xi[1])*(1-xi[2]) (1+xi[1])*(1+xi[2]) (1-xi[1])*(1+xi[2])]\n",
    "end\n",
    "f = MyContinuousField()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1x4 Array{Float64,2}:\n",
       " 0.25  0.25  0.25  0.25"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "f([0.0, 0.0], 1.0)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Dict{ASCIIString,JuliaFEM.Field} with 1 entry:\n",
       "  \"basis\" => MyContinuousField()"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "fs = FieldSet()\n",
    "fs[\"basis\"] = MyContinuousField()\n",
    "fs"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1x4 Array{Float64,2}:\n",
       " 0.25  0.25  0.25  0.25"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "fs[\"basis\"]([0.0, 0.0], 1.0)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Again we have a shortcut to quickly define continuous fields:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "6.0"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "fs[\"continuous field\"] = (xi, t) -> xi[1]*xi[2]*t\n",
    "fs[\"continuous field\"]([1.0, 2.0], 3.0)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Naturally we can pass another fields or even fieldsets to continuous field to make fields depend from each other. Here's another example, where `ContinuousField` takes another field and operates it with some function."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "call (generic function with 1264 methods)"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "type MyContinuousField2 <: ContinuousField\n",
    "    basis :: Function\n",
    "    discrete_field :: DiscreteField\n",
    "end\n",
    "function Base.call(field::MyContinuousField2, xi::Vector, time::Number=1.0)\n",
    "    data = last(field.discrete_field) # get the last timestep last increment\n",
    "    basis = field.basis(xi) # evaluate basis at point ξ.\n",
    "    sum([basis[i]*data[i] for i=1:length(data)]) # sum results\n",
    "end"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now we create two fields, one is discrete and another is continuous taking discrete field as input argument:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "MyContinuousField2(basis,JuliaFEM.DefaultDiscreteField([JuliaFEM.TimeStep(0.0,JuliaFEM.Increment[[1,2,3,4]])]))"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "fs = FieldSet()\n",
    "fs[\"discrete field\"] = [1, 2, 3, 4]\n",
    "basis(xi) = 1/4*[\n",
    "    (1-xi[1])*(1-xi[2]),\n",
    "    (1+xi[1])*(1-xi[2]),\n",
    "    (1+xi[1])*(1+xi[2]),\n",
    "    (1-xi[1])*(1+xi[2])]\n",
    "fs[\"continuous field\"] = MyContinuousField2(basis, fs[\"discrete field\"])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Results:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(2.5,[1,2,3,4])"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "fs[\"continuous field\"]([0.0, 0.0], 1.0), last(fs[\"discrete field\"])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Next we add another discrete field and see that continuous field \"connecting\" to discrete field updates accordingly:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Dict{ASCIIString,JuliaFEM.Field} with 2 entries:\n",
       "  \"continuous field\" => MyContinuousField2(basis,JuliaFEM.DefaultDiscreteField(…\n",
       "  \"discrete field\"   => JuliaFEM.DefaultDiscreteField([JuliaFEM.TimeStep(0.0,Ju…"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "T0 = last(fs[\"discrete field\"])\n",
    "push!(fs[\"discrete field\"], TimeStep(1.0, Increment(T0 + 1.0)))  # push to field\n",
    "fs"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Updated results, notice how the value of continuous field changes according to the update of discrete field."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(3.5,[2.0,3.0,4.0,5.0])"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "fs[\"continuous field\"]([0.0, 0.0], 1.0), last(fs[\"discrete field\"])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "What we just did is that we actually interpolated discrete field using continuous functions. We evaluated discrete field using bilinear basis at midpoint of \"element\":"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(2.5,3.5)"
      ]
     },
     "execution_count": 27,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "1/4*(1+2+3+4), 1/4*(2+3+4+5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "From continuous field we can also go back to discrete fields, if needed. Here we evaluate continuous field in four discrete points, let's call them to Gauss quadrature points."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "getindex (generic function with 126 methods)"
      ]
     },
     "execution_count": 28,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "using JuliaFEM: DiscreteField\n",
    "type MyDiscreteField <: DiscreteField\n",
    "    discrete_points :: Vector\n",
    "    continuous_field :: ContinuousField\n",
    "end\n",
    "Base.length(field::MyDiscreteField) = length(field.discrete_points)\n",
    "Base.endof(field::MyDiscreteField) = endof(field.discrete_points)\n",
    "Base.last(field::MyDiscreteField) = Float64[field[i] for i=1:length(field)]\n",
    "function Base.getindex(field::MyDiscreteField, idx::Int64)\n",
    "    field.continuous_field(field.discrete_points[idx])\n",
    "end"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "4-element Array{Float64,1}:\n",
       " 2.75598\n",
       " 3.08932\n",
       " 3.91068\n",
       " 4.24402"
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "discrete_points = 1.0/sqrt(3.0)*Vector[[-1, -1], [1, -1], [1, 1], [-1, 1]]\n",
    "fs[\"discrete field 2\"] = MyDiscreteField(discrete_points, fs[\"continuous field\"])\n",
    "last(fs[\"discrete field 2\"])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Basically summing the above values together we have (almost) done numerical integration over element area.  By using these two simple concepts we are able to construct rest of the results.\n",
    "\n",
    "## Interpolation\n",
    "\n",
    "In earlier discussion the concepts of `DiscreteField` and `ContinuousField` were introduced, so that now we can define discrete set of values and continuous functions. It has also been shown how fields can depend from each other such a way that we can interpolate continuous field from discrete field and vice versa.\n",
    "\n",
    "This motivates us to create continuous fields that are interpolated from discrete fields using some polynomial basis. By thinking this way interpolation is nothing more than just an application of the earlier results already shown.\n",
    "\n",
    "Some fields has special meaning in JuliaFEM. Typically one needs to define at least discrete field `geometry` and continuous field `basis` so that basic interpolation is working on element. \n",
    "\n",
    "Interpolation of fields works of course both in time and spatial dimension. Here's a simple example showing the main concept."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "using JuliaFEM: Basis"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Yep, it's just continuous field:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "true"
      ]
     },
     "execution_count": 31,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Basis <: ContinuousField"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Basis + DiscreteField = Interpolation:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "JuliaFEM.Basis(basis,dbasis)"
      ]
     },
     "execution_count": 32,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "basis(xi) = 1/4*[(1-xi[1])*(1-xi[2])   (1+xi[1])*(1-xi[2])   (1+xi[1])*(1+xi[2])   (1-xi[1])*(1+xi[2])]\n",
    "dbasis(xi) = 1/4*[\n",
    "    -(1-xi[2])    (1-xi[2])   (1+xi[2])  -(1+xi[2])\n",
    "    -(1-xi[1])   -(1+xi[1])   (1+xi[1])   (1-xi[1])]\n",
    "\n",
    "N = Basis(basis, dbasis)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1x4 Array{Float64,2}:\n",
       " 0.25  0.25  0.25  0.25"
      ]
     },
     "execution_count": 33,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "N([0.0, 0.0])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Interpolation in time domain\n",
    "\n",
    "To interpolate in time domain, call `DiscreteField` given time. Result is an `Increment` interpolated to that time. Here we interpolate the position of particle moving $y = \\frac{1}{2}t^2$ at time $t=1.0$."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(linspace(0.0,2.0,5),[0.0,0.125,0.5,1.125,2.0])"
      ]
     },
     "execution_count": 34,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "t = linspace(0, 2, 5)\n",
    "y = 1/2*t.^2\n",
    "t, y"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1-element JuliaFEM.Increment{Float64}:\n",
       " 0.5"
      ]
     },
     "execution_count": 35,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "timesteps = TimeStep[]\n",
    "for (ti, yi) in zip(t, y)\n",
    "    increment = Increment(yi)\n",
    "    push!(timesteps, TimeStep(ti, increment))\n",
    "end\n",
    "\n",
    "#fs[\"particle position\"] = t, 1/2*t.^2\n",
    "pos = Field(timesteps)\n",
    "pos(1.0)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "It's also possible to take time derivatives using finite difference approximation. To do so, call `Field` with time and additional argument `Val{:diff}`. Again, same example:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1-element Array{Float64,1}:\n",
       " 1.0"
      ]
     },
     "execution_count": 36,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "velocity = pos(1.0, Val{:diff})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Interpolation in spatial domain"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To interpolate in spatial domain, call `Basis` with `Increment` and coordinate $\\boldsymbol\\xi$. Increments to interpolate are the latest ones in each time step by default. Result depends from the content of the field. If it is scalar field, result will be scalar, if it's vector the result will be vector and so on.\n",
    "\n",
    "Let's have a $\\Omega = \\left[0,1\\right]\\times\\left[0,1\\right] \\in \\mathbb{R}^2$ domain with a displacement field\n",
    "\\begin{equation}\n",
    "\\mathbf{u}\\left(X_1, X_2\\right)=t\\begin{bmatrix}X_{1}\\left(X_{2}+1\\right)\\\\\n",
    "X_{1}\\left(4X_{2}-1\\right)\n",
    "\\end{bmatrix}.\n",
    "\\end{equation}\n",
    "\n",
    "Gradient is\n",
    "\\begin{equation}\n",
    "\\mbox{grad}\\left(\\mathbf{u}\\right)=t\\begin{bmatrix}X_{2}+1 & X_{1}\\\\\n",
    "4X_{2}-1 & 4X_{1}\n",
    "\\end{bmatrix}\n",
    "\\end{equation}\n",
    "\n",
    "We look for a displacement in center point of the domain at time $t=1.0$:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "X = Field(Vector{Float64}[[0.0,0.0], [1.0,0.0], [1.0,1.0], [0.0,1.0]])\n",
    "u = Field(\n",
    "    (0.5, Vector{Float64}[[0.0, 0.0], [0.5, -0.5], [1.0, 1.5], [0.0, 0.0]]),\n",
    "    (1.5, Vector{Float64}[[0.0, 0.0], [1.5, -1.5], [3.0, 4.5], [0.0, 0.0]]));"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "([[0.0,0.0],[1.0,0.0],[1.0,1.0],[0.0,1.0]],Any[[0.0,0.0],[1.0,-1.0],[2.0,3.0],[0.0,0.0]])"
      ]
     },
     "execution_count": 38,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# evaluate fields in time t=1.0 -> Increments\n",
    "X_increment = X(1.0)\n",
    "u_increment = u(1.0)\n",
    "X_increment, u_increment"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2-element Array{Float64,1}:\n",
       " 1.25\n",
       " 1.0 "
      ]
     },
     "execution_count": 39,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# evaluate geometry + displacement at midpoint, basically x = X + u\n",
    "N(X_increment, [0.0, 0.0]) + N(u_increment, [0.0, 0.0])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Calculating gradients is also possible, for scalar or vector fields, if passing additional argument `Val{:grad}`. Here's an example of gradient of vector field, i.e, $u_{i,j} = \\frac{\\partial u_i}{\\partial X_j}$:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2x2 Array{Float64,2}:\n",
       " 1.5  0.5\n",
       " 1.0  2.0"
      ]
     },
     "execution_count": 40,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "N = Basis(basis, dbasis)\n",
    "gradu = N(X_increment, u_increment, [0.0, 0.0], Val{:grad})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "One can of course combine the above results. In this case field must be first interpolated in time domain and after that in spatial domain. So calculating time derivative of small strain tensor $\\epsilon$ is basically:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2x2 Array{Float64,2}:\n",
       " 1.5   0.75\n",
       " 0.75  2.0 "
      ]
     },
     "execution_count": 41,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "du = u(1.0, Val{:diff})\n",
    "grad_du = N(X_increment, du, [0.0, 0.0], Val{:grad})\n",
    "strain_rate = 1/2*(grad_du + grad_du')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Symbolic fields\n",
    "\n",
    "There's still one kind of field not yet introduced, namely *symbolic fields*:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "JuliaFEM.SymbolicField(\"displacement\")"
      ]
     },
     "execution_count": 42,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "u = Field(\"displacement\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The idea is to write equation in expression form using tiny symbolic interface and evaluate equations afterwards. Main benefits: \n",
    "\n",
    "- possibility to simplify equations symbolically\n",
    "- prettier syntax\n",
    "- \"lazy evaluation\" gives possibilities to optimize performance.\n",
    "\n",
    "Here's a preliminary example how this could work:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# in future this will be something like this:\n",
    "# u = Field(\"displacement\")\n",
    "# eps = 1/2*(grad(u) + grad(u)')\n",
    "# strain_rate = diff(eps)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(1 / 2) * (grad(diff(displacement)) + grad(diff(displacement))')"
     ]
    }
   ],
   "source": [
    "# now this is\n",
    "expr = JuliaFEM.Expression(\"1/2*(grad(diff(displacement)) + grad(diff(displacement))')\")\n",
    "print(expr.expr)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Evaluation of expression: call `Basis` with `FieldSet`, $\\boldsymbol\\xi$ and time as arguments:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO: expression = (1 / 2) * (grad(diff(displacement)) + grad(diff(displacement))')\n",
      "INFO: expression = 1 / 2\n",
      "INFO: new expression: 1 / 2\n",
      "INFO: expression = grad(diff(displacement)) + grad(diff(displacement))'\n",
      "INFO: expression = grad(diff(displacement))\n",
      "INFO: inside gradient operator\n",
      "INFO: gradient args: Any[:grad,:(diff(displacement))]\n",
      "INFO: expression inside gradient\n",
      "INFO: grad: evaluate field diff(displacement)\n",
      "INFO: taking time derivative of displacement\n",
      "INFO: expression = diff(displacement)\n",
      "INFO: inside diff operator\n",
      "INFO: data = Any[[0.0,0.0],[1.0,-1.0],[2.0,3.0],[0.0,0.0]]\n",
      "INFO: evaluate geometry\n",
      "INFO: evaluate gradient\n",
      "INFO: expression = grad(diff(displacement))'\n",
      "INFO: transpose\n",
      "INFO: expression = transpose(grad(diff(displacement)))\n",
      "INFO: expression = grad(diff(displacement))\n",
      "INFO: inside gradient operator\n",
      "INFO: gradient args: Any[:grad,:(diff(displacement))]\n",
      "INFO: expression inside gradient\n",
      "INFO: evaluate gradient\n",
      "INFO: new expression: transpose([1.5 0.5\n",
      " 1.0 2.0])\n",
      "INFO: new expression: [1.5 0.5\n",
      " 1.0 2.0] + transpose([1.5 0.5\n",
      " 1.0 2.0])\n",
      "INFO: new expression: (1 / 2) * ([1.5 0.5\n",
      " 1.0 2.0] + transpose([1.5 0.5\n",
      " 1.0 2.0]))\n"
     ]
    }
   ],
   "source": [
    "X = Field(Vector[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0]])\n",
    "u = Field(\n",
    "    (0.5, Vector[[0.0, 0.0], [0.5, -0.5], [1.0, 1.5], [0.0, 0.0]]),\n",
    "    (1.5, Vector[[0.0, 0.0], [1.5, -1.5], [3.0, 4.5], [0.0, 0.0]]))\n",
    "fs = FieldSet()\n",
    "fs[\"geometry\"] = X\n",
    "fs[\"displacement\"] = u\n",
    "xi = [0.0, 0.0]\n",
    "time = 1.0\n",
    "result = N(expr, fs, xi, time);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       ":((1 / 2) * (2x2 Array{Float64,2}:\n",
       " 1.5  0.5\n",
       " 1.0  2.0 + transpose(2x2 Array{Float64,2}:\n",
       " 1.5  0.5\n",
       " 1.0  2.0)))"
      ]
     },
     "execution_count": 46,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "result"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "What happened is that fields were automatically interpolated and substituted to the equation. The result is `Expr` and can be evaluated like usual:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2x2 Array{Float64,2}:\n",
       " 1.5   0.75\n",
       " 0.75  2.0 "
      ]
     },
     "execution_count": 47,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "strain_rate = eval(result)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Julia 0.4.0",
   "language": "julia",
   "name": "julia-0.4"
  },
  "language_info": {
   "file_extension": ".jl",
   "mimetype": "application/julia",
   "name": "julia",
   "version": "0.4.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}
