Search code examples
pythonnumpynumpy-ndarrayarray-broadcastingnumpy-slicing

What is the most efficient way to handle conversion from full to symmetric second order tensors using numpy?


I am processing symmetric second order tensors (of stress) using numpy. In order to transform the tensors I have to generate a fully populated tensor, do the transformation and then recover the symmetric tensor in the rotated frame.

My input is a 2D numpy array of symmetric tensors (nx6). The code below works, but I'm pretty sure there must be a more efficient and/or elegant way to manipulate the arrays but I can't seem to figure it out.

I anyone can anyone suggest an improvement I'd be very grateful? The sample input is just 2 symmetric tensors but in use this could be millions of tensors, hence the concernr with efficiency

Thanks,

Doug

# Sample symmetric input (S11, S22, S33, S12, S23, S13)
sym_tens_in=np.array([[0,9], [1,10], [2,11], [3,12], [4,13], [5,14]])
   
# Expand to full tensor
tens_full=np.array([[sym_tens_in[0], sym_tens_in[3], sym_tens_in[4]],
                    [sym_tens_in[3], sym_tens_in[1], sym_tens_in[5]],
                    [sym_tens_in[4], sym_tens_in[5], sym_tens_in[2]]])

# Transpose and reshape to n x 3 x 3 
tens_full=np.transpose(tens_full, axes=(2, 0, 1))

# This where the work on the full tensor will go....

# Reshape for extraction of the symmetric tensor
tens_full=np.reshape(tens_full, (2,9))

# Create an array for the test ouput symmetric tensor
sym_tens_out=np.empty((2,6), dtype=np.int32)

# Extract the symmetric components
sym_tens_out[:,0]=tens_full[:,0]
sym_tens_out[:,1]=tens_full[:,4]
sym_tens_out[:,2]=tens_full[:,8]
sym_tens_out[:,3]=tens_full[:,2]
sym_tens_out[:,4]=tens_full[:,3]
sym_tens_out[:,5]=tens_full[:,5]

# Transpose....
sym_tens_out=np.transpose(sym_tens_out)

Solution

  • This won't be any faster, but it's more compact:

    In [166]: idx=np.array([0,3,4,3,1,5,4,5,2]).reshape(3,3)
    In [167]: sym_tens_in[idx].transpose(2,0,1)
    Out[167]: 
    array([[[ 0,  3,  4],
            [ 3,  1,  5],
            [ 4,  5,  2]],
    
           [[ 9, 12, 13],
            [12, 10, 14],
            [13, 14, 11]]])
    

    The transpose could be done first:

    sym_tens_in.T[:,idx]
    

    Similarly the reverse mapping can be done with:

    In [168]: idx1 = [0,4,8,1,2,5]
    In [171]: tens_full.reshape(2,-1)[:,idx1]
    Out[171]: 
    array([[ 0,  1,  2,  3,  4,  5],
           [ 9, 10, 11, 12, 13, 14]])
    

    with the optional transpose.