Set up structure container for structures & forces calculated by DFT

Hello Hiphive users and developers,

I have done some reference calculations with DFT via Wien2k code. to this aim I have generated the displaced structures with hiphive and changed the supercells_rattled.extxyz to some .xyz files and again changed them to .struct file. the forces of each struct file are computed via DFT in Wien2k code. Now for further computational work, I have to set up structure container, to do this, I am using the commands below:

sc = StructureContainer(cs)
configurations=ase.io.read(supercells_rattled.extxyz)
for i, atoms in enumerate(configurations):
    sc.add_structure(atoms)
sc.write(path_to_structure_container_filename)

but I faced the error below:
‘Atom’ object has no attribute ‘copy’

could you please guide me to solve it?

ase.io.read('supercells_rattled.extxyz') returns an Atoms object, not a series of them. So the
for i, atoms ... loop will actually loop of the series of Atom inside that Atoms object.

Instead you should load the Atoms objects with
ase.io.read('supercells_rattled.extxyz', index=':') to specify that you want all of the structures in the file. (index defaults to -1, the last Atoms in the file. Very useful for geometry optimisation but not so helpful for this kind of thing!)

1 Like

Thank you, I applied the change you said but I got the following error:

ValueError: Atoms must have displacements array

I will send you the complete code. The problem may be in the previous lines:

import ase
import hiphive
from ase.io import write 
from ase.io import read
from ase.build import bulk
from ase.calculators.emt import EMT
from hiphive.structure_generation import generate_mc_rattled_structures
from ase import db 
from ase.db import connect  
from ase.calculators.singlepoint import SinglePointCalculator
from ase.io import read 
from ase.io.wien2k import read_struct
import numpy as np
from numpy import load , genfromtxt
from ase import db 
from hiphive import ClusterSpace, StructureContainer
from hiphive.utilities import get_displacements  

n_structures = 5
cell_size = 2
rattle_std = 0.03
minimum_distance = 2.3

prim = read(path_to_prim)
atoms_ideal = prim.repeat(cell_size)
database = db.connect('database.db')

for i in range(1, 5):
    filename = f'supercell-{i}.struct'
    atoms = read_struct(filename)
    
    # Load the npy file
    forces = np.load(f'forces-{i}.npy')

    atoms.calc = SinglePointCalculator(atoms, forces=forces)
    database.write(atoms, name=filename)

structures = []
for row in db.select():

  # Get forces and displacements.
  atoms = row.toatoms()
  displacements = get_displacements(atoms, atoms_ideal)
  forces = atoms.get_forces()

  # Sanity check, displacements should not be abnormally large
  # (here taken as 1.0 A)
  assert np.linalg.norm(displacements, axis=1).max() < 1.0

  # Finalize.
  # The structure container should see the ideal structure in
  # order to be able to process the symmetry.
  atoms_tmp = atoms_ideal.copy()
  # The displacements and forces are attached as separate arrays.
  atoms_tmp.new_array('displacements', displacements)
  atoms_tmp.new_array('forces', forces)
  structures.append(atoms_tmp)

cutoffs = [5.0, 4.0, 4.0]
# Set up cluster space.
cs = ClusterSpace(atoms_ideal, cutoffs)

sc = StructureContainer(cs)
configurations=ase.io.read('supercells_rattled.extxyz', index=':')
for i, atoms in enumerate(configurations):
    sc.add_structure(atoms)
sc.write(path_to_structure_container_filename)

It is helpful when reporting error messages to also include some of the stacktrace so people can see where it came from.

Here I assume it came from sc.add_structure(atoms) because the StructureContainer needs the displacement information to be attached to Atoms. In the tutorials this is mostly done using hiphive.utilities.prepare_structure.

In this script, it looks like you created a list of atoms “structures” that do have a displacements array — but then this list is never used. Instead, you grab a different list of atoms called “configurations” from the extxyz file that don’t have this data, and pass those to sc.add_structure() instead.

1 Like

yes. you are right. the error is came from sc.add_structure(atoms):

ValueError                                Traceback (most recent call last)
Cell In[15], line 10
      8 configurations=ase.io.read('supercells_rattled.extxyz', index=':')
      9 for i, atoms in enumerate(configurations):
---> 10     sc.add_structure(atoms)
     11 sc.write(path_to_structure_container_filename)

File ~\AppData\Roaming\Python\Python310\site-packages\hiphive\structure_container.py:166, in StructureContainer.add_structure(self, atoms, **meta_data)
    164 # atoms object must contain displacements
    165 if 'displacements' not in atoms_copy.arrays.keys():
--> 166     raise ValueError('Atoms must have displacements array')
    168 # atoms object must contain forces
    169 if 'forces' not in atoms_copy.arrays.keys():

ValueError: Atoms must have displacements array

actually I did not know what to set instead of configurations!?could you please tell me what should I set as configurations?

Some questions to consider:

  • what is the purpose of StructureContainer?
  • what information does the StructureContainer need in order to do its job?
  • what information is in ‘supercells_rattled.extxyz’?
  • why did you attach displacements and forces to “structures”, but not use this object any more?

I went through this script step by step as per the tutorial. I needed togenerate SC to continue the calculations, so I faced to the configurations statement. First, I put the structures instead, which I got the following error. Then I asked about it in https://gitlab.com/materials-modeling/hiphive/-/issues/568, they said that this configuration is actually a list of structures, and that’s why I replaced supercells_rattled.extxyz.

ValueError                                Traceback (most recent call last)
Cell In[7], line 10
      8 #configurations=ase.io.read('supercells_rattled.extxyz', index=':')
      9 for i, atoms in enumerate(structures):
---> 10     sc.add_structure(atoms)
     11 sc.write(path_to_structure_container_filename)

File ~\AppData\Roaming\Python\Python310\site-packages\hiphive\structure_container.py:178, in StructureContainer.add_structure(self, atoms, **meta_data)
    176 for i, structure in enumerate(self._structure_list):
    177     if are_configurations_equal(atoms_copy, structure.atoms):
--> 178         raise ValueError('Atoms is identical to structure {}'.format(i))
    180 logger.debug('Adding structure')
    181 M = self._compute_fit_matrix(atoms)

ValueError: Atoms is identical to structure 1

It looks like you now have the right idea to use the structures that include forces and displacements.

This error message says that you passed more than one identical Atoms to add_structure(). How did that happen? Is it possible that your database has some duplicate rows?