Author Topic: Python Script Loop  (Read 753 times)

jhart

  • Newbie
  • *
  • Posts: 22
Python Script Loop
« on: September 12, 2018, 07:23:06 AM »
Hello,

I have the script below. Notice that I read in a file that is 200eV.dump and it creates a single column of data, the number of vacancies at each frame. I actually have a lot of dump files, that start at 002eV and increase up to 200eV with no repeating pattern (i.e. 002eV.dump, 0.005eV.dump, 0.010eV.dump etc).

How can I loop through all of these files without having to manually read in each dump file for each energy?

The script writes out the number of defects at each timestep for each defect type

Code: [Select]
from ovito.io import *
from ovito.data import *
from ovito.modifiers import *
import numpy as np

node = import_file("../200eV.dump",multiple_frames = True)

# Perform Wigner-Seitz analysis:
ws = WignerSeitzAnalysisModifier(
    per_type_occupancies = True,
    eliminate_cell_deformation = True)
ws.reference.load("../../../WS_Ref/ws.dump")
node.modifiers.append(ws)

# Define a modifier function that selects sites of type A=1 which
# are occupied by exactly one atom of type B=2.
def modify(frame, input, output):

    # Retrieve the two-dimensional Numpy array with the site occupancy numbers.
    occupancies = input.particle_properties['Occupancy'].array
   
    # Get the site types as additional input:
    site_type = input.particle_properties.particle_type.array

    # Calculate total occupancy of every site:
    total_occupancy = np.sum(occupancies, axis=1)

    # Set up a particle selection by creating the Selection property:

    selection1 = (site_type == 1) & (occupancies[:,0] == 0) & (occupancies[:,1] == 0)
   
    output.attributes['Ca_Vac'] = np.count_nonzero(selection1)


# Insert Python modifier into the data pipeline.
node.modifiers.append(PythonScriptModifier(function = modify))

# Let OVITO do the computation and export the number of identified
# antisites as a function of simulation time to a text file:
export_file(node, "defects_200.txt", "txt",
    columns = ['Timestep', 'Ca_Vac'],
    multiple_frames = True)


Alexander Stukowski

  • Administrator
  • Hero Member
  • *****
  • Posts: 516
Re: Python Script Loop
« Reply #1 on: September 12, 2018, 09:57:41 AM »
Hi,

Here is how it is typically done: Put the existing call to export_file() into a Python for-loop and repeatedly use FileSource.load() to replace the input of the data pipeline with a new file:

Code: [Select]
import os

# ... do the pipeline setup here

for filename in files_to_process:

    # Set a new input file for the current data pipeline:
    node.source.load(filename, multiple_frames = True)

    # Derive the name of the output txt file from the input dump file:
    output_filename = os.path.splitext(filename)[0] + ".txt"

    # Let OVITO do the processing:
    export_file(node, output_filename, "txt",
        columns = ['Timestep', 'Ca_Vac'],
        multiple_frames = True)

The list files_to_process should contain the names of the dump files you want to process.

jhart

  • Newbie
  • *
  • Posts: 22
Re: Python Script Loop
« Reply #2 on: September 26, 2018, 04:46:44 AM »
So I was able to get the WS analysis to work, and here is how I did it:

Code: [Select]
import numpy as np
from ovito.data import *
from ovito.io import *
from ovito.modifiers import *

ws = WignerSeitzAnalysisModifier(
    per_type_occupancies=True,
    eliminate_cell_deformation=True)
ws.reference.load("../../../WS_Ref/ws.dump")


def modify(frame, input, output):
    occupancies = input.particle_properties['Occupancy'].array

    site_type = input.particle_properties.particle_type.array

    total_occupancy = np.sum(occupancies, axis=1)  # you are also not using, also not using the frame parameter

    selection1 = (site_type == 2) & (occupancies[:, 0] == 0) & (occupancies[:, 1] == 0)

    output.attributes['F_Vac'] = np.count_nonzero(selection1)


import glob


for file in sorted(glob.glob('../*.dump')):
    node = import_file(file, multiple_frames=True)
    node.modifiers.append(ws)
    node.modifiers.append(PythonScriptModifier(function=modify))
    export_file(
node, "defects_{}.txt".format(file[-10:-7]), "txt",
columns=['Timestep', 'F_Vac'],
multiple_frames=True
    )

I tried changing this to just calculate displacements, rather than WS analysis, but it does not seem to work. Do you see any obvious mistakes here? The error I am getting, though there could be more errors is:

How could i modify this for displacements, with c_displ[4] being the compute within lammps? I keep going through various changes in the code but I cannot get it to work. Any help with how to modify this for displacements?
My code right now is:

Code: [Select]
# Import OVITO modules.
from ovito.io import *
from ovito.modifiers import *

import numpy as np
from ovito.data import *
from ovito.io import *
from ovito.modifiers import *



import glob


for file in sorted(glob.glob('../*.dump')):
    node = import_file(file, multiple_frames=True)
    node.modifiers.append(SelectExpressionModifier(expression = 'c_disp[4]>0.8'))
    node.compute()
    export_file(
node, "defects_{}.txt".format(file[-10:-7]), "txt",
columns=['Timestep', 'Displacements'],
multiple_frames=True
    )

           
But I'm getting this error:

Code: [Select]
RuntimeError: Data pipeline evaluation failed: Modifier 'Expression selection' reported: Unexpected token "[4]>0.8 " found at position 6.
ERROR: The Python script 'displace.py' has exited with an error.

« Last Edit: September 26, 2018, 05:09:47 AM by jhart »

Constanze Kalcher

  • Administrator
  • Full Member
  • *****
  • Posts: 121
Re: Python Script Loop
« Reply #3 on: September 26, 2018, 11:32:27 AM »
Hi,

I haven't gone through your whole script but the current error is definitely due to the naming of the variables in the expression select modifier. I tried with an expression select on an example file in the GUI version of ovito, where you can see the available particle properties in the "Variables" panel. 
You should try the following:

Code: [Select]
node.modifiers.append(SelectExpressionModifier(expression = 'c_disp_4_>0.8'))
This will select all atoms with displacement magnitudes larger than 0.8. What would you like to do with these atoms? Let me know if you need further help.

-Constanze
« Last Edit: September 26, 2018, 11:41:44 AM by Constanze Kalcher »

jhart

  • Newbie
  • *
  • Posts: 22
Re: Python Script Loop
« Reply #4 on: September 28, 2018, 05:55:57 AM »
Thanks, that seemed to fix that error. Now I am getting another one:

Code: [Select]
Traceback (most recent call last):
  File "displace.py", line 22, in <module>
    multiple_frames=True
  File "/apps/projects/ovito/ovito-3.0.0-dev200-x86_64/bin/../lib/ovito/plugins/python/ovito/io/export_file.py", line 177, in export_file
    if not exporter.export_nodes(ovito.task_manager):
RuntimeError: The global attribute 'Displacements' to be exported is not available at animation frame 0.
ERROR: The Python script 'displace.py' has exited with an error.

Constanze Kalcher

  • Administrator
  • Full Member
  • *****
  • Posts: 121
Re: Python Script Loop
« Reply #5 on: September 28, 2018, 09:29:50 AM »
Hi,
well you never calculated a global attribute "Displacement" that you're trying to export. You only created a selection based on the potential energy of the atoms. (This means that they now have a particle property "Selection" which is either 0 or 1. Again, what would you like to do with this selection?

-Constanze

jhart

  • Newbie
  • *
  • Posts: 22
Re: Python Script Loop
« Reply #6 on: September 29, 2018, 06:48:38 AM »
The selection is not based on potential energy, it is based on displacement distance. I want to calculate the number of atoms displaced by more than 0.8 Angstroms. I want to loop through each dump file and output the total number of atoms displaced by 0.8 A. But each file has many timesteps. So I should have many txt files output by this (defects_002.txt, defects_005.txt, etc). And each of these files has a column of data. Each row is the number of displacements at that timestep.

That is what I am trying to do. The code I posted up above (the reply, not the original post, does this for vacancies using WS defect analysis. How can I do the same, but instead of vacancies, just using my compute to select atoms?

Thanks for the help

Constanze Kalcher

  • Administrator
  • Full Member
  • *****
  • Posts: 121
Re: Python Script Loop
« Reply #7 on: September 29, 2018, 09:55:38 AM »
Hi jhart,
Quote
it is based on displacement distance
yes you're right, my mistake.

Quote
I want to calculate the number of atoms displaced by more than 0.8 Angstroms.
Then you could count them and save them as global attribute -  basically what you already did in your previous script.

I added this as line 5 to your script below:

Code: [Select]
for file in sorted(glob.glob('../*.dump')):
    node = import_file(file, multiple_frames=True)
    node.modifiers.append(SelectExpressionModifier(expression = 'c_disp_4_>0.8'))
    data = node.compute()
    node.output.attributes['n_atoms'] = np.count_nonzero(data.particle_properties["Selection"].array)
    export_file(
node, "natoms_{}.txt".format(file[-10:-7]), "txt",
columns=['Timestep', 'n_atoms'],
multiple_frames=True
    )

The Select expression modifier, however, already creates a global attribute 'SelectExpression.num_selected',
(see scripting manual of the Select expression modifier). So you could also just reference that:
Code: [Select]
for file in sorted(glob.glob('../*.dump')):
    node = import_file(file, multiple_frames=True)
    node.modifiers.append(SelectExpressionModifier(expression = 'c_disp_4_>0.8'))
    node.compute()
    export_file(
node, "natoms_{}.txt".format(file[-10:-7]), "txt",
columns=['Timestep', 'SelectExpression.num_selected'],
multiple_frames=True
    )

Does that work for you?
-Constanze
« Last Edit: September 29, 2018, 10:05:09 AM by Constanze Kalcher »