Cantilever with two "domains"

Dear,

I am trying to carry out a project of a cantilever in the multicomponent or multibody style. There would be two domains or two meshes connected to the center by circular elements, such as screws or welds. For the time being I am not considering the contact between the meshes or domains, only that the regions of the holes transmit the loads. See the image below.

I have a code below, but I think it is not as I would like. I believe that the main errors would be to consider how to actually insert two meshes or two overlapping domains and how to make the boundary conditions of the holes between one domain and another.

Can anybody help me? @bleyerj

from fenics import *
from mshr import *
import matplotlib.pyplot as plt

L1 = 4.0
H = 1.0
tol = 1E-14

# Load
F = 200
ds_size = 0.1

class Left(SubDomain):
    def inside(self, x, on_boundary):
        return (x[0] <= 2.5 + tol) #and on_boundary

class Right(SubDomain):
    def inside(self, x, on_boundary):
        return (x[0] >= 1.5 - tol) #and on_boundary

#Define geometry for background
x1 = 2.0
y1 = 0.25
x2 = 2.0
y2 = 0.75
r = 0.1

domain = Rectangle(Point(0, 0), Point(L1, H)) \
    - Circle(Point(x1, y1), r) - Circle(Point(x2, y2), r)
         
mesh = Mesh(generate_mesh(domain, 200))

# Initialize mesh function for interior domains
domains = MeshFunction("size_t", mesh, mesh.topology().dim())
domains.set_all(0)
Left().mark(domains, 1)
Right().mark(domains, 2)
# Define new measures associated with the interior domains
dx = Measure("dx", domain=mesh, subdomain_data=domains)

# Boundary Condition
class Left1(SubDomain):
    def inside(self, x, on_boundary):
        return near(x[0], 0.0) and on_boundary

class Circle1(SubDomain):
    def inside(self, x, on_boundary):
        return on_boundary and near((x[0]-x1)**2+(x[1]-y1)**2, r**2, 1e-3)
    
class Circle2(SubDomain):
    def inside(self, x, on_boundary):
        return on_boundary and near((x[0]-x2)**2+(x[1]-y2)**2, r**2, 1e-3)

# Load
class LoadSurface(SubDomain):
    def inside(self, x, on_boundary):
        return near(x[0], L1) and between(x[1], ((H-ds_size)/2.0, (H+ds_size)/2.0))

loadSurface = LoadSurface()
boundaries = MeshFunction("size_t", mesh, mesh.topology().dim()-1)
boundaries.set_all(0)
loadSurface.mark(boundaries, 1)
ds = ds(subdomain_data=boundaries)
t = Expression(('q_x', 'q_y'), degree = 2, q_x = 0.0, q_y = -F/ds_size)

## Constitutive relation

def eps(v):
      return sym(grad(v))

E = Constant(1e5)
nu = Constant(0.3)
model = "plane_stress"
mu = E/2/(1+nu)
lmbda = E*nu/(1+nu)/(1-2*nu)
if model == "plane_stress":
    lmbda = 2*mu*lmbda/(lmbda+2*mu)

def sigma(v):
       return lmbda*tr(eps(v))*Identity(2) + 2.0*mu*eps(v)

## Variational formulation

## Resolution

V = VectorFunctionSpace(mesh, 'Lagrange', degree=2)
du = TrialFunction(V)
u_ = TestFunction(V)

left1 = DirichletBC(V, Constant((0.0, 0.0)), Left1())

uc = Function(V)
bc1 = DirichletBC(V.sub(0), Constant(0.0), Circle1())
bc1.apply(uc.vector())
bc2 = DirichletBC(V.sub(0), Constant(0.0), Circle2())
bc2.apply(uc.vector())

bcs = [left1, bc1, bc2]

a = inner(sigma(du), eps(u_))*dx(1) + inner(sigma(du), eps(u_))*dx(2)

L = dot(t, u_)*ds(1)

u = Function(V, name="Displacement")
solve(a == L, u, bcs, solver_parameters={'linear_solver': 'bicgstab',
                                        'preconditioner': 'hypre_amg'}) 

plot(u, mode="displacement")
plt.title("displacement")
plt.show()
File("u.pvd") << u


print("Maximal deflection:", - u(L1, H/2.)[1])
print("Beam theory deflection:", float((4*F/ds_size*L1**3)/(E*H**3)))

Vsig = TensorFunctionSpace(mesh, "DG", degree=0)
sig = Function(Vsig, name="Stress")
sig.assign(project(sigma(u), Vsig))
print("Stress at (0,H):", sig(0, H))

Hi,

For this I think that you need dolfinx features like multipoint constraints or the multi mesh functionality in legacy dolfin. I don’t see an easy way to do what you want without things like that…

Télécharger BlueMail pour Android

Dear, @bleyerj

I appreciate your return, but this code will be used in a topological optimization model using dolfin-adjoint, and Dolfinx, as far as I know, does not yet have integration. @dokken

But what about the code above? Could you see if it is feasible considering the proposed model?

Dolfin-x does not support dolfin-adjoint, so a topology optimization algorithm would have to be made by hand.

As far as I can tell, there is no straight-forward way to do this.

Previously you have shown me that this is supposed to be a 3D problem, and I am still curious as to why you keep trying to remove a dimension from your problem (that makes the problem alot harder to solve).

Dear, @dokken

I appreciate your return. Answering your question, I am running the topological optimization of a 3D cantilever using BoxMesh (L = 4, B = 0.05, H = 1) with the following mesh divisions, nx = 200, ny = 5, and nz = 50, respectively. This model running in 2D (L = 4, H = 1; nx = ny = 200), on my computer, takes approximately 3 minutes, with 100 iterations. The 3D model, mentioned above, with the same 2D characteristics, now, at the time of writing, has been in iteration 63 for 37 hours running on my computer. (Core-i9 10th generation, 128gb of memory, etc … )

And if the energy runs out!

That’s the reason to try 2D as much as possible! 3D processing, however ideal, is still very expensive. It would take a great deal of effort to improve processing and be on a team of researchers with a high level of knowledge of phenics. So making a multibody or multicomponent model in 2D is more interesting. If for a cantilever it is already expensive, imagine for a series of interconnected elements. See the number of theses and articles that use other numerical resources and still do so in 2D geometries.

So, when I decided to use fenics, it was for all its good features of open source software and I would not like to now, after almost a year and a half working alone, only with support from the people here at Discourse, having to change the topic or software of my thesis, or even go for commercial software. That’s why I’ve been asking for your support from @bleyerj and others.

And just commenting, my topological optimization code follows the format indicated on the dolfin-adjoint website and I want to publish it, because it has less than 90 lines, great results and it looks great when compared to other programs. But in the thesis I would like to consider multibody or multicomponent.

Dear

I’m trying to access the final mesh (mesh 1 + mesh2) using multimesh, but I can’t. The code below plots but I cannot access the two overlapping meshes.

can anybody help me?

Note that only the last mesh generated remains.

from dolfin import *
from mshr import *
import matplotlib.pyplot as plt

x1 = 2.0
y1 = 0.25
x2 = 2.0
y2 = 0.75
r = 0.1

domain1 = Rectangle(Point(0, 0), Point(2.5, 1)) \
    - Circle(Point(x1, y1), r) - Circle(Point(x2, y2), r)
         
mesh1 = Mesh(generate_mesh(domain1, 20))

domain2 = Rectangle(Point(1.5, 0), Point(4, 1)) \
    - Circle(Point(x1, y1), r) - Circle(Point(x2, y2), r)
         
mesh2 = Mesh(generate_mesh(domain2, 20))

multimesh = MultiMesh()
multimesh.add(mesh1)
multimesh.add(mesh2)
multimesh.build()

for part in range(multimesh.num_parts()):
    mesh = multimesh.part(part)
    
plot(mesh)   

This is not the purpose of MultiMesh, see for instance:

Dear Dokken,

After some changes I managed to have a satisfactory result with my topological optimization code in 3D with less than 10 minutes of processing time.

So, I decided to create a simple example of two interconnected plates in 3D and check an elastic analysis. However, it generates the following operations:

Generating mesh with CGAL 3D mesh generator
Calling FFC just-in-time (JIT) compiler, this may take some time.
Calling FFC just-in-time (JIT) compiler, this may take some time.
Solving linear variational problem.

and nothing happens.

See the sample code:

from fenics import *
from mshr import *

# turn off redundant output in parallel
#parameters["std_out_all_processes"] = False

# Optimization options for the form compiler
parameters["form_compiler"]["quadrature_degree"] = 2

L = 3.0
B = 0.01
H = 1.0
tol = 1E-14

# Load
F = 500
ds_size = 0.1

# Define 3D geometry
box1 = Box(dolfin.Point(0, 0, 0), dolfin.Point(L, B , H))
box2 = Box(dolfin.Point(-2, -0.02, 0), dolfin.Point(1, -0.01 , 1))

cylinder1 = Cylinder(dolfin.Point(0.5, -0.03, 0.25), dolfin.Point(0.5, 0.02, 0.25), .05, .05)
cylinder2 = Cylinder(dolfin.Point(0.5, -0.03, 0.75), dolfin.Point(0.5, 0.02, 0.75), .05, .05)

domain = box1 + box2 + cylinder1 + cylinder2

mesh = generate_mesh(domain, 200)

# Define function space and base functions
V = VectorFunctionSpace(mesh, 'Lagrange', degree=2)

# Boundary Condition
class Left(SubDomain):
    def inside(self, x, on_boundary):
        return on_boundary and near(x[0], -2.0)

left = Left()
bc = DirichletBC(V, Constant((0.0, 0.0, 0.0)), left)

# Load
class LoadSurface(SubDomain):
    def inside(self, x, on_boundary):
        return on_boundary and near(x[0], L) and \
               between(x[1], ((B-ds_size)/2.0, (B+ds_size)/2.0)) and \
               between(x[2], ((H-ds_size)/2.0, (H+ds_size)/2.0))
                        
loadSurface = LoadSurface()
boundaries = MeshFunction("size_t", mesh, mesh.topology().dim()-1)
boundaries.set_all(0)
loadSurface.mark(boundaries, 1)
ds = ds(subdomain_data = boundaries)
t = Expression(('q_x', 'q_y', 'q_z'), degree = 3, q_x = 0.0, q_y = 0.0, q_z = -F/ds_size)

## Constitutive relation

def eps(v):
    return sym(grad(v))

E = Constant(1e5)
nu = Constant(0.3)
model = "plane_stress"
mu = E/2/(1+nu)
lmbda = E*nu/(1+nu)/(1-2*nu)
if model == "plane_stress":
    lmbda = 2*mu*lmbda/(lmbda+2*mu)

def sigma(v):
    dim = v.geometric_dimension()
    return lmbda*tr(eps(v))*Identity(dim) + 2.0*mu*eps(v)

## Variational formulation

## Resolution

du = TrialFunction(V)
u_ = TestFunction(V)
a = inner(sigma(du), eps(u_))*dx
L = dot(t, u_)*ds(1)

u = Function(V, name="Displacement")
solve(a == L, u, bc, solver_parameters={'linear_solver': 'bicgstab',
                                            'preconditioner': 'hypre_amg'})  

#file_results = XDMFFile(MPI.comm_world, "u_3D.xdmf")
file_results = XDMFFile("u_new_3D.xdmf")
file_results.parameters["flush_output"] = True
file_results.parameters["functions_share_mesh"] = True
file_results.write(u, 0.)

How many degrees of freedom are in your system? You can find this out by printing V.dim() immediately after you instantiate the function space.

Do you expect that a bicgstab KSP with hypre’s BoomerAMG as a preconditioner is an appropriate solution method?

Are you running your problem on appropriate hardware?