Hi,
I want to compute the electrostatic force through virtual work method. This involves calculating the sensitivity of energy to a virtual displacement of the object the force is calculated on.
I am not knowledgeable in these things but it seems that shape derivatives could help. After reading a lot of forum posts on the subject, I am still unable to piece things together to solve the entire problem. Specifically, I do not feel sure how to define the displacement field for this case which defines the derivative and then of course its implementation.
Below I am sharing an MWE involving two circles/cylinders in electrostatic field. I need the force on only one of the two circles so only that should be subject to virtual displacement. I will sincerely appreciate if someone could complete the code below or refer me to examples that realize something similar.
import dolfinx, ufl, mpi4py, petsc4py, gmsh, typing
from dolfinx.io import gmshio
from dolfinx.fem.petsc import LinearProblem
import numpy as np
y0 = 0 # sensitivity parameter
#%% geometry
gdim = 2
model_rank = 0 # everything is on one core
gmsh.initialize()
gmsh.option.setNumber("General.Terminal", 0) # disable output messages
gmsh.clear()
occ = gmsh.model.occ
c1 = occ.addDisk(0, 1+y0, 0, 0.5, 0.5)
c2 = occ.addDisk(0, -1, 0, 0.5, 0.5)
r1 = occ.addRectangle(-3, -3, 0, 6, 6)
frag, _ = occ.fragment([(gdim, c1)], [(gdim, r1), (gdim, c2)])
occ.synchronize()
# number all domains
all_doms = gmsh.model.getEntities(gdim)
for j, dom in enumerate(all_doms):
gmsh.model.addPhysicalGroup(dom[0], [dom[1]], j + 1) # create the main group/node
# number all boundaries
all_edges = gmsh.model.getEntities(gdim - 1)
for j, edge in enumerate(all_edges):
gmsh.model.addPhysicalGroup(edge[0], [edge[1]], edge[1]) # create the main group/node
gmsh.model.mesh.generate(gdim)
# gmsh.fltk.run()
mesh, ct, ft = gmshio.model_to_mesh(gmsh.model, mpi4py.MPI.COMM_WORLD, model_rank, gdim)
gmsh.finalize()
dom_indices = (1, 2, 3) # top circle, bottom circle, surrounding domain
# assign permittivity to circles
Omega_dg = dolfinx.fem.functionspace(mesh, ("DG", 0))
epsr = dolfinx.fem.Function(Omega_dg)
epsr.x.array[:] = 1
for tag in dom_indices[:2]:
cells = ct.find(tag)
epsr.x.array[cells] = 9
Omega = dolfinx.fem.functionspace(mesh, ("CG", 2))
u = ufl.TrialFunction(Omega)
v = ufl.TestFunction(Omega)
e = epsr*ufl.dot(ufl.grad(u), ufl.grad(v))*ufl.dx - ufl.dot(dolfinx.fem.Constant(mesh, 0.0), v)*ufl.dx
a = ufl.lhs(e)
L = ufl.rhs(e)
# boundary conditions
on_left = lambda x: np.isclose(x[0], -3)
dofs_l = dolfinx.fem.locate_dofs_geometrical(Omega, on_left)
dbc_l = dolfinx.fem.dirichletbc(1.0, dofs_l, Omega)
on_right = lambda x: np.isclose(x[0], 3)
dofs_r = dolfinx.fem.locate_dofs_geometrical(Omega, on_right)
dbc_r = dolfinx.fem.dirichletbc(0., dofs_r, Omega)
petsc_options = {"ksp_type": "preonly", "pc_type": "lu", "pc_factor_mat_solver_type": "mumps"}
problem = LinearProblem(a, L, bcs=[dbc_l, dbc_r], petsc_options=petsc_options)
uh = problem.solve()
e_sol = -ufl.grad(uh) # electric field
J = 1/2*epsr*ufl.dot(e_sol, e_sol)*ufl.dx # energy density whose sensitivity is to be calculated
# How to calculate dJ/dy0?