2121from ase .parallel import paropen
2222import ase .db
2323import ase .build
24+ from ase .constraints import FixAtoms
2425from pymatgen .core import Structure , Lattice
2526from pymatgen .io .ase import AseAtomsAdaptor
2627
@@ -194,19 +195,22 @@ def write_atoms(
194195 if fmt in ['extxyz' ]:
195196 if kwargs .get ('write_info' , False ):
196197 write_info = kwargs .pop ('write_info' )
197- # if kwargs.get('columns', False):
198- # columns = kwargs.pop('columns')
199- # else:
200- # reserved_ks = ['symbols', 'positions', 'numbers', 'species', 'pos']
201- # columns = ['symbols', 'positions'] + kwargs.get(
202- # 'columns',
203- # [k for k in atoms.arrays.keys() if k not in reserved_ks]
204- # )
205- # if columns_append is not None:
206- # columns += columns_append
207-
208- # if len(atoms.constraints) > 0:
209- # columns.append('move_mask')
198+
199+ reserved_ks = ['symbols' , 'positions' , 'numbers' , 'species' , 'pos' ]
200+ columns = ['symbols' , 'positions' ] + [
201+ k for k in atoms .arrays .keys () if k not in reserved_ks
202+ ]
203+
204+ if len (atoms .constraints ) > 0 :
205+ columns .append ('move_mask' )
206+
207+ if images [0 ].calc is not None :
208+ if 'forces' in images [0 ].calc .results :
209+ columns .append ('forces' )
210+
211+ user_columns = kwargs .get ('columns' , None )
212+ if user_columns is not None :
213+ columns = list (set (columns + user_columns ))
210214
211215 for atoms in images :
212216 # Current workaround magmoms being NaNs is to replace NaNs with 0.0
@@ -245,6 +249,7 @@ def get_atoms(
245249 mp_id : Optional [str ] = None ,
246250 user_api_key : Optional [str ] = None ,
247251 return_type : str = 'ase' ,
252+ constraints : Sequence [dict ] = None ,
248253 ** kwargs
249254) -> Union [Atoms , Structure ]:
250255 """Return an ASE Atoms or pymatgen Structure object based on specified
@@ -272,6 +277,9 @@ def get_atoms(
272277 :param user_api_key: Material Project API key, must be provided to get
273278 structures from Materials Project, defaults to None
274279 :type user_api_key: str, optional
280+ :param constraints: List of constraints to apply to the atoms object,
281+ currently only supports FixAtoms constraints, defaults to None
282+ :type constraints: Sequence[dict], optional
275283 :param return_type: When set to `ase` returns a :class:`ase.Atoms` object,
276284 when set to `pymatgen` returns a
277285 :class:`pymatgen.core.structure.Structure` object, defaults to 'ase'
@@ -298,8 +306,13 @@ def get_atoms(
298306 Atoms(symbols='Cu', pbc=True, cell=[[0.0, 1.805, 1.805], [1.805, 0.0, 1.805], [1.805, 1.805, 0.0]])
299307 >>> get_atoms(builder='bulk', name='Ar', crystalstructure='fcc', a=3.4, cubic=True)
300308 Atoms(symbols='Ar4', pbc=True, cell=[3.4, 3.4, 3.4])
301- >>> get_atoms(builder='fcc100', symbol='Fe', vacuum=8, size=[4,4, 5])
309+ >>> get_atoms(builder='fcc100', symbol='Fe', vacuum=8, size=[4,4,5])
302310 Atoms(symbols='Cu80', pbc=[True, True, False], cell=[10.210621920333747, 10.210621920333747, 23.22], tags=...)
311+
312+ You can also specify constraints to fix atoms in place, for example
313+ >>> get_atoms(builder='fcc100', symbol='Fe', vacuum=8, size=[4,4,5], constraints=[{'constraint': 'FixAtoms', 'indices': [0,1]}])
314+ Atoms(symbols='Cu80', pbc=[True, True, False], cell=[10.210621920333747, 10.210621920333747, 23.22], tags=...)
315+
303316
304317 Some examples for reading an image from a file using :func:`ase.io.read`
305318 are given below. All ``**kwargs`` are passed to :func:`ase.io.read`
@@ -426,6 +439,19 @@ def get_atoms(
426439 elif rattle_stdev is not None and interface == 'pymatgen' :
427440 struct .perturb (distance = rattle_stdev , min_distance = 0 )
428441
442+ if constraints is not None and interface == 'ase' :
443+ consts = []
444+ for constraint_args in constraints :
445+ name = constraint_args .pop ('constraint' )
446+ assert name == 'FixAtoms' , \
447+ 'Only FixAtoms constraints are supported for ASE interface'
448+ constraint_cls = getattr (ase .constraints , name , None )
449+ const = constraint_cls (** constraint_args )
450+ consts .append (const )
451+
452+ for const in consts :
453+ atoms .set_constraint (const )
454+
429455 if return_type == 'ase' and interface == 'ase' :
430456 return atoms
431457 elif return_type == 'pymatgen' and interface == 'ase' :
0 commit comments