Thank you for the link!
It might be helpful to somebody (or even my future self) to have a minimal working example (mwe) on how to do the hole thing, so here is what I came up with. I do not claim that this is the best way to do it and am very open to constructive criticism.
The following mwe consists of three bodies, a box and a cube connected by a cylinder. The “bottom” side of the box is clamped and a constant traction acts on the “top” surface of the cube.
First create a dummy mesh with gmsh (I used version 4.6.0):
"" Generate a dummy mesh with three bodies: a box and a cube box connected by a cylinder. """ import subprocess import gmsh def main(): gmsh.initialize() gmsh.option.setNumber('Mesh.MshFileVersion', 2.2) name2tag = dict() tag = gmsh.model.occ.addBox(-1, -1, 0, 2, 2, -0.2) box = (3, tag) name2tag['box'] = box tag = gmsh.model.occ.addBox(-0.5, -0.5, -2, 1, 1, -1) cube = (3, tag) name2tag['cube'] = cube tag = gmsh.model.occ.addCylinder(0, 0, -0.1, 0, 0, -2, 0.25) cyl = (3, tag) cutcyl = gmsh.model.occ.cut([cyl], [cube, box], removeTool=False)[0][0] gmsh.model.occ.fragment([cutcyl], [cube, box]) # makes mesh conformal name2tag['cylinder'] = cyl gmsh.model.occ.synchronize() name2tag['clamped'] = (2, [27]) name2tag['forced'] = (2, [20]) # add physical groups grouptags = dict() for name, (dim, tags) in name2tag.items(): if isinstance(tags, int): tags = [tags] grouptag = gmsh.model.addPhysicalGroup(dim, tags) gmsh.model.setPhysicalName(dim, grouptag, name) grouptags[name] = (dim, grouptag) print(name, grouptags[name]) gmsh.model.occ.synchronize() # generate mesh (output only for PhysicalGroup elements) elementsize = 0.10 gmsh.model.mesh.setSize(gmsh.model.getEntities(dim=0), elementsize) gmsh.model.mesh.generate() meshfilebasename = 'mesh_mwe' gmsh.write('{}.msh'.format(meshfilebasename)) gmsh.fltk.run() gmsh.finalize() # convert msh to xml command = 'dolfin-convert {f}.msh {f}.xml'.format(f=meshfilebasename) subprocess.check_call(command.split()) if __name__ == '__main__': main()
Then read the xml and setup and run the FEniCS calculation:
""" Linear elastic problem (no body force -> r.h.s. is zero): -div(sigma(u)) = 0 Restrict von Mises output to selected bodies (zero otherwise). """ from __future__ import print_function from fenics import * from ufl import nabla_div import matplotlib.pyplot as plt def main(): elementorder = 2 # Create mesh and define function space mesh = Mesh('mesh_mwe.xml') markers = MeshFunction('size_t', mesh, 'mesh_mwe_physical_region.xml') boundaries = MeshFunction('size_t', mesh, 'mesh_mwe_facet_region.xml') dx = Measure('dx', domain=mesh, subdomain_data=markers) ds = Measure('ds', domain=mesh, subdomain_data=boundaries) V = VectorFunctionSpace(mesh, 'P', elementorder) # Define boundary condition bc = [DirichletBC(V, Constant((0, 0, 0)), boundaries, 4)] # clamp surface 4 # Define strain and stress def epsilon(u): return 0.5 * (nabla_grad(u) + nabla_grad(u).T) E = [0, 194000, 194000, 194000] # 0 -> dummy entry this remains unused nu = [0, 0.3, 0.3, 0.3] # 0 -> dummy entry this remains unused def Sigma(E, nu): lmbda = E * nu / ((1 + nu) * (1 - 2 * nu)) mu = E / (2 * (1 + nu)) def sigma(u): return lmbda * nabla_div(u) * Identity(d) + 2 * mu * epsilon(u) return sigma # Define variational problem u = TrialFunction(V) d = u.geometric_dimension() # space dimension v = TestFunction(V) a = sum(inner(Sigma(E[k], nu[k])(u), epsilon(v))*dx(k) for k in [1, 2, 3]) L = dot(Constant((0, 0, -70)), v)*ds(5) # constant traction on surface 5 # Compute solution u = Function(V) solve(a == L, u, bc) print('plot and write von Mises stresses for selected bodies only:') tags = [1, 2] # tags of the bodies I am interested in W = FunctionSpace(mesh, 'P', elementorder - 1) # derivatives in von_Mises reduce elementorder by one v = TestFunction(W) L = 0 # dummy 0, just to initialize L for tag in tags: sigma = Sigma(E[tag], nu[tag]) s = sigma(u) - (1./3)*tr(sigma(u))*Identity(d) # u most NOT be a TrialFunction already von_Mises = sqrt(3./2*inner(s, s)) L += inner(von_Mises, v)*dx(tag) # v must be a TestFunction here u = TrialFunction(W) a = inner(u, v)*dx stress = Function(W) solve(a==L, stress) plot(stress) plt.show() File('elasticity/von_mises_tag{}.pvd'.format(''.join(map(str, tags)))) << stress if __name__ == '__main__': main()
Your example, dokker, worked perfectly well as long as the bodies didn’t have different material properties (and sigma didn’t depend on the body). It took me a while to realize I have to split this line in two to make it work in that case:
u, v = TrialFunction(VDG), TestFunction(VDG)
So thanks again for your help! I will look into MeshViews, too, though. Using submeshes seems like a good idea and maybe I can get rid of the zeros and get stresses only in the bodies I am interested in.
By the way, the element order of the FunctionSpace for von Mises stresses are reduced by one compared to that of the displacments, because of the derivative in sigma and epsilon. Did I get that right?