Let's say I have a non-linear energy functional E[u]. How do I solve for u that minimizes E over the whole mesh? For example
$$ E[u] = 2\int_0^W{E_d(z)u(z)^2dz} + \int_0^W{c(z)\left(f(z)-b(z)\int_0^z{u(v)^2dv}\right)^2dz} $$
This problem can be solved analytically resulting in a function that is constant below a certain $$ W_c < W $$ and zero above that.
Here is a (not so simple) example in dolfin
import dolfin
from dolfin import *
import ufl
# Optimization options for the form compiler
parameters['linear_algebra_backend'] = 'PETSc'
parameters["form_compiler"]["cpp_optimize"] = True
parameters["form_compiler"]["quadrature_rule"] = 'auto'
ffc_options = {"optimize": True, \
"eliminate_zeros": False, \
"precompute_basis_const": True, \
"precompute_ip_const": True
}
class Primitive(ufl.Coefficient, dolfin.cpp.Expression):
def __init__(self,f,mesh,element,constant= 0.0, **kwargs):
dolfin.cpp.Expression.__init__(self)
self._f = f
self._mesh = mesh
self._constant = constant
subdomains = CellFunction('size_t',mesh)
self._subdomains = subdomains
dx = Measure('dx',domain=mesh)[subdomains]
self._dx=dx
self._F = Form(f*dx(1))
ufl.Coefficient.__init__(self, element, count=self.id())
def eval(self, values, x):
inside_function = lambda y: y<=x[0]
domain = AutoSubDomain(inside_function=inside_function)
self._subdomains.set_all(0)
domain.mark(self._subdomains,1)
F = self._F
values[0] = assemble(F) + self._constant
def eval_cell(self, values, x, cell):
self.eval(values,x)
def ufl_evaluate(self,x, component, derivatives):
values = np.zeros(1,dtype='d')
self.eval(values,x)
return values[0]
mesh = IntervalMesh(100,0,1e-6)
V = FunctionSpace(mesh, "CG", 1)
left = CompiledSubDomain("(std::abs(x[0]) < DOLFIN_EPS) && on_boundary")
right = CompiledSubDomain("(std::abs(x[0] - 1e-6) < DOLFIN_EPS) && on_boundary")
class LinearMismatchProfile(Expression):
def __init__(self, G, W, W_b, f_0,**kwargs):
self._W = W
self._G = G
self._W_b = W_b
self._f_0 = f_0
def eval(self, values, x):
W = self._W
W_b = self._W_b
f_0 = self._f_0
G = self._G
if x[0] <= W-W_b:
values[0] = G*x[0] + f_0
else:
values[0] = G*(W-W_b)
Ed = Constant(8.138931143006406e-09) #energy per unit length of dislocation
c = Constant(1e8) #Constant(92017082108.12033) #elastic constant for biaxial strain
b = Constant(2.161453136448972e-10) #part of Burger's vector responsible for relaxation (in-plane component)
W = 1e-6
W_b = 2.64e-7
G = 70000
f_0 = 0.0
f = LinearMismatchProfile(G=G,W=W,W_b=W_b,f_0=f_0,element=V.ufl_element())
u = Function(V) # dislocation density profile
v = TestFunction(V)
du = TrialFunction(V)
Pu = Primitive(f=u*u,mesh=mesh, constant=0.0, element=V.ufl_element())
epsilon = f - b*Pu #in-plane strain
E = 2*Ed*u*u*dx + c*(epsilon**2)*dx
F = derivative(E,u,v)
J = derivative(F,u,du)
F = Form(F, form_compiler_parameters=ffc_options)
J = Form(J, form_compiler_parameters=ffc_options)
bcl = DirichletBC(V,sqrt(G/float(b)),left)
bcr = DirichletBC(V,0.0,right)
bcs = []
# Set up the non-linear problem
class TersoffModelProblem(NonlinearProblem):
def __init__(self, F, J, bcs=None, form_compiler_parameters=None, ident_zeros=False,
exterior_facet_domains=None, interior_facet_domains=None):
NonlinearProblem.__init__(self)
self._F = F
self._J = J
self._bcs = bcs
self._form_compiler_parameters = form_compiler_parameters
def F(self, b, x):
assemble(self._F, tensor=b,
form_compiler_parameters=self._form_compiler_parameters,
keep_diagonal=False)
#Apply boundary conditions
for bc in self._bcs:
bc.apply(b, x)
def J(self, A, x):
assemble(self._J, tensor=A,
form_compiler_parameters=self._form_compiler_parameters,
keep_diagonal=False)
for bc in self._bcs:
bc.apply(A)
#if self.ident_zeros:
#A.ident_zeros()
problem = TersoffModelProblem(F,J,bcs=bcs)
class TersoffModelSolver(NewtonSolver):
def __init__(self, u, Pu, epsilon):
NewtonSolver.__init__(self)
self._epsilon = epsilon
#self._epsilon_ufl = epsilon_ufl
self._u = u
self._Pu = Pu
# def update_solution(self, x, dx):
# NewtonSolver.update_solution(self,x,dx)
# plot(self._u)
# plot(self._Pu,mesh=mesh)
solver = TersoffModelSolver(u, Pu, epsilon)
solver_parameters={'nonlinear_solver': 'snes',
'newton_solver':
{"linear_solver": "lu",
"maximum_iterations": 100,
'relative_tolerance': 1e-30,
'absolute_tolerance': 1e-20,
'relaxation_parameter': 1.0,
},
'snes_solver':
{'sign': 'default',
'method': 'default',
'absolute_tolerance': 1e-12,
'solution_tolerance': 1e-12,
}
}
solver.parameters.update(solver_parameters['newton_solver'])
# Solve the problem
(iter, converged) = solver.solve(problem,u.vector())
# Plot and hold solution
plot(u, interactive = False)
This is similar to the strategy used for hyperelasticity demo. My attempt to use this strategy results in the trivial solution u=0. I'd like to know a general strategy to obtain the correct energy form.