Author Topic: Averaging the strain tensor  (Read 127 times)

lx_pico

  • Newbie
  • *
  • Posts: 11
Averaging the strain tensor
« on: June 22, 2019, 10:45:29 AM »
Hi, Ovito users,
    I'm trying to average the strain tersor of each atom in a certain selected region. Since i want tracing this region through every frames, BinAndReduce is not what i want. So, i used FreezePropertyModifier to keep traking the selected atoms. But when i try to average the strain tensor, i'm getting a error: "The DataCollection does not contain a particle property with the name 'Strain Tensor.XX'

Code: [Select]
modifier = AtomicStrainModifier(cutoff = 10.0,
eliminate_cell_deformation=True,
output_strain_tensors = True)
modifier.reference.load("reference file")
node.modifiers.append(modifier)

#select all atoms for test
mod = SelectExpressionModifier(expression = ' ParticleType!=0  ')
node.modifiers.append(mod)
modifier = FreezePropertyModifier(source_property = 'Selection',
  destination_property = 'Selection')
node.modifiers.append(modifier)
dataset.anim.current_frame = 0
modifier.take_snapshot()


def averaging_strain_tensors(frame, input, output):
StrainTensor_XX = input.particle_properties['Strain Tensor.XX'].array
StrainTensor_YY = input.particle_properties['Strain Tensor.YY'].array
StrainTensor_ZZ = input.particle_properties['Strain Tensor.ZZ'].array
StrainTensor_XY = input.particle_properties['Strain Tensor.XY'].array
StrainTensor_XZ = input.particle_properties['Strain Tensor.XZ'].array
StrainTensor_YZ = input.particle_properties['Strain Tensor.YZ'].array
selection = input.particle_properties['Selection'].array
output.attributes["strain"] = frame /5.0
output.attributes['XX_mean'] = numpy.mean(StrainTensor_XX[selection != 0])
output.attributes['YY_mean'] = numpy.mean(StrainTensor_YY[selection != 0])
output.attributes['ZZ_mean'] = numpy.mean(StrainTensor_ZZ[selection != 0])
output.attributes['XY_mean'] = numpy.mean(StrainTensor_XY[selection != 0])
output.attributes['XZ_mean'] = numpy.mean(StrainTensor_XZ[selection != 0])
output.attributes['YZ_mean'] = numpy.mean(StrainTensor_YZ[selection != 0])
node.modifiers.append(PythonScriptModifier(function=averaging_strain_tensors))

Isn't 'Strain Tensor.XX' a particle_properties? But i can find  'Strain Tensor.XX'  in the properties list in the SelectExpressionModifier under graphic mode.
« Last Edit: June 22, 2019, 11:27:07 AM by lx_pico »

Alexander Stukowski

  • Administrator
  • Hero Member
  • *****
  • Posts: 620
Re: Averaging the strain tensor
« Reply #1 on: June 23, 2019, 02:40:56 AM »
On the Python side, particle properties such as the 'Strain Tensor' property appear as two-dimensional Numpy arrays containing all the tensor components.  Thus, you should instead use the following code to access the six components of the symmetric strain tensor:

Code: [Select]
StrainTensor = input.particle_properties['Strain Tensor'].array
StrainTensor_XX = StrainTensor[:,0]
StrainTensor_YY = StrainTensor[:,1]
StrainTensor_ZZ = StrainTensor[:,2]
StrainTensor_XY = StrainTensor[:,3]
StrainTensor_XZ = StrainTensor[:,4]
StrainTensor_YZ = StrainTensor[:,5]

lx_pico

  • Newbie
  • *
  • Posts: 11
Re: Averaging the strain tensor
« Reply #2 on: June 24, 2019, 02:22:35 PM »
Thank you ,Alex. I can access the StrainTensor components now. But  i found that the total numbers in my seleted box is not conserve for each frame. Here is my code:

Code: [Select]
for frame in range(node.source.num_frames):
node.compute(frame)
print('Calculating ',frame, 'frames')

# select a certain block
Select=SelectExpressionModifier(expression = 'Position.X>10 && Position.X<30\
&& Position.Y>10 && Position.Y<30')
node.modifiers.append(Select)

# freeze the position of selected block in Frame0
modifier = FreezePropertyModifier(source_property = 'Selection',
  destination_property = 'Selection')
node.modifiers.append(modifier)
dataset.anim.current_frame = 0
modifier.take_snapshot()
node.compute(frame)

def averaging_strain_tensors(frame, input, output):
...
node.compute(frame)

print(node.output.attributes['SelectExpression.num_selected'],node.output.attributes['XX_mean'])


the results are:
Calculating  0 frames
1358 -4.05699e-08
Calculating  1 frames
1342 0.000547836
Calculating  2 frames
1338 -0.00055211

then i check produce in graphic mode, it seems the tracking of selected atom had no problem, but the "'SelectExpression.num_selected" are still the value for (expression = 'Position.X>10 && Position.X<30 && Position.Y>10 && Position.Y<30') in each frame.

My question is, which group of atoms had been averaged for tensors in each frame? is the (expression = 'Position.X>10 && Position.X<30 && Position.Y>10 && Position.Y<30') in frame0 being tracked, or the (expression = 'Position.X>10 && Position.X<30 && Position.Y>10 && Position.Y<30') in each frame? If the latter,how should i revise?

Alexander Stukowski

  • Administrator
  • Hero Member
  • *****
  • Posts: 620
Re: Averaging the strain tensor
« Reply #3 on: June 24, 2019, 02:47:14 PM »
Yes, the global attribute 'SelectExpression.num_selected' will reflect the changing number of atoms in your region, not the initial number at time 0. This is because the FreezePropertyModifier only freezes the selection state of the individual atoms but not the global value of the attribute dynamically set by the SelectExpressionModifier. What the FreezePropertyModifier, coming after the SelectExpressionModifier in the data pipeline, does is to overwrite the changing selection state of atoms with the saved original state. But it does not do that the same for the 'SelectExpression.num_selected' attribute.

Thus, if you need to know many atoms are selected, you can either use the value of 'SelectExpression.num_selected' at frame 0, or count the number of atoms yourself that are part of the selection set, i.e. by considering the values of the 'Selection' particle property. 

Furthermore, let me point out that you script keeps adding modifiers to the pipeline in a for-loop. This is a typical mistake and is not how it should be done. Instead, you should create and insert all modifiers only once in the beginning before entering the for-loop. Inside of the for-loop, you should only make calls to node.compute() or change the settings of existing modifiers in the pipeline. Otherwise you pipeline will become longer and longer with each iteration of the for-loop.

lx_pico

  • Newbie
  • *
  • Posts: 11
Re: Averaging the strain tensor
« Reply #4 on: July 03, 2019, 04:31:35 PM »
Hi, Alex

Now i also try to output the average position of the selected block

Code: [Select]
def averaging_strain_tensors(frame, input, output):
Position = input.particle_properties['Position'].array
Position_X = Position[:,0]
        mean_PositionX = mean()
node.compute(frame)

But i got artificial results for those blocks acrossing the periodic boundary. Clearly, i need to access the "unwarpped" coordinates or the image flags, do Ovito store these values?

Alexander Stukowski

  • Administrator
  • Hero Member
  • *****
  • Posts: 620
Re: Averaging the strain tensor
« Reply #5 on: July 04, 2019, 07:40:07 AM »
The easiest solution is to make sure the imported atom coordinates are already in unwrapped form. For example, when using LAMMPS to generate dump files, the "xu", "yu" and "zu" fields should be exported.

In case you have already run your simulation and it is too late to do this, you need to use OVITO's "Unwrap Trajectories" modifier, which is only available in the latest development build of OVITO 3.0.

This modifier can either make use of the image flag information (ix,iy,iz) to unfold the trajectories -if these fields are present in the dump file-, or it can try to detect crossings of the periodic boundaries by analyzing the trajectories for discontinuities.

See here for more information: https://ovito.org/manual_testing/particles.modifiers.unwrap_trajectories.html

You need to insert the Unwrap Trajectories modifier into the data pipeline before your user-defined modifier function for computing the center of mass.