IndexError: tuple index out of range

Hello everyone,
I am trying to reproduce one of the codes given in Computational Reality : Solving Nonlinear and Coupled Problems in Continuum Mechanics (great book by the way) by @bilenemek but unable to do so due to the following error -

Here is the MWE to reproduce the error -

from fenics import *

xL=20.0 
yL=20.0
zL=20.0

mesh = BoxMesh(Point(-xL /2 , -yL /2 , -zL ) , Point( xL/2 , yL/2 , 0), 30 ,30,30)

V = VectorFunctionSpace(mesh , 'P' , 1)
T = TensorFunctionSpace(mesh , 'P' , 1)
du = TrialFunction(V)
delu = TestFunction(V)
u00 = Function(V)
u0 = Function(V)
u = Function(V)

init = Expression(( '0.' , '0.' , '0.'), degree = 0)
u.interpolate(init)
u0.assign(u)
u00.assign(u0)
 
delta = Identity(3)

F = as_tensor(delta[0, 1] + u[0].dx(1))

eps= as_tensor( 1.0/2.0*(u[0].dx(2) + u[2].dx(0)))
eps0= as_tensor( 1.0/2.0*(u0[0].dx(2) + u0[2].dx(0)))

eps_dev= as_tensor(eps[0, 1] - 1.0/3.0*eps[2, 2]*(delta[0, 1]))

Error -

---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
/tmp/ipykernel_1270/3465712857.py in <module>
----> 1 eps_dev= as_tensor(eps[0, 1] - 1.0/3.0*eps[2, 2]*(delta[0, 1]))

/usr/lib/python3/dist-packages/ufl/exproperators.py in _getitem(self, component)
    436 
    437     # Analyse slices (:) and Ellipsis (...)
--> 438     all_indices, slice_indices, repeated_indices = create_slice_indices(component, shape, self.ufl_free_indices)
    439 
    440     # Check that we have the right number of indices for a tensor with

/usr/lib/python3/dist-packages/ufl/index_combination_utils.py in create_slice_indices(component, shape, fi)
    150             all_indices.append(ind)
    151         elif isinstance(ind, int):
--> 152             if int(ind) >= shape[len(all_indices)]:
    153                 error("Index out of bounds.")
    154             all_indices.append(FixedIndex(ind))

IndexError: tuple index out of range

Kindly provide me some assistance to solve this error.
Thanks.

Thanks a lot for great words! The code in the book is a bit old, please download #027 source code on my web site

https://bilenemek.abali.org/src/bib/027.zip

In the zip file, there is a pdf including all changes you need to do in order to run it in the legacy version dolfin 2019.2.

If not working, please write it here.

Best, Emek

Thanks for the quick response. I downloaded #027 zip file from the website but it contains only correction pdf and does not have source code I was looking for (linear rheology code that is). Can you please tell me where to find the code you are referring to in your comment ?
Thanks.

Yes, it has the pdf with changes you need to do for bringing the source code into the current version. Source codes are in the book, I did not put them into the zip file. Looking from the book and writing the code is a beneficial exercise to understand the code.

But if you do not want to write them on your own, write me an email and I send you the python file.

Emek

I made the changes suggested by you in the pdf but now the equation is solved in 0 iterations as initial guess is (0, 0, 0). When I am trying non-zero guess like (0.01, 0., 0.), the newton solver converges but I am not able to make any useful interpretation from the results.

Can you please tell me where I am going wrong ?

Here is the modified code -

from dolfin import *
from ufl import indices

xL=20.0 
yL=20.0
zL=20.0

mesh = BoxMesh(Point(-xL /2 , -yL /2 , -zL ) , Point( xL/2 , yL/2 , 0), 5, 5, 5)

V = VectorFunctionSpace(mesh , 'P' , 1)
T = TensorFunctionSpace(mesh , 'P' , 1)

left = CompiledSubDomain( 'near( x[0] , l ) && on_boundary' , l = -xL/2)
right = CompiledSubDomain( 'near( x[0] , l ) && on_boundary' , l=xL/2)
back = CompiledSubDomain( 'near( x[1] , l ) && on_boundary' , l=-yL/2)
front= CompiledSubDomain( 'near( x[1] , l ) && on_boundary' , l=yL/2)
bottom = CompiledSubDomain( 'near( x[2] , l ) && on_boundary' , l=-zL)
top = CompiledSubDomain( 'x[2] == 0.0 && (x[0]*x[0]) + (x[1]*x[1]) <= r*r ', r=0)

cells = MeshFunction("size_t", mesh, mesh.topology().dim(), 0)
facets = MeshFunction("size_t", mesh, mesh.topology().dim() - 1, 0)
dA = Measure( 'ds' , domain=mesh , subdomain_data= facets, metadata = {"quadrature_degree": 2})
dV = Measure( 'dx' , domain=mesh , subdomain_data= cells, metadata = {"quadrature_degree": 2})
facets.set_all(0)

nanoindent = Expression((' 0.0' , '0.0' , '-Amp' ) , degree = 1, Amp = 1.)

bc = [ DirichletBC(V, ( 0. , 0. , 0. ) , bottom ) ]
f_gr = Constant((0. , 0. ,0. ))

du = TrialFunction(V)
delu = TestFunction(V)
u00 = Function(V)
u0 = Function(V)
u = Function(V)
S=Function(T)
S0=Function(T)

t = 0.0
tend = 3000.0
dt = 100.

init = Expression(( '0.' , '0.' , '0.' ), degree = 0)
u.interpolate(init )
u0.assign(u)
u00.assign(u0)

rho0 = 9000.0*(10**-15) 
lambada = 90.0 
E1 , E2 = 200.0 , 200.0 
mu = 2.0*(10**5) 
i,j,k,r = indices(4)
delta = Identity(3)

F = as_tensor(delta[i, j] + u[i].dx(j), [i, j])

eps= as_tensor( 1.0/2.0*(u[i].dx(k) + u[k].dx(i)), [i, k])
eps0= as_tensor( 1.0/2.0*(u0[i].dx(k) + u0[k].dx(i)), [i,k])

eps_dev= as_tensor(eps[i, j] - 1.0/3.0*eps[k, k]*(delta[i, j]), [i, j])
eps0_dev= as_tensor(eps0[i, j] - eps0[k, k]*1.0/3.0*(delta[i, j]), [i, j])

devS0= as_tensor(S0[i, j] - S0[k, k]*1.0/3.0*delta[i, j], [i, j])
S = as_tensor(lambada*eps[ j, j ]*delta[ i, k ] + mu/(E2*dt+mu)*devS0[ i, k] + E1*E2*dt / (E2*dt+mu)*eps_dev[ i, k ] + mu*( E1+E2 ) /( E2*dt+mu)*(eps_dev[i, k] - eps0_dev[i, k]), [i, k])

N = FacetNormal(mesh)

Form = (rho0/dt/dt*(u - 2.*u0 + u00)[i]*delu[i] + F[i, k]*S[r, k]*delu[i].dx(r) - rho0*f_gr[i]*delu[i])*dV - nanoindent[i]*delu[i]*dA(1)
nz = as_tensor([ 0.0 , 0.0 , 1.0])
forceZ = as_tensor(F[i, k]*S[r, k]*nz[r], [i,])

Gain = derivative(Form , u , du )
file_u = File('disp.pvd')

u_max = 0.1
A = 24.5*u_max**2
radius = sqrt(A/pi)

r = radius
top.mark(facets , 1)

time_values =[0.0]
u_max_values =[0.0]
forceZ_values =[0.0]

solver_parameters = {
    "newton_solver": {
        "linear_solver": "cg",
        "preconditioner" : "hypre_amg",
        "relative_tolerance" : 1e-2,
        "absolute_tolerance" : 1e-5,
        "maximum_iterations" : 30
    }
}

form_compiler_parameters = {
    "cpp_optimize": True,
    "representation": "quadrature",
    "quadrature_degree": 2
}

while t<tend:
    t += dt
    
    if t < 1000.: nanoindent.Amp = 0.002*t/A
    if t >= 1000. and t <=1500.: nanoindent.Amp = 2./A
    if t > 1500.: nanoindent.Amp = (2. - 0.002*(t - 1500.))/A
    if t >=2500.: nanoindent.Amp = 0.
        
    solve(Form == 0 , u , bc , J=Gain , solver_parameters= solver_parameters,form_compiler_parameters= form_compiler_parameters)

    file_u << (u,t)
    time_values.append(t)
    u_max = abs(u((0 ,0 ,0))[2])
    u_max_values.append(u_max)
    fZ = project(forceZ,V)
    fZvalue = abs(fZ((0 ,0 ,0))[2])*A
    forceZ_values.append(fZvalue)
    S0.assign(project(S ,T))
    u00.assign(u0)
    u0.assign(u)

Newton-solver on initial trivial guess -

Solving nonlinear variational problem.
  Newton iteration 0: r (abs) = 0.000e+00 (tol = 1.000e-05) r (rel) = -nan (tol = 1.000e-02)
  Newton solver finished in 0 iterations and 0 linear solver iterations.

PS - I have not refined the mesh as my memory keeps running out when I do it.

I guess that r=0 in top is a bit of problematic. Later you change r = … but there is no link with python r and top’s inside r. You need to renew like top.r = radius before top.mark(…) and also in the time loop.

Thanks for the guidance, but when I renew top.r = radius, it gives the error -

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
/tmp/ipykernel_2830/1860499864.py in <module>
      2 A = 24.5*u_max**2
      3 radius = sqrt(A/pi)
----> 4 top.r = radius
      5 top.mark(facets , 1)
      6 
AttributeError: 'dolfin.cpp.mesh.SubDomain' object has no attribute 'r'

My bad, top is not an Expression, so you cannot update its value.

Either, you use

facets.set_all(0)
A = 24.5*u_max**2
radius = sqrt(A/pi)
top = CompiledSubDomain( 'x[2] == 0.0 && (x[0]*x[0]) + (x[1]*x[1]) <= r*r ', r=radius)
top.mark(facets , 1)

whenever you want to update the selected surface elements. Or you may think of doing it by the expression directly and selecting the whole surface but applying a Gauss distribution function in order to update its value

facets.set_all(0)
top = CompiledSubDomain( 'x[2] == 0.0 ')
top.mark(facets , 1)

A = 24.5*u_max**2
radius = sqrt(A/pi)
nanoindent = Expression((' 0.0' , '0.0' , '-Amp * exp( 1./(2.*sig*sig) * (-pow(x[0], 2) -pow(x[1], 2)) )' ) , degree = 2, Amp = 1., sig = radius/2.)

then in the time loop, nanoindent.sig = radius/2. would update it. No need to change the marking.

This Gauss distribution is the 2D version of

where I assume that the coordinate 0,0,0 is the indenting tip. But you may use

nanoindent = Expression((' 0.0' , '0.0' , '-Amp * exp( 1./(2.*sig*sig) * (-pow(x[0]-xtip, 2) -pow(x[1]-ytip, 2)) )' ) , degree = 2, Amp = 1., sig = radius/2., xtip=..., ytip=...)

for a nanoindenter at the position xtip, ytip.

1 Like

Thanks for the insightful answer, it solved the problem. Just last question, I’m not getting the same values or trend given in the book’s figure. Is it because I’m not using refined mesh ? I have attached the whole code with all the modifications as well.

Book’s figure -

My figure -

Here is the whole code -

from dolfin import *
from ufl import indices

xL=20.0 
yL=20.0
zL=20.0

mesh = BoxMesh(Point(-xL /2 , -yL /2 , -zL ) , Point( xL/2 , yL/2 , 0), 5, 5, 5)

V = VectorFunctionSpace(mesh , 'P' , 1)
T = TensorFunctionSpace(mesh , 'P' , 1)

left = CompiledSubDomain( 'near( x[0] , l ) && on_boundary' , l = -xL/2)
right = CompiledSubDomain( 'near( x[0] , l ) && on_boundary' , l=xL/2)
back = CompiledSubDomain( 'near( x[1] , l ) && on_boundary' , l=-yL/2)
front= CompiledSubDomain( 'near( x[1] , l ) && on_boundary' , l=yL/2)
bottom = CompiledSubDomain( 'near( x[2] , l ) && on_boundary' , l=-zL)
top = CompiledSubDomain('x[2] == 0.0')

u_max = 0.1
A = 24.5*u_max**2
radius = sqrt(A/pi)
nanoindent = Expression((' 0.0' , '0.0' , '-Amp * exp( 1./(2.*sig*sig) * (-pow(x[0], 2) -pow(x[1], 2)) )' ) , degree = 2, Amp = 1., sig = radius/2.)

cells = MeshFunction("size_t", mesh, mesh.topology().dim(), 0)
facets = MeshFunction("size_t", mesh, mesh.topology().dim() - 1, 0)
dA = Measure( 'ds' , domain=mesh , subdomain_data= facets, metadata = {"quadrature_degree": 2})
dV = Measure( 'dx' , domain=mesh , subdomain_data= cells, metadata = {"quadrature_degree": 2})
facets.set_all(0)
top.mark(facets , 1)

bc = [ DirichletBC(V, ( 0. , 0. , 0. ) , bottom )]
f_gr = Constant((0. , 0. ,0. ))

du = TrialFunction(V)
delu = TestFunction(V)
u00 = Function(V)
u0 = Function(V)
u = Function(V)
S=Function(T)
S0=Function(T)

t = 0.0
tend = 3000.0
dt = 100.

init = Expression(('0.' , '0.' , '0.'), degree = 0)
u.interpolate(init)
u0.assign(u)
u00.assign(u0)

rho0 = 9000.0*(10**-15) 
lambada = 90.0 
E1 , E2 = 200.0 , 200.0 
mu = 2.0*(10**5) 
i,j,k,r = indices(4)
delta = Identity(3)

F = as_tensor(delta[i, j] + u[i].dx(j), [i, j])

eps= as_tensor( 1.0/2.0*(u[i].dx(k) + u[k].dx(i)), [i, k])
eps0= as_tensor( 1.0/2.0*(u0[i].dx(k) + u0[k].dx(i)), [i,k])

eps_dev= as_tensor(eps[i, j] - 1.0/3.0*eps[k, k]*(delta[i, j]), [i, j])
eps0_dev= as_tensor(eps0[i, j] - eps0[k, k]*1.0/3.0*(delta[i, j]), [i, j])

devS0= as_tensor(S0[i, j] - S0[k, k]*1.0/3.0*delta[i, j], [i, j])
S = as_tensor(lambada*eps[ j, j ]*delta[ i, k ] + mu/(E2*dt+mu)*devS0[ i, k] + E1*E2*dt / (E2*dt+mu)*eps_dev[ i, k ] + mu*( E1+E2 ) /( E2*dt+mu)*(eps_dev[i, k] - eps0_dev[i, k]), [i, k])

N = FacetNormal(mesh)

Form = (rho0/dt/dt*(u - 2.*u0 + u00)[i]*delu[i] + F[i, k]*S[r, k]*delu[i].dx(r) - rho0*f_gr[i]*delu[i])*dV - nanoindent[i]*delu[i]*dA(1)
nz = as_tensor([ 0.0 , 0.0 , 1.0])
forceZ = as_tensor(F[i, k]*S[r, k]*nz[r], [i,])

Gain = derivative(Form , u , du )
file_u = File('disp.pvd')

time_values =[0.0]
u_max_values =[0.0]
forceZ_values =[0.0]

solver_parameters = {
    "newton_solver": {
        "linear_solver": "cg",
        "preconditioner" : "hypre_amg",
        "relative_tolerance" : 1e-2,
        "absolute_tolerance" : 1e-5,
        "maximum_iterations" : 30
    }
}

form_compiler_parameters = {
    "cpp_optimize": True,
    "representation": "quadrature",
    "quadrature_degree": 2
}

while t<tend:
    t += dt
    
    if t < 1000.: nanoindent.Amp = 0.002*t/A
    if t >= 1000. and t <=1500.: nanoindent.Amp = 2./A
    if t > 1500.: nanoindent.Amp = (2. - 0.002*(t - 1500.))/A
    if t >=2500.: nanoindent.Amp = 0.
        
    nanoindent.sig = radius/2.
    solve(Form == 0 , u , bc , J=Gain , solver_parameters= solver_parameters,form_compiler_parameters= form_compiler_parameters)

    file_u << (u,t)
    time_values.append(t)
    u_max = abs(u((0 ,0 ,0))[2])
    u_max_values.append(u_max)
    fZ = project(forceZ,V)
    fZvalue = abs(fZ((0 ,0 ,0))[2])*A
    forceZ_values.append(fZvalue)
    S0.assign(project(S ,T))
    u00.assign(u0)
    u0.assign(u)

I do not know but I would also guess that the discrepancy is due to the mesh. I used there a very fine mesh to get the tip as small as possible, even though needed to use u_max=0.1 rather “gigantic” to begin with.

Yet in your example, you use a Gauss distribution with Amp as the max at 0,0,0 but the total energy is different than in the book. So the values may be different.

The only disturbing fact is that the time loop is holding the Amp constant between 1000 and 1500, but the force in the graph is still increasing.

A small hint, in the time loop, u_max = … is creating a new object such that the u_max value outside of the time loop is still the same, in other words, you think radius is changing, but it is not. Simply use a print() in the time loop and check right before nanoindent.sig if the radius value is changing. I would simply use

A = 24.5*u_max**2
radius = sqrt(A/pi)

in the time loop as well in order to use the new u_max in the loop.

Depending on the application, you may need to increase the mesh. Please do a convergence analysis in order to estimate the error because of the mesh.

Best, Emek

You are right, radius was not changing due to u_max being constant. I added the lines mentioned by you in the time loop. But now value of u_max with time is not going as expected and due to which F vs u_max graph is also unexpected. What can be the source of this error ?

Also, can you please explain what do you mean by -

Thanks.

I think that your main question has been answered and you start asking application specific questions.

Your initial u_max is too large causing some problems. Also controlling force is a bit tough in FEM, you may try to control the displacement and DirichletBC for a better result.

Convergence analysis is an FEM test where number of nodes are doubled in each iteration. A comparison between doubled degrees of freedom gives a monotonous convergence and a relative error. Without knowing the solution, it is then possible to find out the error introduced by the mesh. If the error is smaller than acceptable, say 1% or so, the mesh is fine enough.

Best, Emek

I will try out displacement controlled approach and do some more digging how to carry out convergence analysis on mesh in order to apply it.

I didn’t realize that the initial question have been solved and I have digressed from the main question. Thanks for reminding.

Thanks for your patience, time and solutions to the above problems.