Skip to content

Instantly share code, notes, and snippets.

@klunkean
Created September 23, 2021 10:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save klunkean/546de7670dab823ca495f1b98b2ae0df to your computer and use it in GitHub Desktop.
Save klunkean/546de7670dab823ca495f1b98b2ae0df to your computer and use it in GitHub Desktop.
from dolfin import *
import matplotlib.pyplot as plt
import numpy as np
import mshr
import math
# Algorithmic parameters
niternp = 20 # number of non-penalized iterations
niter = 100 # total number of iterations
pmax = 4 # maximum SIMP exponent
exponent_update_frequency = 4 # minimum number of steps between exponent update
tol_mass = 1e-4 # tolerance on mass when finding Lagrange multiplier
thetamin = 0.001 # minimum density modeling void
# Problem parameters
thetamoy = 0.4 # target average material density
E = Constant(1)
nu = Constant(0.3)
lamda = E*nu/(1+nu)/(1-2*nu)
mu = E/(2*(1+nu))
ctype = CellType.Type.quadrilateral
ne_x = 128
ne_y = int(ne_x//2)
mesh = RectangleMesh.create(
MPI.comm_world,
[Point(0, 0), Point(2., 1.)],
[ne_x, ne_y],
ctype)
class Last(SubDomain):
def inside(self, x, on_boundary):
return near(x[1],1.) and on_boundary
class Fix(SubDomain):
def inside(self, x, on_boundary):
return near(x[1], 0.) and (x[0]<.25 or x[0]>1.75) and on_boundary
subdomains = MeshFunction("size_t", mesh, 1)
last = Last()
last.mark(subdomains,2)
fix = Fix()
fix.mark(subdomains,1)
File("quadsubd.pvd") << subdomains
ds = Measure("ds", subdomain_data=subdomains)
fe = FiniteElement("CG", mesh.ufl_cell(), 1)
vfe = VectorElement(fe)
# Function space for density field
V0 = FunctionSpace(mesh, "DG", 0)
# Function space for displacement
V2 = VectorFunctionSpace(mesh, "CG", 2)
# Fixed boundary condtions
# bc1 = DirichletBC(V2, Constant((0, 0)), subdomains, 1)
bc1 = DirichletBC(V2, Constant((0, 0)), subdomains, 1)
# bc2 = DirichletBC(V2, Constant((0, -0.1)), subdomains, 1)
bc = [bc1]
p = Constant(1) # SIMP penalty exponent
exponent_counter = 0 # exponent update counter
lagrange = Constant(1) # Lagrange multiplier for volume constraint
thetaold = Function(V0, name="Density")
thetaold.interpolate(Constant(thetamoy))
coeff = thetaold**p
theta = Function(V0)
volume = assemble(Constant(1.)*dx(domain=mesh))
avg_density_0 = assemble(thetaold*dx)/volume # initial average density
avg_density = 0.
def eps(v):
return sym(grad(v))
def sigma(v):
return coeff*(lamda*div(v)*Identity(2)+2*mu*eps(v))
def energy_density(u, v):
return inner(sigma(u), eps(v))
# Force
N = FacetNormal(mesh)
f = Expression(["0.0","-pow(x[0],2)"], element=vfe)
# Inhomogeneous elastic variational problem
u_ = TestFunction(V2)
du = TrialFunction(V2)
a = inner(sigma(u_), eps(du))*dx
L = dot(f, u_)*ds(2)
def local_project(v, V):
dv = TrialFunction(V)
v_ = TestFunction(V)
a_proj = inner(dv, v_)*dx
b_proj = inner(v, v_)*dx
solver = LocalSolver(a_proj, b_proj)
solver.factorize()
u = Function(V)
solver.solve_local_rhs(u)
return u
def update_theta():
theta.assign(local_project((p*coeff*energy_density(u, u)/lagrange)**(1/(p+1)), V0))
thetav = theta.vector().get_local()
theta.vector().set_local(np.maximum(np.minimum(1, thetav), thetamin))
theta.vector().apply("insert")
avg_density = assemble(theta*dx)/volume
return avg_density
def update_lagrange_multiplier(avg_density):
avg_density1 = avg_density
# Initial bracketing of Lagrange multiplier
if (avg_density1 < avg_density_0):
lagmin = float(lagrange)
while (avg_density < avg_density_0):
lagrange.assign(Constant(lagrange/2))
avg_density = update_theta()
lagmax = float(lagrange)
elif (avg_density1 > avg_density_0):
lagmax = float(lagrange)
while (avg_density > avg_density_0):
lagrange.assign(Constant(lagrange*2))
avg_density = update_theta()
lagmin = float(lagrange)
else:
lagmin = float(lagrange)
lagmax = float(lagrange)
# Dichotomy on Lagrange multiplier
inddico=0
while ((abs(1.-avg_density/avg_density_0)) > tol_mass):
lagrange.assign(Constant((lagmax+lagmin)/2))
avg_density = update_theta()
inddico += 1;
if (avg_density < avg_density_0):
lagmin = float(lagrange)
else:
lagmax = float(lagrange)
print(" Dichotomy iterations:", inddico)
def update_exponent(exponent_counter):
exponent_counter += 1
if (i < niternp):
p.assign(Constant(1))
elif (i >= niternp):
if i == niternp:
print("\n Starting penalized iterations\n")
if ((abs(compliance-old_compliance) < 0.01*compliance_history[0]) and
(exponent_counter > exponent_update_frequency) ):
# average gray level
gray_level = assemble((theta-thetamin)*(1.-theta)*dx)*4/volume
p.assign(Constant(min(float(p)*(1+0.3**(1.+gray_level/2)), pmax)))
exponent_counter = 0
print(" Updated SIMP exponent p = ", float(p))
return exponent_counter
u = Function(V2, name="Displacement")
old_compliance = 1e30
ffile = XDMFFile("quad.xdmf")
ffile.parameters["flush_output"]=True
ffile.parameters["functions_share_mesh"]=True
compliance_history = []
for i in range(niter):
solve(a == L, u, bc, solver_parameters={"linear_solver": "cg", "preconditioner": "hypre_amg"})
ffile.write(thetaold, i)
ffile.write(u, i)
compliance = assemble(action(L, u))
compliance_history.append(compliance)
print("Iteration {}: compliance =".format(i), compliance)
avg_density = update_theta()
update_lagrange_multiplier(avg_density)
exponent_counter = update_exponent(exponent_counter)
# Update theta field and compliance
thetaold.assign(theta)
old_compliance = compliance
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment