diff --git a/docs/examples/CMakeLists.txt b/docs/examples/CMakeLists.txt
index cb06ae1..9525068 100644
--- a/docs/examples/CMakeLists.txt
+++ b/docs/examples/CMakeLists.txt
@@ -1,60 +1,77 @@
 
 cmake_minimum_required(VERSION 3.0)
 
 if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
     project(GooseFEM-examples)
     find_package(GooseFEM REQUIRED CONFIG)
 endif()
 
 option(WARNINGS "Show build warnings" ON)
 option(SIMD "Enable xsimd" ON)
 option(DEBUG "Enable all assertions" ON)
 
 find_package(HDF5 REQUIRED)
 
 add_library(libraries INTERFACE IMPORTED)
 
 target_link_libraries(libraries INTERFACE GooseFEM ${HDF5_LIBRARIES})
 
 if (SIMD)
     target_link_libraries(libraries INTERFACE xtensor::optimize xtensor::use_xsimd)
 endif()
 
 if (WARNINGS)
     target_link_libraries(libraries INTERFACE GooseFEM::compiler_warnings)
 endif()
 
 if (DEBUG)
     target_link_libraries(libraries INTERFACE GooseFEM::debug)
 endif()
 
 
 # create executable
 
 set(exec "statics_FixedDisplacements_LinearElastic_example")
 set(source "statics/FixedDisplacements_LinearElastic/example.cpp")
 
 add_executable(${exec} ${source})
 
 target_link_libraries(${exec} PRIVATE libraries)
 
 configure_file(
      "statics/FixedDisplacements_LinearElastic/example.py"
      "statics_FixedDisplacements_LinearElastic_example.py" COPYONLY)
 
 configure_file(
      "statics/FixedDisplacements_LinearElastic/plot.py"
      "statics_FixedDisplacements_LinearElastic_plot.py" COPYONLY)
 
 # create executable
 
 set(exec "statics_FixedDisplacements_LinearElastic_manual_partition")
 set(source "statics/FixedDisplacements_LinearElastic/manual_partition.cpp")
 
 add_executable(${exec} ${source})
 
 target_link_libraries(${exec} PRIVATE libraries)
 
 configure_file(
      "statics/FixedDisplacements_LinearElastic/manual_partition.py"
      "statics_FixedDisplacements_LinearElastic_manual_partition.py" COPYONLY)
+
+# create executable
+
+set(exec "statics_MixedPeriodic_LinearElastic_example")
+set(source "statics/MixedPeriodic_LinearElastic/example.cpp")
+
+add_executable(${exec} ${source})
+
+target_link_libraries(${exec} PRIVATE libraries)
+
+configure_file(
+     "statics/MixedPeriodic_LinearElastic/example.py"
+     "statics_MixedPeriodic_LinearElastic_example.py" COPYONLY)
+
+configure_file(
+     "statics/MixedPeriodic_LinearElastic/plot.py"
+     "statics_MixedPeriodic_LinearElastic_plot.py" COPYONLY)
diff --git a/docs/examples/statics/MixedPeriodic_LinearElastic/CMakeLists.txt b/docs/examples/statics/MixedPeriodic_LinearElastic/CMakeLists.txt
index 24c53c3..6019030 100644
--- a/docs/examples/statics/MixedPeriodic_LinearElastic/CMakeLists.txt
+++ b/docs/examples/statics/MixedPeriodic_LinearElastic/CMakeLists.txt
@@ -1,64 +1,14 @@
 cmake_minimum_required(VERSION 3.1)
 
-# basic info
-# ----------
-
-# define a project name
-project(main)
-
-# define empty list of libraries to link
-set(PROJECT_LIBS "")
-
-# copy files to build folder
-configure_file("plot.py" "plot.py" COPYONLY)
-
-# basic compiler options
-# ----------------------
-
-# set optimization level
-set(CMAKE_BUILD_TYPE Release)
-
-# set C++ standard
-set(CMAKE_CXX_STANDARD 14)
-set(CMAKE_CXX_STANDARD_REQUIRED ON)
-
-# set warnings on
-set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic -g")
-
-# enable optimisation
-set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native")
-set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DXTENSOR_USE_XSIMD=ON")
-
-# assertions
-if(NOT ASSERT)
-  add_definitions(-DNDEBUG)
-else()
-  add_definitions(-DXTENSOR_ENABLE_ASSERT=ON)
-endif()
-
-# load packages
-# -------------
-
-# add custom include paths
-if(NOT "$ENV{INCLUDE_PATH}" STREQUAL "")
-  string(REPLACE ":" ";" INCLUDE_LIST "$ENV{INCLUDE_PATH}")
-  include_directories(${INCLUDE_LIST})
-endif()
-
-# load HDF5
-find_package(HDF5 COMPONENTS CXX REQUIRED)
-include_directories(${HDF5_INCLUDE_DIRS})
-set(PROJECT_LIBS ${PROJECT_LIBS} ${HDF5_LIBS} ${HDF5_LIBRARIES})
-
-# load eigen
-find_package(PkgConfig)
-pkg_check_modules(EIGEN3 REQUIRED eigen3)
-include_directories(SYSTEM ${EIGEN3_INCLUDE_DIRS})
-
-# create executable
-# -----------------
-
-add_executable(${PROJECT_NAME} main.cpp)
-
-target_link_libraries(${PROJECT_NAME} ${PROJECT_LIBS})
-
+project(example)
+
+set(HIGHFIVE_USE_XTENSOR 1)
+set(HIGHFIVE_USE_BOOST 0)
+find_package(HighFive REQUIRED)
+find_package(xtensor REQUIRED)
+find_package(GooseFEM REQUIRED)
+find_package(GMatElastic REQUIRED)
+
+add_executable(example example.cpp)
+target_link_libraries(example PRIVATE GMatElastic GooseFEM HighFive)
+target_link_libraries(example INTERFACE xtensor::optimize xtensor::use_xsimd)
diff --git a/docs/examples/statics/MixedPeriodic_LinearElastic/example.cpp b/docs/examples/statics/MixedPeriodic_LinearElastic/example.cpp
new file mode 100644
index 0000000..1ac17c7
--- /dev/null
+++ b/docs/examples/statics/MixedPeriodic_LinearElastic/example.cpp
@@ -0,0 +1,145 @@
+#include <GooseFEM/GooseFEM.h>
+#include <GooseFEM/MatrixPartitioned.h>
+#include <GMatElastic/Cartesian3d.h>
+#include <highfive/H5Easy.hpp>
+
+int main()
+{
+    // mesh
+    // ----
+
+    // define mesh
+    GooseFEM::Mesh::Quad4::Regular mesh(5, 5);
+
+    // mesh dimensions
+    size_t nelem = mesh.nelem();
+    size_t nne = mesh.nne();
+    size_t ndim = mesh.ndim();
+
+    // mesh definitions
+    xt::xtensor<double,2> coor = mesh.coor();
+    xt::xtensor<size_t,2> conn = mesh.conn();
+    xt::xtensor<size_t,2> dofs = mesh.dofs();
+
+    // node sets
+    xt::xtensor<size_t,1> nodesLft = mesh.nodesLeftOpenEdge();
+    xt::xtensor<size_t,1> nodesRgt = mesh.nodesRightOpenEdge();
+    xt::xtensor<size_t,1> nodesTop = mesh.nodesTopEdge();
+    xt::xtensor<size_t,1> nodesBot = mesh.nodesBottomEdge();
+
+    // periodicity and fixed displacements DOFs
+    // ----------------------------------------
+
+    for (size_t j = 0; j < coor.shape(1); ++j) {
+        xt::view(dofs, xt::keep(nodesRgt), j) = xt::view(dofs, xt::keep(nodesLft), j);
+    }
+
+    dofs = GooseFEM::Mesh::renumber(dofs);
+
+    xt::xtensor<size_t,1> iip = xt::concatenate(xt::xtuple(
+        xt::view(dofs, xt::keep(nodesBot), 0),
+        xt::view(dofs, xt::keep(nodesBot), 1),
+        xt::view(dofs, xt::keep(nodesTop), 0),
+        xt::view(dofs, xt::keep(nodesTop), 1)));
+
+    // simulation variables
+    // --------------------
+
+    // vector definition
+    GooseFEM::VectorPartitioned vector(conn, dofs, iip);
+
+    // allocate system matrix
+    GooseFEM::MatrixPartitioned<> K(conn, dofs, iip);
+
+    // nodal quantities
+    xt::xtensor<double,2> disp = xt::zeros<double>(coor.shape());
+    xt::xtensor<double,2> fint = xt::zeros<double>(coor.shape());
+    xt::xtensor<double,2> fext = xt::zeros<double>(coor.shape());
+    xt::xtensor<double,2> fres = xt::zeros<double>(coor.shape());
+
+    // element vectors
+    xt::xtensor<double,3> ue = xt::empty<double>({nelem, nne, ndim});
+    xt::xtensor<double,3> fe = xt::empty<double>({nelem, nne, ndim});
+    xt::xtensor<double,3> Ke = xt::empty<double>({nelem, nne * ndim, nne * ndim});
+
+    // element/material definition
+    // ---------------------------
+
+    // element definition
+    GooseFEM::Element::Quad4::QuadraturePlanar elem(vector.AsElement(coor));
+    size_t nip = elem.nip();
+
+    // material definition
+    GMatElastic::Cartesian3d::Matrix mat(nelem, nip);
+    xt::xtensor<size_t,2> Ihard = xt::zeros<size_t>({nelem, nip});
+    xt::view(Ihard, xt::keep(0, 1, 5, 6), xt::all()) = 1;
+    xt::xtensor<size_t,2> Isoft = xt::ones<size_t>({nelem, nip}) - Ihard;
+    mat.setElastic(Isoft, 10.0, 1.0);
+    mat.setElastic(Ihard, 10.0, 10.0);
+
+    // integration point tensors
+    xt::xtensor<double,4> Eps = xt::empty<double>({nelem, nip, 3ul, 3ul});
+    xt::xtensor<double,4> Sig = xt::empty<double>({nelem, nip, 3ul, 3ul});
+    xt::xtensor<double,6> C = xt::empty<double>({nelem, nip, 3ul, 3ul, 3ul, 3ul});
+
+    // solve
+    // -----
+
+    // strain
+    vector.asElement(disp, ue);
+    elem.symGradN_vector(ue, Eps);
+
+    // stress & tangent
+    mat.tangent(Eps, Sig, C);
+
+    // internal force
+    elem.int_gradN_dot_tensor2_dV(Sig, fe);
+    vector.assembleNode(fe, fint);
+
+    // stiffness matrix
+    elem.int_gradN_dot_tensor4_dot_gradNT_dV(C, Ke);
+    K.assemble(Ke);
+
+    // set fixed displacements
+    xt::view(disp, xt::keep(nodesTop), 0) = +0.1;
+
+    // residual
+    xt::noalias(fres) = fext - fint;
+
+    // solve
+    K.solve(fres, disp);
+
+    // post-process
+    // ------------
+
+    // compute strain and stress
+    vector.asElement(disp, ue);
+    elem.symGradN_vector(ue, Eps);
+    mat.stress(Eps, Sig);
+
+    // internal force
+    elem.int_gradN_dot_tensor2_dV(Sig, fe);
+    vector.assembleNode(fe, fint);
+
+    // apply reaction force
+    vector.copy_p(fint, fext);
+
+    // residual
+    xt::noalias(fres) = fext - fint;
+
+    // print residual
+    std::cout << xt::sum(xt::abs(fres))[0] / xt::sum(xt::abs(fext))[0] << std::endl;
+
+    // average stress per node
+    xt::xtensor<double,4> dV = elem.DV(2);
+    xt::xtensor<double,3> SigAv = xt::average(Sig, dV, {1});
+
+    // write output
+    H5Easy::File file("output.h5", H5Easy::File::Overwrite);
+    H5Easy::dump(file, "/coor", coor);
+    H5Easy::dump(file, "/conn", conn);
+    H5Easy::dump(file, "/disp", disp);
+    H5Easy::dump(file, "/Sig", SigAv);
+
+    return 0;
+}
diff --git a/docs/examples/statics/MixedPeriodic_LinearElastic/example.py b/docs/examples/statics/MixedPeriodic_LinearElastic/example.py
new file mode 100644
index 0000000..d56432a
--- /dev/null
+++ b/docs/examples/statics/MixedPeriodic_LinearElastic/example.py
@@ -0,0 +1,188 @@
+import sys
+import GooseFEM
+import GMatElastic
+import numpy as np
+
+# mesh
+# ----
+
+# define mesh
+mesh = GooseFEM.Mesh.Quad4.Regular(5, 5)
+
+# mesh dimensions
+nelem = mesh.nelem()
+nne = mesh.nne()
+ndim = mesh.ndim()
+
+# mesh definitions
+coor = mesh.coor()
+conn = mesh.conn()
+dofs = mesh.dofs()
+
+# node sets
+nodesLft = mesh.nodesLeftOpenEdge()
+nodesRgt = mesh.nodesRightOpenEdge()
+nodesTop = mesh.nodesTopEdge()
+nodesBot = mesh.nodesBottomEdge()
+
+# periodicity and fixed displacements DOFs
+# ----------------------------------------
+
+for j in range(coor.shape[1]):
+    dofs[nodesRgt, j] = dofs[nodesLft, j]
+
+dofs = GooseFEM.Mesh.renumber(dofs)
+
+iip = np.concatenate((
+    dofs[nodesBot, 0],
+    dofs[nodesBot, 1],
+    dofs[nodesTop, 0],
+    dofs[nodesTop, 1]
+))
+
+# simulation variables
+# --------------------
+
+# vector definition
+vector = GooseFEM.VectorPartitioned(conn, dofs, iip)
+
+# allocate system matrix
+K = GooseFEM.MatrixPartitioned(conn, dofs, iip)
+
+# nodal quantities
+disp = np.zeros(coor.shape)
+fint = np.zeros(coor.shape)
+fext = np.zeros(coor.shape)
+fres = np.zeros(coor.shape)
+
+# element vectors
+ue = np.empty((nelem, nne, ndim))
+fe = np.empty((nelem, nne, ndim))
+Ke = np.empty((nelem, nne * ndim, nne * ndim))
+
+# element/material definition
+# ---------------------------
+
+# element definition
+elem = GooseFEM.Element.Quad4.QuadraturePlanar(vector.AsElement(coor))
+nip = elem.nip()
+
+# material definition
+mat = GMatElastic.Cartesian3d.Matrix(nelem, nip)
+Ihard = np.zeros((nelem, nip), dtype='int')
+Ihard[[0, 1, 5, 6], :] = 1
+Isoft = np.ones((nelem, nip), dtype='int') - Ihard
+mat.setElastic(Isoft, 10.0, 1.0)
+mat.setElastic(Ihard, 10.0, 10.0)
+
+# integration point tensors
+Eps = np.empty((nelem, nip, 3, 3))
+Sig = np.empty((nelem, nip, 3, 3))
+C = np.empty((nelem, nip, 3, 3, 3, 3))
+
+# solve
+# -----
+
+# strain
+ue = vector.AsElement(disp)
+Eps = elem.SymGradN_vector(ue)
+
+# stress & tangent
+(Sig, C) = mat.Tangent(Eps)
+
+# internal force
+fe = elem.Int_gradN_dot_tensor2_dV(Sig)
+fint = vector.AssembleNode(fe)
+
+# stiffness matrix
+Ke = elem.Int_gradN_dot_tensor4_dot_gradNT_dV(C)
+K.assemble(Ke)
+
+# set fixed displacements
+disp[nodesTop, 0] = +0.1
+
+# residual
+fres = fext - fint
+
+# solve
+disp = K.Solve(fres, disp)
+
+# post-process
+# ------------
+
+# compute strain and stress
+ue = vector.AsElement(disp)
+Eps = elem.SymGradN_vector(ue)
+Sig = mat.Stress(Eps)
+
+# internal force
+fe = elem.Int_gradN_dot_tensor2_dV(Sig)
+fint = vector.AssembleNode(fe)
+
+# apply reaction force
+fext = vector.Copy_p(fint, fext)
+
+# residual
+fres = fext - fint
+
+# print residual
+print(np.sum(np.abs(fres)) / np.sum(np.abs(fext)))
+
+# average stress per node
+dV = elem.DV(2)
+Sig = np.average(Sig, weights=dV, axis=1)
+
+# skip plot with "--no-plot" command line argument
+# ------------------------------------------------
+
+if len(sys.argv) == 2:
+    if sys.argv[1] == "--no-plot":
+        sys.exit(0)
+
+# plot
+# ----
+
+import matplotlib.pyplot as plt
+import GooseMPL as gplt
+
+plt.style.use(['goose', 'goose-latex'])
+
+# extract dimension
+
+nelem = conn.shape[0]
+
+# tensor products
+
+def ddot22(A2, B2):
+    return np.einsum('eij, eji -> e', A2, B2)
+
+
+def ddot42(A4, B2):
+    return np.einsum('eijkl, elk -> eij', A4, B2)
+
+
+def dyad22(A2, B2):
+    return np.einsum('eij, ekl -> eijkl', A2, B2)
+
+# identity tensors
+
+i = np.eye(3)
+I = np.einsum('ij, e', i, np.ones([nelem]))
+I4 = np.einsum('ijkl, e -> eijkl', np.einsum('il, jk', i, i), np.ones([nelem]))
+I4rt = np.einsum('ijkl, e -> eijkl', np.einsum('ik,jl', i, i), np.ones([nelem]))
+I4s = 0.5 * (I4 + I4rt)
+II = dyad22(I, I)
+I4d = I4s - II / 3.0
+
+# compute equivalent stress
+
+Sigd = ddot42(I4d, Sig)
+sigeq = np.sqrt(3.0 / 2.0 * ddot22(Sigd, Sigd))
+
+# plot
+
+fig, ax = plt.subplots()
+gplt.patch(coor=coor + disp, conn=conn, cindex=sigeq, cmap='jet', axis=ax, clim=(0, 0.1))
+gplt.patch(coor=coor, conn=conn, linestyle='--', axis=ax)
+plt.savefig('plot.pdf')
+plt.close()
diff --git a/docs/examples/statics/MixedPeriodic_LinearElastic/main.cpp b/docs/examples/statics/MixedPeriodic_LinearElastic/main.cpp
deleted file mode 100644
index 8f53f81..0000000
--- a/docs/examples/statics/MixedPeriodic_LinearElastic/main.cpp
+++ /dev/null
@@ -1,159 +0,0 @@
-#include <Eigen/Eigen>
-#include <GooseFEM/GooseFEM.h>
-#include <GMatElastic/Cartesian3d.h>
-#include <xtensor-io/xhighfive.hpp>
-
-int main()
-{
-  // mesh
-  // ----
-
-  // define mesh
-
-  GooseFEM::Mesh::Quad4::Regular mesh(5,5);
-
-  // mesh dimensions
-
-  size_t nelem = mesh.nelem();
-  size_t nne   = mesh.nne();
-  size_t ndim  = mesh.ndim();
-
-  // mesh definitions
-
-  xt::xtensor<double,2> coor = mesh.coor();
-  xt::xtensor<size_t,2> conn = mesh.conn();
-  xt::xtensor<size_t,2> dofs = mesh.dofs();
-
-  // node sets
-
-  xt::xtensor<size_t,1> nodesLeft   = mesh.nodesLeftOpenEdge();
-  xt::xtensor<size_t,1> nodesRight  = mesh.nodesRightOpenEdge();
-  xt::xtensor<size_t,1> nodesTop    = mesh.nodesTopEdge();
-  xt::xtensor<size_t,1> nodesBottom = mesh.nodesBottomEdge();
-
-  // periodicity and fixed displacements DOFs
-  // ----------------------------------------
-
-  for ( size_t i = 0 ; i < nodesLeft.size() ; ++i )
-    for ( size_t j = 0 ; j < coor.shape()[1] ; ++j )
-      dofs(nodesRight(i),j) = dofs(nodesLeft(i),j);
-
-  dofs = GooseFEM::Mesh::renumber(dofs);
-
-  xt::xtensor<size_t,1> iip = xt::concatenate(xt::xtuple(
-    xt::view(dofs, xt::keep(nodesBottom), 0),
-    xt::view(dofs, xt::keep(nodesBottom), 1),
-    xt::view(dofs, xt::keep(nodesTop   ), 0),
-    xt::view(dofs, xt::keep(nodesTop   ), 1)
-  ));
-
-  // simulation variables
-  // --------------------
-
-  // vector definition
-
-  GooseFEM::VectorPartitioned vector(conn, dofs, iip);
-
-  // nodal quantities
-
-  xt::xtensor<double,2> disp = xt::zeros<double>(coor.shape());
-  xt::xtensor<double,2> fint = xt::zeros<double>(coor.shape());
-  xt::xtensor<double,2> fext = xt::zeros<double>(coor.shape());
-  xt::xtensor<double,2> fres = xt::zeros<double>(coor.shape());
-
-  // element vectors
-
-  xt::xtensor<double,3> ue = xt::empty<double>({nelem, nne, ndim});
-  xt::xtensor<double,3> fe = xt::empty<double>({nelem, nne, ndim});
-  xt::xtensor<double,3> Ke = xt::empty<double>({nelem, nne*ndim, nne*ndim});
-
-  // element/material definition
-  // ---------------------------
-
-  GooseFEM::Element::Quad4::QuadraturePlanar elem(vector.AsElement(coor));
-
-  size_t nip = elem.nip();
-
-  GMatElastic::Cartesian3d::Matrix mat(nelem, nip);
-
-  xt::xtensor<size_t,2> Ihard = xt::zeros<size_t>({nelem, nip});
-
-  xt::view(Ihard, xt::keep(0,1,5,6), xt::all()) = 1;
-
-  xt::xtensor<size_t,2> Isoft = xt::ones<size_t>({nelem, nip}) - Ihard;
-
-  mat.setElastic(Isoft,10.,1. );
-  mat.setElastic(Ihard,10.,10.);
-
-  // solve
-  // -----
-
-  // allocate tensors
-  size_t d = 3;
-  xt::xtensor<double,4> Eps = xt::empty<double>({nelem, nip, d, d      });
-  xt::xtensor<double,4> Sig = xt::empty<double>({nelem, nip, d, d      });
-  xt::xtensor<double,6> C   = xt::empty<double>({nelem, nip, d, d, d, d});
-
-  // allocate system matrix
-  GooseFEM::MatrixPartitioned<> K(conn, dofs, iip);
-
-  // strain
-  vector.asElement(disp, ue);
-  elem.symGradN_vector(ue, Eps);
-
-  // stress & tangent
-  mat.tangent(Eps, Sig, C);
-
-  // internal force
-  elem.int_gradN_dot_tensor2_dV(Sig, fe);
-  vector.assembleNode(fe, fint);
-
-  // stiffness matrix
-  elem.int_gradN_dot_tensor4_dot_gradNT_dV(C, Ke);
-  K.assemble(Ke);
-
-  // set fixed displacements
-  xt::view(disp, xt::keep(nodesTop), xt::keep(0)) = +0.1;
-
-  // residual
-  xt::noalias(fres) = fext - fint;
-
-  // solve
-  K.solve(fres, disp);
-
-  // post-process
-  // ------------
-
-  // compute strain and stress
-  vector.asElement(disp, ue);
-  elem.symGradN_vector(ue, Eps);
-  mat.stress(Eps, Sig);
-
-  // internal force
-  elem.int_gradN_dot_tensor2_dV(Sig, fe);
-  vector.assembleNode(fe, fint);
-
-  // apply reaction force
-  vector.copy_p(fint, fext);
-
-  // residual
-  xt::noalias(fres) = fext - fint;
-
-  // print residual
-  std::cout << xt::sum(xt::abs(fres))[0] / xt::sum(xt::abs(fext))[0] << std::endl;
-
-  // average stress per node
-  xt::xtensor<double,4> dV = elem.DV(2);
-  xt::xtensor<double,3> SigAv = xt::average(Sig, dV, {1});
-
-  // write output
-
-  HighFive::File file("main.h5", HighFive::File::Overwrite);
-
-  xt::dump(file, "/coor", coor);
-  xt::dump(file, "/conn", conn);
-  xt::dump(file, "/disp", disp);
-  xt::dump(file, "/Sig" , SigAv);
-
-  return 0;
-}
diff --git a/docs/examples/statics/MixedPeriodic_LinearElastic/plot.py b/docs/examples/statics/MixedPeriodic_LinearElastic/plot.py
index 0bf1209..8447c77 100644
--- a/docs/examples/statics/MixedPeriodic_LinearElastic/plot.py
+++ b/docs/examples/statics/MixedPeriodic_LinearElastic/plot.py
@@ -1,51 +1,51 @@
-
 import h5py
-
 import matplotlib.pyplot as plt
-import GooseMPL          as gplt
-import numpy             as np
+import GooseMPL as gplt
+import numpy as np
 
 plt.style.use(['goose', 'goose-latex'])
 
-# open file
-file = h5py.File('main.h5', 'r')
+# load data
 
-# read fields
-coor = file['/coor'][...]
-conn = file['/conn'][...]
-disp = file['/disp'][...]
-Sig  = file['/Sig' ][...]
-
-# extract dimension
-nelem = conn.shape[0]
+with h5py.File('output.h5', 'r') as data:
+    coor = data['/coor'][...]
+    conn = data['/conn'][...]
+    disp = data['/disp'][...]
+    Sig = data['/Sig'][...]
+    nelem = conn.shape[0]
 
 # tensor products
-ddot22 = lambda A2,B2: np.einsum('eij  ,eji->e    ',A2,B2)
-ddot42 = lambda A4,B2: np.einsum('eijkl,elk->eij  ',A4,B2)
-dyad22 = lambda A2,B2: np.einsum('eij  ,ekl->eijkl',A2,B2)
 
-# identity tensor (single tensor)
-i    = np.eye(3)
+def ddot22(A2, B2):
+    return np.einsum('eij, eji -> e', A2, B2)
 
-# identity tensors (grid)
-I    = np.einsum('ij  ,e'       ,                  i   ,np.ones([nelem]))
-I4   = np.einsum('ijkl,e->eijkl',np.einsum('il,jk',i,i),np.ones([nelem]))
-I4rt = np.einsum('ijkl,e->eijkl',np.einsum('ik,jl',i,i),np.ones([nelem]))
-I4s  = (I4+I4rt)/2.
-II   = dyad22(I,I)
-I4d  = I4s-II/3.
 
-# compute equivalent stress
-Sigd  = ddot42(I4d, Sig)
-sigeq = np.sqrt(3./2.*ddot22(Sigd,Sigd))
+def ddot42(A4, B2):
+    return np.einsum('eijkl, elk -> eij', A4, B2)
 
-# plot
 
-fig, ax = plt.subplots()
+def dyad22(A2, B2):
+    return np.einsum('eij, ekl -> eijkl', A2, B2)
 
-gplt.patch(coor=coor+disp, conn=conn, cindex=sigeq, cmap='jet', axis=ax, clim=(0,0.1))
+# identity tensors
 
-gplt.patch(coor=coor, conn=conn, linestyle='--', axis=ax)
+i = np.eye(3)
+I = np.einsum('ij, e', i, np.ones([nelem]))
+I4 = np.einsum('ijkl, e -> eijkl', np.einsum('il, jk', i, i), np.ones([nelem]))
+I4rt = np.einsum('ijkl, e -> eijkl', np.einsum('ik,jl', i, i), np.ones([nelem]))
+I4s = 0.5 * (I4 + I4rt)
+II = dyad22(I, I)
+I4d = I4s - II / 3.0
 
-plt.show()
+# compute equivalent stress
+
+Sigd = ddot42(I4d, Sig)
+sigeq = np.sqrt(3.0 / 2.0 * ddot22(Sigd, Sigd))
+
+# plot
 
+fig, ax = plt.subplots()
+gplt.patch(coor=coor + disp, conn=conn, cindex=sigeq, cmap='jet', axis=ax, clim=(0, 0.1))
+gplt.patch(coor=coor, conn=conn, linestyle='--', axis=ax)
+plt.savefig('plot.pdf')
+plt.close()