UFL Representation of Differentiation

Hello,

I’m just try to understand the resultant of diff operator. It is a good way to get differentiation with automated way but is it possible to represent the resultant explicitly by UFL representation ?

An example from hyperelasticity:

F = Identity(len(u)) + grad(u)
F = variable(F)
.
.
.


#Free Energy Form:
psi= (mu/2)*((Ic - 3))- mu*ln(J) + (lmbda/2)*(ln(J))**2

#Exact PK Sress:
S_exact = lambda F: mu * F - mu * inv(F).T + lmbda * ln(det(F)) * inv(F).T

#Alternative PK Stress:
S_alter = diff(psi, F)


For simple problems as above, it is simple to take difference between S_exact and S_alter but for complicated energy functions, I cannot be sure about the resultant of diff operator. Is there any way to print the result of S_Alter explicitly as we write S_exact ?

Regards,

You can print the symbolic form of derivatives from diff as follows:

from dolfin import *
u = Constant(1)
u.rename("u","u")
u = variable(u)
f = exp(sin(u**2))
df_du = diff(f,u)
print(expand_derivatives(df_du))


In the above minimal example, it’s easy to interpret and verify the output, but the automatically-generated derivatives of more complicated expressions may not be simplified in the same way as they would be when calculated by hand, which can make them difficult to read.

Hello kamensky,

Thanks for your answer. I tried your simple example, and it works well. However, for complicated one I got thousands of lines which is not meaningful as below:

{ A | A_{i_{229}, i_{230}} = (({ A | A_{i_{221}, i_{222}} = f_21[5] * (sum_{i_{47}} ({ A | A_{i_{219}, i_{220}} = ([0, 1.0, 0])[i_{47}] * ({ A | A_{i_{217}, i_{218}} = ({ A | A_{i_{38}, i_{215}, i_{216}} = (sum_{i_{39}} ({ A | A_{i_{213}, i_{214}} = ([0, 1.0, 0])[i_{39}] * ({ A | A_{i_{211}, i_{212}} = ({ A | A_{i_{35}, i_{36}, i_{209}, i_{210}} = (sum_{i_{37}} ({ A | A_{i_{207}, i_{208}} = ({ A | A_{i_{203}, i_{204}} = ({ A | A_{i_{12}, i_{13}, i_{133}, i_{134}} = (sum_{i_{14}} ({ A | A_{i_{131}, i_{132}} = ({ A | A_{i_{129}, i_{130}} = ({ A | A_{i_{51}, i_{53}, i_{52}, i_{54}} = I[i_{51}, i_{52}] * I[i_{53}, i_{54}] })[i_{12}, i_{14}, i_{129}, i_{130}] })[i_{131}, i_{132}] * ({ A | A_{i_{42}, i_{43}} = ([
[(({ A | A_{i_{40}, i_{41}} = ([0, 0, 1.0])[i_{40}] * ([0, 0, 1.0])[i_{41}] }) + ({ A | A_{i_{10}, i_{11}} = (I + ({ A | A_{i_8, i_9} = -1 * ({ A | A_{i_{40}, i_{41}} = ([0, 0, 1.0])[i_{40}] * ([0, 0, 1.0])[i_{41}] })[i_8, i_9] }))[i_{10}, i_{11}] * (1 + f_45) }))[1, 1] * (({ A | A_{i_{40}, i_{41}} = ([0, 0, 1.0])[i_{40}] * ([0, 0, 1.0])[i_{41}] }) + ({ A | A_{i_{10}, i_{11}} = (I + ({ A | A_{i_8, i_9} = -1 * ({ A | A_{i_{40}, i_{41}} = ([0, 0, 1.0])[i_{40}] * ([0, 0, 1.0])[i_{41}] })[i_8, i_9] }))[i_{10}, i_{11}] * (1 + f_45) }))[2, 2] + -1 * (({ A | A_{i_{40}, i_{41}} = ([0, 0, 1.0])[i_{40}] * ([0, 0, 1.0])[i_{41}] }) + ........


Is there any other method to read it ?

The output is technically meaningful; you can still interpret simpler examples like the following:

from dolfin import *
u = Constant((1,1))
u.rename("u","u")
u = variable(u)
v = Constant((1,1))
v.rename("v","v")
print(expand_derivatives(diff(dot(u,v),u)))


What a human analyst might write is:

\frac{\partial (u_i v_i)}{\partial u_j} = \delta_{ij}v_i = v_j

The output

sum_{i_8} ({ A | A_{i_{10}} = ({ A | A_{i_9} = I[i_8, i_9] })[i_{10}] * v[i_8] })


corresponds roughly to the first equality (where \delta is I). The notation

({ A | A_{x} = y })


can be read "an indexed tensor with free indices x, defined as y". The definition y may itself be defined in terms of more such indexed tensors. In the above example, the free index j is i_10 and the summation index i is i_8.

Of course, anything even slightly more complex becomes very tedious to read. However, the differentiation is already pretty well validated. Thus, it’s likely not worth the effort to try deciphering UFL’s internal representation of derivatives, which is better-suited to automation and code generation than human readability.

1 Like