```# -*- coding: utf-8 -*-
from __future__ import print_function, division

import pdb

import numpy
import scipy.sparse

from utils import xy_to_id, id_to_xy

class VonMisesStressCalculator:
def __init__(self, nelx, nely, Emin, Emax, penal):
self.nelx, self.nely, self.Emin, self.Emax, self.penal = (
nelx, nely, Emin, Emax, penal)
self.edofMat = self.build_indices()

@staticmethod
def B(side):
""" Precomputed strain-displacement matrix. """
n = -0.5 / side
p = 0.5 / side
return numpy.array([[p, 0, n, 0, n, 0, p, 0],
[0, p, 0, p, 0, n, 0, n],
[p, p, p, n, n, n, n, p]])

@staticmethod
def E(nu):
""" Precomputed constitutive matrix. """
return numpy.array([[1, nu, 0],
[nu, 1, 0],
[0, 0, (1 - nu) / 2.]]) / (1 - nu**2)

def build_indices(self):
edofMat = numpy.zeros((8, self.nelx * self.nely), dtype=int)
for elx in range(self.nelx):
for ely in range(self.nely):
el = ely + elx * self.nely # Element index
n1 = (self.nely + 1) * elx + ely # Left nodes
n2 = (self.nely + 1) * (elx + 1) + ely # Right nodes
edofMat[:, el] = numpy.array([2 * n1 + 2, 2 * n1 + 3,
2 * n2 + 2, 2 * n2 + 3, 2 * n2, 2 * n2 + 1, 2 * n1,
2 * n1 + 1])
return edofMat

def penalized_densities(self, x):
""" Compute the penalized densties. """
return self.Emin + (self.Emax - self.Emin) * x**self.penal

def diff_penalized_densities(self, x):
""" Compute the penalized densties. """
return (self.Emax - self.Emin) * self.penal * x**(self.penal - 1)

def calculate_principle_stresses(self, x, u, nu, side=1):
"""
Calculate the principle stresses in the x, y, and shear directions.
"""
rho = self.penalized_densities(x)
EB = self.E(nu).dot(self.B(side))
stress = sum([EB.dot(u[:, i][self.edofMat]) for i in range(u.shape[1])])
stress *= rho / float(u.shape[1])
return numpy.hsplit(stress.T, 3)

def calculate_stress(self, x, u, nu, side=1):
"""
Calculate the Von Mises stress given the densities x, displacements u,
and young modulus nu.
"""
s11, s22, s12 =  self.calculate_principle_stresses(x, u, nu, side)
vm_stress = numpy.sqrt(s11**2 - s11 * s22 + s22**2 + 3 * s12**2)
return vm_stress

def calculate_diff_stress(self, x, u, nu, side=1):
"""
Calculate the derivative of the Von Mises stress given the densities x,
displacements u, and young modulus nu. Optionally, provide the side
length (default: 1).
"""
rho = self.penalized_densities(x)
EB = self.E(nu).dot(self.B(side))
EBu = sum([EB.dot(u[:, i][self.edofMat]) for i in range(u.shape[1])])
s11, s22, s12 = numpy.hsplit((EBu * rho / float(u.shape[1])).T, 3)
drho = self.diff_penalized_densities(x)
ds11, ds22, ds12 = numpy.hsplit(
((1 - rho) * drho * EBu / float(u.shape[1])).T, 3)
vm_stress = numpy.sqrt(s11**2 - s11 * s22 + s22**2 + 3 * s12**2)
if abs(vm_stress).sum() > 1e-8:
dvm_stress = (0.5 * (1. / vm_stress) * (2 * s11 * ds11 -
ds11 * s22 - s11 * ds22 + 2 * s22 * ds22 + 6 * s12 * ds12))
return dvm_stress
return 0

def calculate_fdiff_stress(self, x, u, nu, side=1, dx=1e-6):
"""
Calculate the derivative of the Von Mises stress using finite
differences given the densities x, displacements u, and young modulus
nu. Optionally, provide the side length (default: 1) and delta x
(default: 1e-6).
"""
ds = self.calculate_diff_stress(x, u, nu, side)
dsf = numpy.zeros(x.shape)
x = numpy.expand_dims(x, -1)
for i in range(x.shape[0]):
delta = scipy.sparse.coo_matrix(([dx], [[i], [0]]), shape=x.shape)
s1 = self.calculate_stress((x + delta.A).squeeze(), u, nu, side)
s2 = self.calculate_stress((x - delta.A).squeeze(), u, nu, side)
dsf[i] = ((s1 - s2) / (2. * dx))[i]
print("finite differences: {:g}".format(numpy.linalg.norm(dsf - ds)))
return dsf
```