Files
JuliaFEM.jl/notebooks/2015-06-14-data-structures.ipynb
T

930 lines
116 KiB
Plaintext
Raw Normal View History

{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Data structures\n",
"\n",
"Author(s): Jukka Aho (<jukka.aho@kapsi.fi>)\n",
"\n",
"####Abstract\n",
"A proposal how to work with element, nodal etc. data. Main point is that we have one simple principle: *everything is field*. That is, variables are recorded to nodes, element nodes, element gauss points dynamically and there's nothing \"fixed\". This allows us to write very elegant \"microsolvers\" and very easy model structure. Concept will be very easy to unit test. Moreover, when we keep structures as simple as possible, the concept is easy to understand."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"First we talk about this \"model\". It could be something very easy, like struct of dicts. We don't care *what* we are recording to field or *how many* fields exists for single node/element/model, but we are caring about *where* (in topological sense) we need to save values. For example:\n",
"\n",
"### Model fields\n",
"Global variables like \n",
"- mesh file name\n",
"- results file name\n",
"- time\n",
"\n",
"### Nodal fields\n",
"- primal variable after solution\n",
"\n",
"### Element fields\n",
"- body id\n",
"- connected nodes\n",
"- shape function family\n",
"- dimension\n",
"- everything else we don't have to interpolate\n",
"\n",
"### Element node fields\n",
"Defined in element local nodes\n",
"- density\n",
"- young's modulus\n",
"- thickness\n",
"- boundary load\n",
"- if shape functions are defined, we can interpolate these inside element\n",
"\n",
"### Element gauss fields\n",
"Defined in element gauss points\n",
"- stress tensor\n",
"- pretty much everything we need during the solution\n",
"- these can be extrapolated to element local nodes"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"So, what this means in practice?"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"type Model\n",
" mo # For global variables\n",
" no # For nodes\n",
" el # For elements\n",
" elno # For element nodes\n",
" elga # For element gauss points\n",
"end"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"new_model (generic function with 1 method)"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"function new_model()\n",
" \"\"\"Initialize empty model.\n",
" \"\"\"\n",
" return Model(Dict(), Dict(), Dict(), Dict(), Dict())\n",
"end"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"new_field (generic function with 1 method)"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"function new_field(field_type, field_name)\n",
" d = Dict()\n",
" setindex!(field_type, d, field_name)\n",
" return d\n",
"end"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"And that's it. We can do a lot of things using that simple construction when we are dealing with dynamic languages. "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Structure is following:\n",
"\n",
" model.mo[parameter] = value\n",
" model.no[field][node_id] = value\n",
" model.el[field][element_id] = value\n",
" model.elno[field][element_id][local_node_id] = value\n",
" model.elga[field][element_id][local_gauss_id] = value\n",
"\n",
"`Value` can be scalar, vector, tensor, composite type or whatever is needed. \n",
"\n",
"`Field` can also be whatever needed: temperature, displacement, velocity, pressure, density, etc.\n",
"\n",
"Examples:\n",
"\n",
" model.mo[\"simulation_time\"] = datetime.datetime.now()\n",
" model.no[\"coords\"][node_id] = [1, 2, 3]\n",
" model.el[\"color\"][element_id] = \"red\"\n",
" model.elno[\"thickness\"][element_id][1] = 5 # 1 here stands for \"first local node of element\"\n",
" model.elno[\"thickness\"][element_id][2] = 10\n",
" model.elga[\"stress\"][element_id] = [1 2; 3 4]\n",
"\n",
"We apply *solvers* to our model. Solver is generic name for a function that uses model data and updates it."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Example 1, normal directions in global nodes\n",
"\n",
"Next we can start writing our microsolvers. Our goal is to calculate normal direction in nodal points, averaged from element nodes."
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"INFO: Loading help data...\n"
]
},
{
"data": {
"image/png": [
"iVBORw0KGgoAAAANSUhEUgAAAqMAAAIUCAYAAAAuUCWlAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAPYQAAD2EBqD+naQAAIABJREFUeJzs3X10VOW9//1PIEUEhGUxxUpBKUrkyYOiEWiqNZFA58AgKEWEUhLSWgwYIwb4IUIQEYkoIinFVaJFgYA8isYDmATFcCwBouekNgGkVAwKEmixMSBNmPuPHr0JYZQN2bmumf1+rZU/zuRhf2e9z7Zf5mlHBAKBgAAAAAADGpkeAAAAAN7FMgoAAABjWEYBAABgDMsoAAAAjGEZBQAAgDEsowAAADCGZRQAAADGsIwCAADAGJZRAAAAGMMyCgAAAGMuaBn98ssvNX36dPXv31/f//731ahRIy1ZsuScP1taWqr+/fvrsssuU+vWrTVq1ChVVFRc1NAAAAAIDxe0jB45ckQzZ87U7t271aNHD0lSREREnZ8rLy/Xbbfdpr/+9a+aPXu2HnnkEeXm5qpv377617/+dXGTAwAAIORFXsgvXXXVVTp06JB+8IMfaNeuXbrlllvO+XNPPvmkTpw4offff18/+tGPJEkxMTHq27ev/vjHP+rXv/71hU8OAACAkHdBj4w2adJEP/jBDyRJgUAg6M+tWbNGAwYM+GYRlaT4+Hh16tRJr7766oUcGgAAAGHEtTcwHTx4UEeOHNHNN99c53u33HKL3n//fbcODQAAgBDh2jL62WefSZJ++MMf1vneD3/4Qx07dozXjQIAAHjcBb1m9HycOHFCknTJJZfU+V7Tpk2/+Znvfe97tb5XUVGhTZs26ZprrtGll17q1ngAAAC4QCdOnNDf/vY39evXT1dcccVF/S3XltGvF8mvvvqqzvdOnjxZ62fOtGnTJo0cOdKtsQAAAFBPli5dqhEjRlzU33BtGf366fmvn64/02effabWrVvXeVRUkq655hpJ/75znTt3dms8XKB77rlHq1evNj0GzoE2dqOPvWhjL9rYq7S0VCNHjvxmb7sYri2jbdu2VVRUlHbs2FHne0VFRd98PunZvn60tHPnzrrpppvcGg8X6Hvf+x5dLEUbu9HHXrSxF23sVx8vqXT1cqB333233njjDZWXl39zW35+vvbu3auhQ4e6eWi4JDo62vQICII2dqOPvWhjL9p4wwU/MpqVlaV//OMf+vTTTyVJGzZs0IEDByRJDz74oFq2bKkpU6Zo1apVuuOOO5Samqp//vOfevrpp3XDDTcoMTGxfu4BAAAAQtYFL6PPPPOMPv74Y0n/vhTounXrtHbtWkVERGjUqFFq2bKlfvSjH+mdd97Rww8/rMmTJ+uSSy7RgAED9Mwzz5zz9aIAAADwlgteRvfv339eP9elSxdt3LjxQg8DywwYMMD0CAiCNnajj71oYy/aeIOrrxlF+HnjjTdMj4AgaGM3+tiLNvaijTewjMKRjIwM0yMgCNrYjT72oo29aOMNLKNwhI/YsBdt7EYfe9HGXrTxBpZRAAAAGMMyCgAAAGNYRuFIdna26REQBG3sRh970cZetPEGllE4UlxcbHoEBEEbu9HHXrSxF228ISIQCARMD3Gm4uJi9ezZU7t27eKFywAAABaqz32NR0YBAABgDMsoAAAAjGEZBQAAgDEso3DE7/ebHgFB0MZu9LEXbexFG29gGYUj48aNMz0CgqCN3ehjL9rYizbewDIKRxISEkyPgCBoYzf62Is29qKNN7CMAgAAwBiWUQAAABjDMgpH1q9fb3oEBEEbu9HHXrSxF228gWUUjuTk5JgeAUHQxm70sRdt7EUbb+ByoAAAAHCEy4ECAAAgLLCMAgAAwBiWUQAAABjDMgpHEhMTTY+AIGhjN/rYizb2oo03sIzCEa6GYS/a2I0+9qKNvWjjDbybHgAAAI7wbnoAAACEBZZRAAAAGMMyCkcKCwtNj4AgaGM3+tiLNvaijTewjMKRzMxM0yMgCNrYjT72oo29aOMNLKNwZMWKFaZHQBC0sRt97EUbe9HGG1hG4UizZs1Mj4AgaGM3+tiLNvaijTewjAIAAMAYllEAAAAYwzIKR9LT002PgCBoYzf62Is29qKNN7CMwpH27dubHgFB0MZu9LEXbexFG2/gcqAAAABwhMuBAgAAICywjAIAAMAYllE4UlZWZnoEBEEbu9HHXrSxF228gWUUjkycONH0CAiCNnajj71oYy/aeAPLKBzJysoyPQKCoI3d6GMv2tiLNt7AMgpH+JgNe9HGbvSxF23sRRtvYBkFAACAMSyjAAAAMIZlFI7MmTPH9AgIgjZ2o4+9aGMv2ngDyygcqaqqMj0CgqCN3ehjL9rYizbewOVAAQAA4AiXAwUAAEBYYBkFAACAMSyjcKSiosL0CAiCNnajj71oYy/aeAPLKBxJSkoyPQKCoI3d6GMv2tiLNt7AMgpHMjIyTI+AIGhjN/rYizb2oo03sIzCET7hwF60sRt97EUbe9HGG1hGAQAAYAzLKAAAAIxhGYUj2dnZpkdAELSxG33sRRt70cYbWEbhSHFxsekREARt7EYfe9HGXrTxBi4HCgAAAEe4HCgAAADCAssoAAAAjGEZBQAAgDEso3DE7/ebHgFB0MZu9LEXbexFG29gGYUj48aNMz0CgqCN3ehjL9rYizbewDIKRxISEkyPgCBoYzf62Is29qKNN7CMAgAAwBiWUQAAABjj+jK6c+dODRo0SFdddZWaN2+uzp07a+bMmTpx4oTbh4YL1q9fb3oEBEEbu9HHXrSxF228wdVltKSkRLGxsSopKdFDDz2k+fPnq3fv3po+fbqGDx/u5qHhkpycHNMjIAja2I0+9qKNvWjjDZFu/vEVK1bo1KlTys3NVefOnSVJycnJOn36tF5++WUdP35crVq1cnME1LOVK1eaHgFB0MZu9LEXbexFG29w9ZHRSy+9VJL0gx/8oNbtV155pRo3bqwmTZq4eXgAAABYztVlNCkpSW3atNGYMWP0P//zP/rkk0+0cuVKLVq0SA8++OA3yyoAAAC8ydWn6a+66ipt27ZNPp9PN9544ze3T506VY8//ribhwYAAEAIcPWR0cOHD+vnP/+5AoGA/vCHP2jt2rVKSkrSrFmz9Lvf/c7NQ8MliYmJpkdAELSxG33sRRt70cYbXF1GZ86cqYMHD2rLli0aM2aM7rrrLi1evFi/+tWvNGnSJB07dizo7/p8Pvn9/lpfvXv3rvMxD5s3bz7ntWtTUlKUnZ1d67bi4mL5/X5VVFTUun369OmaM2dOrdsOHDggv9+vsrKyWrcvWLBA6enptW6rqqqS3+9XYWFhrdtzcnLOeSINGzYsZO/HmVfDCOX7caZwuR9lZWVhcT/CpcfZ9yMhISEs7ocUHj3OvB9nX+UnVO/H2cLhfiQkJITF/ZBCu0dOTs43u1iHDh3Uo0cPpaWl1fk7FyoiEAgE6u2vnaVHjx667LLL9O6779a6fd26dbr77ruVl5enuLi4Wt8rLi5Wz549tWvXLt10001ujQYAAIALVJ/7mquPjP7rX/9STU3NOW+XpOrqajcPDwAAAMu5uozedNNNKi4u1t69e2vdnpOTo8aNG+uGG25w8/AAAACwnKvLaHp6uho1aqSf/vSneuKJJ7Rw4UL5fD699tprSkpK0pVXXunm4eGCs19/AnvQxm70sRdt7EUbb3B1Gb3hhhv09ttv68Ybb9TTTz+ttLQ07d+/X08++aR+//vfu3louCQzM9P0CAiCNnajj71oYy/aeIOrb2C6ELyByW5VVVVq1qyZ6TFwDrSxG33sRRt70cZeIfMGJoQf/qNgL9rYjT72oo29aOMNLKMAAAAwhmUUAAAAxrCMwpGzr/IAe9DGbvSxF23sRRtvYBmFI+3btzc9AoKgjd3oYy/a2Is23sC76QEAAOAI76YHAABAWGAZBQAAgDEso3CkrKzM9AgIgjZ2o4+9aGMv2ngDyygcmThxoukREARt7EYfe9HGXrTxBpZROJKVlWV6BARBG7vRx160sRdtvIFlFI7wMRv2oo3d6GMv2tiLNt7AMgoAAABjWEYBAABgDMsoHJkzZ47pERAEbexGH3vRxl608QaWUTh
],
"text/plain": [
"Figure(PyObject <matplotlib.figure.Figure object at 0x10dd04390>)"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/plain": [
"(-2,10)"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"using PyPlot\n",
"plot([0, 4, 7], [0, 3, 7], \"-ko\")\n",
"grid()\n",
"xlim(-2, 10)\n",
"ylim(-2, 10)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"First obvious thing is to generate or read geometry to model."
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Model(Dict{Any,Any}(),{\"connects_to\"=>{2=>[1,2],3=>[2],1=>[1]},\"coords\"=>{2=>[4.0,3.0],3=>[7.0,7.0],1=>[0.0,0.0]}},{\"connectivity\"=>{2=>[2,3],1=>[1,2]},\"dimension\"=>{2=>1,1=>1}},Dict{Any,Any}(),Dict{Any,Any}())\n"
]
}
],
"source": [
"function dummymeshreader(model)\n",
" \"\"\"Read mesh to model. Rewrite older mesh. This microsolver is for demonstration purposes.\"\"\"\n",
"\n",
" # Create new field for node coordinates. Coordinates is nodal field.\n",
" coords = new_field(model.no, \"coords\")\n",
" reverse_connectivity = new_field(model.no, \"connects_to\")\n",
"\n",
" # Create new field for connectivity and dimension. This is element property.\n",
" connectivity = new_field(model.el, \"connectivity\")\n",
" dimension = new_field(model.el, \"dimension\")\n",
"\n",
" # Do actual reading, call another microsolver which reads geometry e.g. ABAQUS\n",
"\n",
" # Here we just create three nodes and two linear segments to demonstrate the concept\n",
" coords[1] = [0.0, 0.0]\n",
" coords[2] = [4.0, 3.0]\n",
" coords[3] = [7.0, 7.0]\n",
" connectivity[1] = [1, 2]\n",
" connectivity[2] = [2, 3]\n",
" dimension[1] = 1\n",
" dimension[2] = 1\n",
" reverse_connectivity[1] = [1]\n",
" reverse_connectivity[2] = [1, 2]\n",
" reverse_connectivity[3] = [2]\n",
" return model\n",
"end\n",
"\n",
"function test_dummymeshreader()\n",
" m = new_model() # Create new model\n",
" dummymeshreader(m) # Apply microsolver \"dummymeshreader\" to model m\n",
" println(m)\n",
" @assert m.no[\"coords\"][1] == [0.0, 0.0]\n",
" @assert m.el[\"connectivity\"][1] == [1, 2]\n",
"end\n",
"\n",
"test_dummymeshreader()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Our next microsolver assigns some function space to elements. We use linear Lagrange polynomials. We test routine with some simple interpolation. We want to find midpoint of first element [0.0, 0.0] -> [4.0, 3.0] but because we can interpolate only from element nodes, we need some utility functions to move field variables from nodes to element nodes. So we need to write another microsolver which makes such transformation."
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{2=>[4.0,3.0],1=>[0.0,0.0]}\n"
]
}
],
"source": [
"function no2elno(model, field)\n",
" \"\"\"Move field from nodes to element nodes.\"\"\"\n",
" f = new_field(model.elno, field)\n",
" for (elid, node_ids) in model.el[\"connectivity\"] # loop through element connectivity list\n",
" f[elid] = Dict()\n",
" for (i, c) in enumerate(node_ids)\n",
" f[elid][i] = model.no[field][c]\n",
" end\n",
" end\n",
"end\n",
"\n",
"function test_no2elno()\n",
" m = new_model()\n",
" dummymeshreader(m)\n",
" no2elno(m, \"coords\")\n",
" @assert \"coords\" in keys(m.elno) # new element node field\n",
" el = m.elno[\"coords\"][1] # first element ..\n",
" println(el)\n",
" @assert el[1] == [0.0, 0.0]\n",
" @assert el[2] == [4.0, 3.0]\n",
"end\n",
"\n",
"test_no2elno()"
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[1.0 0.0]\n",
"[0.0 1.0]\n",
"{2=>[4.0,3.0],1=>[0.0,0.0]}\n",
"[2.0,1.5]\n"
]
}
],
"source": [
"function get_shape_functions(family, dim, order)\n",
" if family == \"Lagrange\"\n",
" if dim == 1\n",
" if order == 1\n",
" return xi -> [0.5*(1-xi) 0.5*(1+xi)]\n",
" end\n",
" end\n",
" end\n",
" throw(\"Unknown shape function requested.\")\n",
"end\n",
"\n",
"function interpolate(shape_functions, elnofield, xi)\n",
" result = zeros(size(elnofield[1]))\n",
" sf = shape_functions(xi)\n",
" for i=1:length(sf)\n",
" result += sf[i]*elnofield[i]\n",
" end\n",
" return result\n",
"end\n",
"\n",
"function test_shape_functions()\n",
" sf = get_shape_functions(\"Lagrange\", 1, 1)\n",
" println(sf(-1))\n",
" println(sf(1))\n",
" @assert sf(-1) == [1 0]\n",
" @assert sf(1) == [0 1]\n",
"end\n",
"\n",
"function test_define_fuction_space()\n",
" m = new_model()\n",
" dummymeshreader(m) # Read mesh\n",
" no2elno(m, \"coords\") # Move nodal field (coordinates) from nodes to element nodes\n",
" sf = get_shape_functions(\"Lagrange\", 1, 1)\n",
" midpoint = interpolate(sf, m.elno[\"coords\"][1], 0.0)\n",
" println(m.elno[\"coords\"][1])\n",
" println(midpoint)\n",
" @assert midpoint == [2.0, 1.5]\n",
"end\n",
"\n",
"test_shape_functions()\n",
"test_define_fuction_space()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Our next microsolver introduces new field, namely normal direction in *element nodes*"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[0.6,-0.8]\n",
"{2=>[0.6,-0.8],1=>[0.6,-0.8]}\n"
]
}
],
"source": [
"function get_shape_function_derivatives(family, dim, order)\n",
" if family == \"Lagrange\"\n",
" if dim == 1\n",
" if order == 1\n",
" return xi -> [-0.5 0.5]\n",
" end\n",
" end\n",
" end\n",
" throw(\"Unknown shape function requested.\")\n",
"end\n",
"\n",
"function calculate_element_normals(model)\n",
" \"\"\"A solver to calculate boundary element normals.\"\"\"\n",
" normals = new_field(model.elno, \"normals\")\n",
" coords = model.elno[\"coords\"]\n",
" dsf = get_shape_function_derivatives(\"Lagrange\", 1, 1)\n",
" for (elid, node_ids) in model.el[\"connectivity\"]\n",
" normals[elid] = Dict()\n",
"\n",
" t = interpolate(dsf, coords[elid], -1)\n",
" n = [t[2], -t[1]]\n",
" n /= norm(n)\n",
" normals[elid][1] = n/norm(n)\n",
"\n",
" t = interpolate(dsf, coords[elid], 1)\n",
" n = [t[2], -t[1]]\n",
" n /= norm(n)\n",
" normals[elid][2] = n/norm(n)\n",
"\n",
" end\n",
"end\n",
"\n",
"function test_element_normal_field_calculation()\n",
" m = new_model()\n",
" dummymeshreader(m) # Read mesh\n",
" no2elno(m, \"coords\") # Move nodal field (coordinates) from nodes to element nodes\n",
" calculate_element_normals(m)\n",
" n = [3, -4]\n",
" n /= norm(n)\n",
" println(n)\n",
" element_normals = m.elno[\"normals\"][1] # Normals of first element\n",
" println(element_normals)\n",
" @assert element_normals[1] == n\n",
" @assert element_normals[2] == n\n",
"end\n",
"\n",
"test_element_normal_field_calculation()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Our last solver reads element node values and transforms them back to nodal field values. We now have different values for each element node so we have to somehow combine them. Let's take simple average for now."
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[0.7,-0.7]\n",
"{\"connects_to\",\"normals\",\"coords\"}\n",
"{2=>[0.7,-0.7],3=>[0.8,-0.6],1=>[0.6,-0.8]}\n"
]
}
],
"source": [
"function elno2no(model, field)\n",
" \"\"\"Move field from element nodes to global nodes.\"\"\"\n",
" f = new_field(model.no, field)\n",
" for (nid, elids) in model.no[\"connects_to\"] # loop through nodes \"reverse\" connectivity list\n",
" # find variable dimension from first element first local variable\n",
" vardim = size(model.elno[field][elids[1]][1])\n",
" f[nid] = zeros(vardim)\n",
" for elid in elids\n",
" local_node_id = findfirst(model.el[\"connectivity\"][elid], nid)\n",
" f[nid] += model.elno[field][elid][local_node_id]\n",
" end\n",
" f[nid] /= length(elids)\n",
" end\n",
"end\n",
"\n",
"function test_elno2no()\n",
" m = new_model()\n",
" dummymeshreader(m)\n",
" no2elno(m, \"coords\") # Move nodal field (coordinates) from nodes to element nodes\n",
" calculate_element_normals(m) # Calculate normals in elements\n",
" #println(m.elno[\"normals\"])\n",
" elno2no(m, \"normals\") # Move element node normals back to global nodes\n",
" n1 = [3, -4]\n",
" n1 /= norm(n1)\n",
" n2 = [4, -3]\n",
" n2 /= norm(n2)\n",
" navg = 0.5*(n1+n2)\n",
" println(navg)\n",
" println(keys(m.no))\n",
" println(m.no[\"normals\"])\n",
" @assert m.no[\"normals\"][1] == n1 # Normal of first node\n",
" @assert m.no[\"normals\"][2] == navg # Normal of middle node\n",
" @assert m.no[\"normals\"][3] == n2 # Normal of third node\n",
"end\n",
"\n",
"test_elno2no()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"So our final solver combines all above to find correct normal directions in boundary nodes:"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{2=>[0.7071067811865475,-0.7071067811865475],3=>[0.8,-0.6],1=>[0.6,-0.8]}\n"
]
}
],
"source": [
"function calculate_node_normals(model)\n",
" no2elno(model, \"coords\") # Move nodal field (coordinates) from nodes to element nodes\n",
" calculate_element_normals(model) # Calculate normals in elements\n",
" elno2no(model, \"normals\") # Move element node normals back to global nodes\n",
" for (nid, ndir) in model.no[\"normals\"]\n",
" model.no[\"normals\"][nid] /= norm(model.no[\"normals\"][nid])\n",
" end\n",
"end\n",
"\n",
"function test_calculate_node_normals()\n",
" m = new_model()\n",
" dummymeshreader(m)\n",
" calculate_node_normals(m)\n",
" n1 = [3, -4]\n",
" n1 /= norm(n1)\n",
" n2 = [4, -3]\n",
" n2 /= norm(n2)\n",
" navg = 0.5*(n1+n2)\n",
" navg /= norm(navg)\n",
" println(m.no[\"normals\"])\n",
" @assert m.no[\"normals\"][1] == n1 # Normal of first node\n",
" @assert m.no[\"normals\"][2] == navg # Normal of middle node\n",
" @assert m.no[\"normals\"][3] == n2 # Normal of third node\n",
"end\n",
" \n",
"test_calculate_node_normals()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Summary for example 1"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"2 {2=>[7.0,7.0],1=>[4.0,3.0]}\n",
"1 {2=>[4.0,3.0],1=>[0.0,0.0]}\n",
"[4.0,3.0]\n",
"[0.7071067811865475,-0.7071067811865475]\n"
]
},
{
"data": {
"image/png": [
"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
],
"text/plain": [
"Figure(PyObject <matplotlib.figure.Figure object at 0x10df93f50>)"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/plain": [
"(-3,10)"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"m = new_model()\n",
"dummymeshreader(m)\n",
"calculate_node_normals(m)\n",
"for i in keys(m.el[\"connectivity\"])\n",
" c = m.elno[\"coords\"][i]\n",
" println(i, \" \", c)\n",
" nor1 = m.elno[\"normals\"][i][1]\n",
" nor2 = m.elno[\"normals\"][i][2]\n",
" plot([c[1][1], c[2][1]], [c[1][2], c[2][2]], \"-ko\")\n",
" plot([c[1][1], c[1][1]+nor1[1]], [c[1][2], c[1][2]+nor1[2]], \"-k\")\n",
" plot([c[2][1], c[2][1]+nor2[1]], [c[2][2], c[2][2]+nor2[2]], \"-k\")\n",
"end\n",
"\n",
"node2_normal = m.no[\"normals\"][2]\n",
"node2_coords = m.no[\"coords\"][2]\n",
"println(node2_coords)\n",
"println(node2_normal)\n",
"plot([node2_coords[1], node2_coords[1]+node2_normal[1]*2],\n",
" [node2_coords[2], node2_coords[2]+node2_normal[2]*2], \"-r\")\n",
"\n",
"grid()\n",
"xlim(-3, 10)\n",
"ylim(-3, 10)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Here small black lines are element *local* normal directions. Red line is *global* normal direction, defined in node point. Element local directions are in field `model.elno[\"normals\"][elid][id]` while global direction defined in nodes are in `model.no[\"normals\"][node_id]`."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Example 2, poisson equation in 1d"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Next we solve PDE $ku'' = 1$ in domain $I=[0, 1]$ with boundary conditions $u(0)=0$, $u'(1)=1$. For this we need to define field variable for $k$, let's call it to stiffness and it must be defined in element nodes because we have to integrate over it. $k$ not necessarily have to be constant that way. After standard FE assembly and solution discrete $u$ values are in nodal field, let's call it to displacement. \n",
"\n",
"Accurate solution is $u=\\frac{1}{2}x^2$ for $k=1$.\n",
"\n",
"First we must again write some solver to generate geometry. In 1d this is easy enough (even for me ;))."
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"generate_1d_mesh (generic function with 1 method)"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"function generate_1d_mesh(model, N, I)\n",
" \"\"\"Generate 1D mesh with N nodes for interval I.\"\"\"\n",
" coords = new_field(model.no, \"coords\")\n",
" connectivity = new_field(model.el, \"connectivity\")\n",
" X = linspace(I[1], I[2], N)\n",
" for (i, x) in enumerate(X)\n",
" model.no[\"coords\"][i] = [x]\n",
" end\n",
" for i=1:N-1\n",
" model.el[\"connectivity\"][i] = [i, i+1]\n",
" end\n",
"end"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"node 1 x = [0.0]\n",
"node 2 x = [0.25]\n",
"node 3 x = [0.5]\n",
"node 4 x = [0.75]\n",
"node 5 x = [1.0]\n",
"element 1 connects to nodes [1,2]\n",
"element 2 connects to nodes [2,3]\n",
"element 3 connects to nodes [3,4]\n",
"element 4 connects to nodes [4,5]\n"
]
}
],
"source": [
"N = 5 # Number of nodes in model\n",
"mo = new_model()\n",
"generate_1d_mesh(mo, N, [0, 1])\n",
"no2elno(mo, \"coords\") # again move global coords to node local coords\n",
"for i=1:N\n",
" println(\"node \", i, \" x = \", mo.no[\"coords\"][i])\n",
"end\n",
"for i=1:N-1\n",
" println(\"element \", i, \" connects to nodes \", mo.el[\"connectivity\"][i])\n",
"end"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Our elements need stiffness and force. Let's define new field for them. Note that both are defined on *element nodes* (we must interpolate them in assembly)."
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"Dict{Any,Any} with 4 entries:\n",
" 4 => {2=>[1.0],1=>[1.0]}\n",
" 2 => {2=>[1.0],1=>[1.0]}\n",
" 3 => {2=>[1.0],1=>[1.0]}\n",
" 1 => {2=>[1.0],1=>[1.0]}"
]
},
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"new_field(mo.elno, \"stiffness\")\n",
"new_field(mo.elno, \"force\")\n",
"for i=1:N-1\n",
" mo.elno[\"stiffness\"][i] = Dict()\n",
" mo.elno[\"stiffness\"][i][1] = [1.0]\n",
" mo.elno[\"stiffness\"][i][2] = [1.0]\n",
" mo.elno[\"force\"][i] = Dict()\n",
" mo.elno[\"force\"][i][1] = [1.0]\n",
" mo.elno[\"force\"][i][2] = [1.0]\n",
"end\n",
"mo.elno[\"stiffness\"]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Next the solver. For simplicity boundary conditions are handled \"silly\". We also skip defining function spaces for now."
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"poissonsolver1d (generic function with 1 method)"
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"function poissonsolver1d(model)\n",
" # Note that we could save all intermediate variables like jacobian, stiffness etc. to\n",
" # model gauss points. TODO.\n",
" ipoints = [-sqrt(1/3) sqrt(1/3)]\n",
" iweights = [1 1]\n",
" K = zeros(N, N)\n",
" F = zeros(N)\n",
" basis = get_shape_functions(\"Lagrange\", 1, 1)\n",
" #println(basis(-1), \" \", basis(1))\n",
" dbasis = get_shape_function_derivatives(\"Lagrange\", 1, 1)\n",
" for i=1:N-1 # Loop through elements\n",
"\n",
" # create local stiffness matrix and force vector\n",
" k = zeros(2, 2)\n",
" f = zeros(2)\n",
" for j=1:length(iweights)\n",
" wi = iweights[j]\n",
" ip = ipoints[j]\n",
" N = basis(ip)\n",
" dNdxi = dbasis(ip)\n",
" J = norm(interpolate(dbasis, model.elno[\"coords\"][i], ip))\n",
" dNdx = dNdxi*J^-1\n",
"\n",
" # stiffness and force in integration point, interpolated from element nodes\n",
" # (not have to be constant then necessary)\n",
" stiff = interpolate(basis, model.elno[\"stiffness\"][i], ip)[1]\n",
" force = interpolate(basis, model.elno[\"force\"][i], ip)[1]\n",
" k += wi * stiff * dNdx'*dNdx * J\n",
" # FIXME: There's a mistake somewhere ...\n",
" f -= wi * force * N' * J\n",
" #println(N')\n",
" end\n",
" \n",
" # assemble to global matrices\n",
" gdofs = model.el[\"connectivity\"][i]\n",
" K[gdofs, gdofs] += k\n",
" F[gdofs] += f\n",
" end\n",
" # add neumann boundary condition to RHS and solve\n",
" F[end] += 1.0\n",
" u = zeros(N)\n",
" free_dofs = 2:N\n",
" u[free_dofs] = K[free_dofs, free_dofs] \\ F[free_dofs]\n",
" #println(K[free_dofs, free_dofs])\n",
" #println(F[free_dofs])\n",
" #println(u)\n",
" # create new field and save results to nodes\n",
" new_field(model.no, \"displacement\")\n",
" for i=1:N\n",
" model.no[\"displacement\"][i] = u[i]\n",
" end\n",
"end"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"poissonsolver1d(mo)"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"image/png": [
"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vfVWnXPOOUpMTFStWrXUvn17zZkz54iP37Nnj/785z+rWbNmql69umrXrq0WLVpoxIgRKiwsPOHHhiInJydsrwXvo2+70Ldd6DuKfPONs2B/Sop07bWm05RiSI0h/fr107Zt23TFFVdo8ODBuv322/X111+rZ8+eGjVqVJnHbtmyRUlJSXr66aeVmJiofv36qU+fPjrzzDM1YcIEFRQUnNBjQ5WVlRW214L30bdd6Nsu9B0lAgGpb1/nIqnnnjOdpgy7D/cXFkp5ee6/b9OmUmJi2F9206ZNatSoUZltBw4c0HXXXaexY8fqvvvuU7169SRJd9xxh/Lz8/X0009r2LBhZZ6zZ88eVa9evfSfg3lsqLKzs8P2WvA++rYLfduFvqPErFnOIf6FC6XatU2nKcPuITUvT2rVyv33zc2VkpLC/rKHD6iSVKVKFfXr108rVqzQihUr1KNHD+Xm5uqDDz5Qy5Ytyw2dknTKKaf8KmrFHwsAAKLIjh3Sgw9Kd94pde5sOk05dg+pTZs6A6OJ942A/Px8paWlafny5dq2bZv27dtX5vfbt2+XJH3wwQeSpGuuuea4rxnMYwEAQJQIBKR775WqVpXGjzed5ojsHlITEyOyR9OEr776Sm3atNEPP/ygyy67TNdee61q1aqlypUra8uWLZo5c6aKiookST/88IMkqX79+sd93WAeCwAAosScOc4h/tdekzx6VJQLp2LEc889pz179mj69OlasWKFJkyYoMcee0yjRo0qt6By7f+ec/LNN98c93WDeWw4pKSkuPI+8Ab6tgt924W+PWznTmngQOlPf5I8fPtahtQY8cUXXyguLk633nprud+tWrWqzD+3bdtWkrR48WIFAoFjvm4wjw0H7lBiF/q2C33bhb496tBh/oQEadIk02mOiSE1RjRq1EiBQEDvvPNOme2LFy/WtGnTymxLSkrSJZdcoo0bNyotLa3ca3333XelpwYE89hw6N69e9heC95H33ahb7vQt0fNni298Yb0t7959jD/IXafkxpD+vXrp8zMTHXt2lVdunRR3bp19cknn2jx4sW67bbbyi0F8uKLL+qKK67QI488ovnz5+vyyy9XIBDQv/71Ly1dulSff/65GjRoEPRjAQCAR23fLg0aJPXs6enD/IcwpMaI3//+93rnnXc0cuRIvfnmmyouLlaLFi20YMEC1apVq9yQ2rBhQ/n9fqWnpysnJ0dTpkxRtWrV1LBhQz388MOqU6fOCT0WAAB40KFF+6tVk55/3nSaCmFIjSFt27bV8uXLj/i7gwcPltt2yimnaOzYsRo7duxxXzuYx4Zi9erVat++fUTfA95B33ahb7vQt8fMnCktWuQc6v/Nb0ynqRDOSYWnpKenm44AF9G3XejbLvTtId984xzmv+su6frrTaepMIZUeMrcuXNNR4CL6Nsu9G0X+vaIQEC65x6pRg1pwgTTaYLC4X54SmJioukIcBF924W+7ULfHpGZKb39tvTmm9J/1z6PFuxJBQAAiEX5+dLgwVKvXlKnTqbTBI0hFQAAINYEAlKfPlLNmlF3mP8QhlR4ytChQ01HgIvo2y70bRf6Nuz//k9atkzKyJBq1TKd5oQwpMJTuCmAXejbLvRtF/o26MsvpYcflu67T4ri29MypMJTHnjgAdMR4CL6tgt924W+DTl4UEp
],
"text/plain": [
"Figure(PyObject <matplotlib.figure.Figure object at 0x10e1eb890>)"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"for i=1:N\n",
" plot(mo.no[\"coords\"][i][1], mo.no[\"displacement\"][i], \"ko\")\n",
"end\n",
"x = linspace(0, 1)\n",
"plot(x, 1/2*x.^2, \"-r\", label=\"acc\")\n",
"legend(loc=\"best\")\n",
"grid()\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Julia 0.3.8",
"language": "julia",
"name": "julia-0.3"
},
"language_info": {
"name": "julia",
"version": "0.3.8"
}
},
"nbformat": 4,
"nbformat_minor": 0
}