Gradient Descent in FEniCSx

I have a gradient descent code that i am trying to convert from FEniCS to FEniCSx. The original post where i got it to work is available “here“. The code is reproduced below:

from dolfin import *

L = 10
mesh = IntervalMesh(100, 0, L)
element = FiniteElement(“Lagrange”, mesh.ufl_cell(), 1)
V = FunctionSpace(mesh, “CG”, 1)
gamma = float(0.1)  # Learning rate.

u = Function(V)
u_up = Function(V)

phi = (1-u)**2

# Total potential energy
Pi = phi*dx

# Defining derivative
F = derivative(Pi, u)

F_vector = assemble(F)

# Set up the non-linear problem
u_up.vector()[:] = u.vector()[:] - gamma*F_vector[:]

Any help is highly appreciated.

It seems to me that translating a stand-alone code snippet is a great exercise to acquaint yourself with the FEniCSx syntax :wink:

The new syntax for the first 10 lines can be found on the first demo (Poisson equation — DOLFINx 0.9.0 documentation). The derivative is now ufl.derivative, I believe.

The code i shared was the MWE. I did most of the basic translation for the whole code. But i was getting enough errors that i wanted a good starting point to proceed from. Ie if this code was written in FEniCSx, I could proceed from there and figure out the modifications. I want to be clear that i am trying to figure it out by myself on the side. Was hoping for a little bit of a kick in the right direction.

Let me share a bigger (yet simpler) MWE. The idea is to have an initial guess stored in U. I perform gradient descent from this initial guess, and i update this initial guess. I know there is a tutorial that uses Scipy. But my cluster doesnt have scipy installed and ive tried to get them to install it. If i run the tutorial as is, it doesn’t work. Still waiting. Hoping that updating my old code would work.

The bigger yet simpler MWE is shared below:

import ufl                                                                                           
from ufl import *                                                                                    
import dolfinx                                                                                       
from dolfinx import *                                                                                
from mpi4py import MPI                                                                               
from petsc4py import PETSc                                                                           
from petsc4py.PETSc import ScalarType                                                                
import numpy as np                                                                                   
                                                                                                     
#Parameters                                                                                          
gamma = float(0.01)                                                                                  
NN = int(100)      #No of iterations.
kappa =float (2.0) #Material parameter.                                                                                                                             
tol = float(0.000001)                                                                                
                                                                                                     
#Create mesh and define function space                                                               
Nx = int(np.ceil(1*100/kappa))                                                                      
tol_prev = 1 # setting this to 1 randomly.                                                           
                                                                                                     
domain = dolfinx.mesh.create_unit_square(MPI.COMM_WORLD, Nx, Nx)                                     
                                                                                                     
#The trial functions are the 2d vector u and the degree 2 tensor q                                   
V = dolfinx.fem.functionspace(domain, ("Lagrange", 1))                                               
u = ufl.TrialFunction(V)                                                                             
u_up = ufl.TrialFunction(V)                                                                          
                                                                                                     
#The energy                                                                                          
Pi = ( (1-u**2)**2/2 + (u.dx(0)/kappa)**2 + (u.dx(1)/kappa)**2 )*ufl.dx                              
                                                                                                     
Fu = ufl.derivative(Pi, u)                                                                           
                                                                                                     
Fu_vec  = assemble(Fu)                                                                               
                                                                                                     
u_up.vector()[:]  = u.vector()[:]  - gamma*Fu_vec[:]                                                 
                                                                                                     
tol_test = np.linalg.norm(np.asarray(Fu_vec.get_local()))                                            
                                                                                                     
##Save solution in a .xdmf file and for paraview.                                                    
var_out = XDMFFile('/<path to output>/GL-0.xdmf')                   
var_out.write_checkpoint(u, "u", 0, XDMFFile.Encoding.HDF5, False) #false means not appending to file
pvd_file = File("/project/psharma/ssen/code/FEniCSx/Ginzburg-Landau/GL-0.pvd") # for paraview.       
pvd_file << u                                                                                        
var_out.close()                                                                                      
                                                                                                     
pie = assemble(*( (1-u**2)**2/2 + (u.dx(0)/kappa)**2 + (u.dx(1)/kappa)**2)*dx )          
                       
c = plot(u)                                                                                          
plt.title(r"$u(x)$",fontsize=26)                                                                     
cb = plt.colorbar(c)                                                                                 
plt.savefig('/<path to output>/u(x).png')
cb.remove()

MPI.finalized()

I am getting an error form the ufl.derivative line saying

ValueError: Can only create arguments automatically for non-indexed coefficients.

got it. I’m taking down the original post. Let me spend some more time on it.

Nonlinear forms shouldn’t use TrialFunctions, but functions, as pointed out in the aforementioned tutorial.
This is not new to DOLFINx, and was also a requirement in legacy FEniCS