#
# The high-throughput toolkit (httk)
# Copyright (C) 2012-2015 Rickard Armiento
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os, hashlib, random, string, re
from collections import OrderedDict
import httk
import httk.httkio
from httk.atomistic.data import periodictable, spacegroups
from httk.core import *
from httk.atomistic import Structure, Spacegroup
[docs]def cif_to_struct(ioa, backends=['internal', 'cif2cell', 'ase', 'platon']):
for backend in backends:
#print("CIF TO STRUCT BACKEND",backend)
if backend == 'internal':
try:
cifdata, cifheader = httk.httkio.read_cif(ioa)
return cifdata_to_struct(cifdata)
except ImportError:
pass
if backend == 'ase':
try:
from httk.external import ase_ext
return ase_ext.ase_read(ioa)
except ImportError:
pass
if backend == 'platon':
try:
from httk.external import platon_ext
sgstruct = platon_ext.cif_to_sgstructure(ioa)
return sgstruct.to_structure()
#return platon_if.cif_to_structure(ioa)
except ImportError:
pass
if backend == 'cif2cell':
try:
from httk.external import cif2cell_ext
return cif2cell_ext.cif_to_structure_reduce(ioa)
except ImportError:
pass
if backend == 'cif2cell_reduce':
try:
from httk.external import cif2cell_ext
return cif2cell_ext.cif_to_structure_reduce(ioa)
except ImportError:
pass
if backend == 'cif2cell_noreduce':
try:
from httk.external import cif2cell_ext
return cif2cell_ext.cif_to_structure_noreduce(ioa)
except ImportError:
pass
if backend == 'cif_reader_that_can_only_read_isotropy_cif':
try:
return cif_reader_that_can_only_read_isotropy_cif(ioa)
except ImportError:
pass
if backend == 'cif_reader_httk_preprocessed':
try:
return cif_reader_httk_preprocessed(ioa)
except ImportError:
pass
raise Exception("cif_to_struct: None of the requested / available backends available, tried:"+str(backends))
[docs]def struct_to_cif(struct, ioa, backends=['httk']):
for backend in backends:
if backend == 'httk':
cifdata = struct_to_cifdata(struct)
header = "# cif file generated by httk"
httk.httkio.cif.write_cif(ioa, cifdata, header)
return
#return struct_to_cif_httk(struct, ioa)
if backend == 'ase':
try:
from httk.external import ase_glue
return ase_glue.ase_write_struct(struct, ioa, 'cif')
except ImportError:
pass
raise Exception("struct_to_cif: None of the requested / available backends available, tried:"+str(backends))
[docs]def struct_to_cif_httk_simplified(struct, ioa, header=None, symops=True):
ioa = IoAdapterFileWriter.use(ioa)
f = ioa.file
wyckoffsymbols = None
if struct.has_rc_repr:
la = struct.rc_lengths_and_angles
coordgroups = struct.rc_sites.reduced_coordgroups
hall = struct.hall_symbol
sgnumber = struct.spacegroup.number
try:
hmsymbol = spacegroups.get_proper_hm_symbol(hall)
except Exception:
hmsymbol = None
multiplicities = struct.rc_sites.multiplicities
wyckoffsymbols = struct.rc_sites.wyckoff_symbols
else:
la = struct.uc_lengths_and_angles
coordgroups = struct.uc_sites.reduced_coordgroups
hall = 'P 1'
sgnumber = 1
hmsymbol = 'P 1'
if header is not None:
f.write(header)
f.write("\n")
f.write("data_image0\n")
f.write("\n")
try:
if struct.get_tag('name') is not None:
f.write("_chemical_name_systematic '"+struct.get_tag('name').value+"'\n")
except Exception:
pass
#if struct.has_uc_repr:
f.write("_chemical_formula_sum '"+struct.formula_spaceseparated+"'\n")
f.write("\n")
f.write("_cell_length_a "+(la[0].to_string())+"\n")
f.write("_cell_length_b "+(la[1].to_string())+"\n")
f.write("_cell_length_c "+(la[2].to_string())+"\n")
f.write("_cell_angle_alpha "+(la[3].to_string())+"\n")
f.write("_cell_angle_beta "+(la[4].to_string())+"\n")
f.write("_cell_angle_gamma "+(la[5].to_string())+"\n")
f.write("\n")
f.write("_symmetry_space_group_name_Hall '"+str(hall)+"'\n")
if hmsymbol is not None:
f.write("_symmetry_space_group_name_H-M '"+str(hmsymbol)+"'\n")
f.write("_symmetry_Int_Tables_number "+str(sgnumber)+"\n")
f.write("\n")
f.write("loop_\n")
f.write("_atom_site_label\n")
f.write("_atom_site_type_symbol\n")
if wyckoffsymbols is not None:
f.write("_atom_site_symmetry_multiplicity\n")
f.write("_atom_site_Wyckoff_symbol\n")
f.write("_atom_site_fract_x\n")
f.write("_atom_site_fract_y\n")
f.write("_atom_site_fract_z\n")
f.write("_atom_site_occupancy\n")
seen = {}
for i, cg in enumerate(coordgroups):
for coord in cg:
x = coord[0]
y = coord[1]
z = coord[2]
symbols = struct.assignments.symbollists[i]
ratios = struct.assignments.ratioslist[i]
for occ in range(len(symbols)):
symbol = symbols[occ]
ratio = ratios[occ]
if symbol in seen:
seen[symbol] += 1
idx = seen[symbol]
else:
seen[symbol] = 1
idx = 1
label = symbol+str(idx)
if wyckoffsymbols is not None:
multiplicity = multiplicities[i]
wyckoffsymbol = wyckoffsymbols[i]
f.write("%s %s %d %s %.14f %.14f %.14f %.14f\n" % (label, symbol, multiplicity, wyckoffsymbol, float(x), float(y), float(z), float(ratio)))
else:
f.write("%s %s %.14f %.14f %.14f %.14f\n" % (label, symbol, float(x), float(y), float(z), float(ratio)))
ioa.close()
[docs]def cif_reader_that_can_only_read_isotropy_cif(ioa):
def cell_length(results, match):
results['length_'+match.group(1)] = FracVector.create(match.group(2))
def cell_angle(results, match):
results['angle_'+match.group(1)] = FracVector.create(match.group(2))
def print_hm_and_hall(results):
grpnbr = results['grpnbr']
setting = results['setting']
hmsymb = results['hmfull']
hallsymb = spacegroups.spacegroup_get_hall(str(grpnbr)+":"+setting)
results['hall_symbol'] = hallsymb
def hm_symbol_origin(results, match):
results['out'] = True
results['hmfull'] = match.group(1)
results['hm'] = match.group(2)
if match.group(3) == 'hexagonal axes':
results['setting'] = '1'
else:
results['setting'] = str(match.group(3))
if 'hm' in results and 'grpnbr' in results:
print_hm_and_hall(results)
def hm_symbol_no_origin(results, match):
results['out'] = True
results['hmfull'] = match.group(1)
results['hm'] = match.group(1)
results['setting'] = "1"
if 'hm' in results and 'grpnbr' in results:
print_hm_and_hall(results)
def groupnbr(results, match):
results['grpnbr'] = match.group(1)
if 'hm' in results and 'grpnbr' in results:
print_hm_and_hall(results)
def coords(results, match):
newcoord = httk.FracVector.create([match.group(5), match.group(6), match.group(7)])
occup = {'atom': periodictable.atomic_number(match.group(2)), 'ratio': FracVector.create(match.group(8)), }
if match.group(4) == 'alpha':
wyckoff = '&'
else:
wyckoff = match.group(4)
multiplicities = int(match.group(3))
if newcoord in results['seen_coords']:
idx = results['seen_coords'][newcoord]
results['occups'][idx].append(occup)
else:
results['seen_coords'][newcoord] = results['idx']
results['coords'].append(newcoord)
results['occups'].append([occup])
results['wyckoff'].append(wyckoff)
results['multiplicities'].append(multiplicities)
results['idx'] += 1
results = {'idx': 0, 'occups': [], 'wyckoff': [], 'multiplicities': [], 'coords': [], 'seen_coords': {}}
httk.basic.micro_pyawk(ioa, [
['^_cell_length_([^ ]*) (.*) *$', None, cell_length],
['^_cell_angle_([^ ]*) (.*) *$', None, cell_angle],
['^_symmetry_Int_Tables_number +(.*)$', None, groupnbr],
['^_symmetry_space_group_name_H-M +"(([^()]+) \(origin choice ([0-9]+)\))" *$', None, hm_symbol_origin],
['^_symmetry_space_group_name_H-M +"(([^()]+) \((hexagonal axes)\))" *$', None, hm_symbol_origin],
['^_symmetry_space_group_name_H-M +"([^()]+)" *$', None, hm_symbol_no_origin],
['^ *([^ ]+) +([^ ]+) +([^ ]+) +([^ ]+) +([0-9.-]+) +([0-9.-]+) +([0-9.-]+) +([0-9.-]+) *$', None, coords],
], debug=False, results=results)
struct = Structure.create(rc_a=results['length_a'], rc_b=results['length_b'], rc_c=results['length_c'],
rc_alpha=results['angle_alpha'], rc_beta=results['angle_beta'], rc_gamma=results['angle_gamma'],
rc_reduced_occupationscoords=results['coords'], rc_occupancies=results['occups'],
spacegroup=results['hall_symbol'], wyckoff_symbols=results['wyckoff'], multiplicities=results['multiplicities'])
return struct
[docs]def cif_reader_httk_preprocessed(ioa):
ioa = IoAdapterStringList.use(ioa)
for i in range(len(ioa.stringlist)):
if ioa.stringlist[i].startswith("INPUT"):
ioa.stringlist[i] = ""
newstruct = cif_to_struct(ioa, backends=['cif2cell_reduce'])
for i in range(len(ioa.stringlist)):
if ioa.stringlist[i].startswith("# Data extracted using the FINDSYM utility follows"):
ioa.stringlist = ioa.stringlist[i:]
break
only_rc_struct = cif_to_struct(ioa, backends=['cif_reader_that_can_only_read_isotropy_cif'])
if only_rc_struct.assignments.hexhash != newstruct.assignments.hexhash:
# This happens IF the rc representation is broken due to the use of
# different but equivalent sites. In this case we have already lost, and need
# to return only the "proper" structure in newstruct. This will cause us to loose
# Wyckoff information, but for now I see now way of rescuing this.
# This will all be solved when we implement our own cif reader.
return newstruct
newstruct.rc_sites.wyckoff_symbols = only_rc_struct.rc_sites.wyckoff_symbols
# Cell basis can only be constructed from the cif approximately
only_rc_struct._rc_cell = newstruct._rc_cell
# Make sure the hexhash is recomputed
only_rc_struct.rc_sites._hexhash = None
#print("CHECK THIS:", newstruct.rc_sites.hexhash, only_rc_struct.rc_sites.hexhash)
#print("CHECK THIS:", newstruct.cellobj.to_tuple(), only_rc_struct.cellobj.to_tuple())
if newstruct.rc_sites.hexhash != only_rc_struct.rc_sites.hexhash:
#print("Cell mismatch:",cell_mismatch)
print("Structure hashes:", newstruct.rc_sites.hexhash, only_rc_struct.rc_sites.hexhash)
#print("Structures:", newstruct.rc_sites.to_tuple(), only_rc_struct.rc_sites.to_tuple())
raise Exception("isotropy_ext.struct_process_with_isotropy: internal error, structures that absolutely should be the same are not, sorry.")
return newstruct
[docs]def cifdata_to_struct(cifdata, debug=False):
if len(cifdata) > 1:
raise Exception("httk.atomistic.atomisticio.structure_cif_io: cifdata to struct with more than one image in cifdata.")
element = cifdata[0][1]
if debug:
import pprint
pp = pprint.PrettyPrinter()
debugout = dict(element)
if 'symmetry_equiv_pos_as_xyz' in debugout:
del debugout['symmetry_equiv_pos_as_xyz']
if 'space_group_symop_operation_xyz' in debugout:
del debugout['space_group_symop_operation_xyz']
if 'space_group_symop_id' in debugout:
del debugout['space_group_symop_id']
pp.pprint(debugout)
rc_lengths = FracVector.create([element['cell_length_a'], element['cell_length_b'], element['cell_length_c']])
rc_cosangles = FracVector.create_cos([element['cell_angle_alpha'], element['cell_angle_beta'], element['cell_angle_gamma']],degrees=True)
hall_symbol = None
hm_symbol = None
spacegroupnumber = None
setting = None
symops = None
if 'symmetry_space_group_name_hall' in element:
hall_symbol = element['symmetry_space_group_name_hall']
if 'space_group_symop_operation_xyz' in element:
symops = element['space_group_symop_operation_xyz']
elif 'symmetry_equiv_pos_as_xyz' in element:
symops = element['symmetry_equiv_pos_as_xyz']
if 'symmetry_space_group_name_h-m' in element:
hm_symbol = element['symmetry_space_group_name_h-m']
if 'symmetry_Int_Tables_number' in element:
spacegroupnumber = int(element['symmetry_Int_Tables_number'])
# cif verb symmetry_cell_setting really is not defined as it would seems logical. Due to ensuing confusion, we better simply ignore it
#if 'symmetry_cell_setting' in element:
# setting = element['symmetry_cell_setting']
if hall_symbol is None and symops is None and hm_symbol is None and spacegroupnumber is None:
raise Exception("No symmetry information given in cif file, impossible to interpret coordinate data!")
spacegroup = Spacegroup.create(hall_symbol=hall_symbol, hm_symbol=hm_symbol, spacegroupnumber=spacegroupnumber, setting=setting, symops=symops)
rc_occupancies = []
rc_reduced_occupationscoords = []
wyckoff_symbols = None
multiplicities = None
if 'atom_site_wyckoff_symbol' in element:
wyckoff_symbols = element['atom_site_wyckoff_symbol']
elif 'atom_site_wyckoff_label' in element:
wyckoff_symbols = element['atom_site_wyckoff_label']
if 'atom_site_symmetry_multiplicity' in element:
multiplicities = [int(x) for x in element['atom_site_symmetry_multiplicity']]
for atom in range(len(element['atom_site_label'])):
if 'atom_site_occupancy' in element:
ratio = element['atom_site_occupancy'][atom]
else:
ratio = 1
symbol = str(re.match('[A-Z][a-z]?',element['atom_site_label'][atom]).group(0))
occup = {'atom': periodictable.atomic_number(symbol), 'ratio': FracVector.create(ratio), }
coord = [element['atom_site_fract_x'][atom], element['atom_site_fract_y'][atom], element['atom_site_fract_z'][atom]]
rc_occupancies += [occup]
rc_reduced_occupationscoords += [coord]
#print("X",rc_lengths,rc_angles,rc_reduced_occupationscoords,rc_occupancies,spacegroup,setting,wyckoff_symbols,multiplicities)
tags = {}
if 'chemical_name_common' in element:
if is_sequence(element['chemical_name_common']):
tags['name'] = element['chemical_name_common'][0]
tags['names'] = ",".join(element['chemical_name_common'])
else:
tags['name'] = element['chemical_name_common']
elif 'chemical_name_systematic' in element:
if is_sequence(element['chemical_name_systematic']):
tags['name'] = element['chemical_name_systematic'][0]
tags['names'] = ",".join(element['chemical_name_systematic'])
else:
tags['name'] = element['chemical_name_systematic']
authorlist = None
if 'publ_author_name' in element:
authorlist = []
authors = element['publ_author_name']
if not basic.is_sequence(authors):
authors = [authors]
for author in authors:
authorparts = author.partition(",")
lastname = authorparts[0].strip()
givennames = authorparts[2].strip()
authorlist += [Author.create(lastname, givennames)]
# I didn't find this in the spec, on the other hand, the publ_ space is "not specified", so, this seems
# as a logical extension of the author list in case of a cif file that has been published in a book.
# I see no harm in including it, at least.
editorlist = None
if 'publ_editor_name' in element:
editorlist = []
editors = element['publ_editor_name']
if not basic.is_sequence(editors):
editors = [editors]
for editor in editors:
editorparts = editor.partition(",")
lastname = editorparts[0].strip()
givennames = editorparts[2].strip()
editorlist += [Author.create(lastname, givennames)]
refs = None
if 'journal_name_full' in element or 'journal_name_abbrev' in element:
journal = None
journal_page_first = None
journal_page_last = None
journal_volume = None
journal_title = None
journal_book_title = None
journal_year = None
journal_issue = None
journal_publisher = None
journal_publisher_city = None
if 'journal_name_abbrev' in element:
journal = element['journal_name_abbrev']
if 'journal_name_full' in element:
journal = element['journal_name_full']
if 'journal_page_first' in element:
journal_page_first = element['journal_page_first']
if 'journal_page_last' in element:
journal_page_last = element['journal_page_last']
if 'journal_volume' in element:
journal_volume = element['journal_volume']
if 'journal_issue' in element:
journal_issue = element['journal_issue']
if 'journal_title' in element:
journal_title = element['journal_title']
if 'journal_book_title' in element:
journal_book_title = element['journal_book_title']
if 'journal_year' in element:
journal_year = element['journal_year']
if 'journal_publisher' in element:
journal_publisher = element['journal_publisher']
if 'journal_publisher_city' in element:
journal_publisher_city = element['journal_publisher_city']
if 'journal_book_publisher' in element:
journal_publisher = element['journal_book_publisher']
if 'journal_book_publisher_city' in element:
journal_publisher_city = element['journal_book_publisher_city']
refs = [Reference.create(authors=authorlist, editors=editorlist, journal=journal, journal_issue=journal_issue, journal_volume=journal_volume,
page_first=journal_page_first, page_last=journal_page_last, title=journal_title, year=journal_year, book_publisher=journal_publisher,
book_publisher_city=journal_publisher_city, book_title=journal_book_title)]
# This is based on some assumptions... IF a journal_* type tree exists, then we assume this is a 'published' cif, and
# in that case the only reference we want to keep is the one to the published work. Citations in the citation_* tree is going to be
# all the citations from that paper, which we do not want, *unless* they mark citation_coordinate_linkage. However,
# if no journal_* tree exists, then this is a 'generic' structure cif, where the citations should point to all publications of
# this structure; and we don't want to assume 'citation_coordinate_linkage'
add_all_citations = False
if refs is None or len(refs) == 0:
add_all_citations = True
if 'citation_journal_full' in element or 'citation_journal_abbrev' in element or 'citation_book_title' in element:
if refs is None:
refs = []
if 'citation_journal_full' in element:
N = len(element['citation_journal_full'])
elif 'citation_journal_abbrev' in element:
N = len(element['citation_journal_abbrev'])
elif 'citation_book_title' in element:
N = len(element['citation_book_title'])
for i in range(N):
if not add_all_citations and ('citation_coordinate_linkage' not in element or (element['citation_coordinate_linkage'][i].lower() != 'yes' and element['citation_coordinate_linkage'][i].lower() != 'y')):
continue
journal = None
journal_page_first = None
journal_page_last = None
journal_volume = None
journal_title = None
journal_book_title = None
journal_issue = None
journal_publisher = None
journal_publisher_city = None
if 'citation_journal_full' in element:
journal = element['citation_journal_full'][i]
if 'citation_journal_abbrev' in element:
journal = element['citation_journal_abbrev'][i]
if 'citation_page_first' in element:
journal_page_first = element['citation_page_first'][i]
if 'citation_page_last' in element:
journal_page_last = element['citation_page_last'][i]
if 'citation_journal_volume' in element:
journal_volume = element['citation_journal_volume'][i]
if 'citation_journal_issue' in element:
journal_issue = element['citation_journal_issue'][i]
if 'citation_title' in element:
journal_title = element['citation_title'][i]
if 'citation_book_title' in element:
journal_book_title = element['citation_book_title'][i]
if 'journal_year' in element:
journal_year = element['journal_year'][i]
if 'citation_book_publisher' in element:
journal_publisher = element['citation_book_publisher'][i]
if 'citation_book_publisher_city' in element:
journal_publisher_city = element['citation_book_publisher_city'][i]
refs += [Reference.create(authors=authorlist, editors=editorlist, journal=journal, journal_issue=journal_issue, journal_volume=journal_volume,
page_first=journal_page_first, page_last=journal_page_last, title=journal_title, year=journal_year, book_publisher=journal_publisher,
book_publisher_city=journal_publisher_city, book_title=journal_book_title)]
struct = Structure.create(rc_lengths=rc_lengths,
rc_cosangles=rc_cosangles,
rc_reduced_occupationscoords=rc_reduced_occupationscoords,
rc_occupancies=rc_occupancies,
spacegroup=spacegroup, setting=setting,
periodicity=0,
wyckoff_symbols=wyckoff_symbols, multiplicities=multiplicities, tags=tags, refs=refs)
return struct
[docs]def struct_to_cifdata(struct, entryid=None):
entry = OrderedDict()
wyckoffsymbols = None
if struct.has_rc_repr:
la = struct.rc_lengths_and_angles
coordgroups = struct.rc_sites.reduced_coordgroups
hall = struct.hall_symbol
sgnumber = struct.spacegroup_number
try:
hmsymbol = spacegroups.get_proper_hm_symbol(hall)
except Exception:
hmsymbol = None
multiplicities = struct.rc_sites.multiplicities
wyckoffsymbols = struct.rc_sites.wyckoff_symbols
else:
la = struct.uc_lengths_and_angles
coordgroups = struct.uc_sites.reduced_coordgroups
hall = 'P 1'
sgnumber = 1
hmsymbol = 'P 1'
try:
if struct.get_tag('name') is not None:
entry["chemical_name_common"] = struct.get_tag('name').value
except Exception:
pass
if struct.has_uc_repr:
entry["chemical_formula_sum"] = struct.formula_spaceseparated
entry["cell_length_a"] = la[0].to_string()
entry["cell_length_b"] = la[1].to_string()
entry["cell_length_c"] = la[2].to_string()
entry["cell_angle_alpha"] = la[3].to_string()
entry["cell_angle_beta"] = la[4].to_string()
entry["cell_angle_gamma"] = la[5].to_string()
entry["symmetry_space_group_name_hall"] = str(hall)
if hmsymbol is not None:
entry["symmetry_space_group_name_h-m"] = str(hmsymbol)
entry["symmetry_Int_Tables_number"] = str(sgnumber)
# Add setting
entry["symmetry_space_group_name_hall"] = str(hall)
refs = struct.get_refs()
if len(refs) > 0:
entry["loop_1"] = ["citation_id",
"citation_journal_full", "citation_journal_issue", "citation_journal_volume",
"citation_page_first", "citation_page_last",
"citation_title", "citation_year", "citation_book_publisher",
"citation_book_publisher_city", "citation_book_title", "citation_coordinate_linkage"]
entry["loop_2"] = ["citation_author_citation_id", "citation_author_name"]
entry["loop_3"] = ["citation_editor_citation_id", "citation_editor_name"]
for key in entry["loop_1"] + entry['loop_2'] + entry['loop_3']:
entry[key] = []
for i in range(len(refs)):
data = {}
ref = refs[i].reference
data['citation_id'] = str(i)
data["citation_journal_full"] = ref.journal
data['citation_journal_issue'] = ref.journal_issue
data['citation_journal_volume'] = ref.journal_volume
data['citation_page_first'] = ref.page_first
data['citation_page_last'] = ref.page_last
data['citation_title'] = ref.title
data['citation_year'] = ref.year
data['citation_book_publisher'] = ref.book_publisher
data['citation_book_publisher_city'] = ref.book_publisher_city
data['citation_book_title'] = ref.book_title
data['citation_coordinate_linkage'] = "yes"
for key in entry["loop_1"]:
entry[key] += [data[key]]
if ref.authors is not None:
for author in ref.authors:
entry['citation_author_citation_id'] += [str(i)]
entry['citation_author_name'] += [author.last_name + ", "+author.given_names]
if ref.editors is not None:
for editor in ref.editors:
entry['citation_editor_citation_id'] += [str(i)]
entry['citation_author_name'] += [editor.last_name + ", "+editor.given_names]
if 'citation_author_citation_id' in entry and len(entry['citation_author_citation_id']) == 0:
del entry['citation_author_citation_id']
del entry['citation_author_name']
del entry['loop_2']
if 'citation_editor_citation_id' in entry and len(entry['citation_editor_citation_id']) == 0:
del entry['citation_editor_citation_id']
del entry['citation_editor_name']
del entry['loop_3']
entry["loop_0"] = ["atom_site_label", "atom_site_type_symbol"]
if wyckoffsymbols is not None:
entry["loop_0"] += ["atom_site_symmetry_multiplicity", "atom_site_Wyckoff_symbol"]
entry["loop_0"] += ["atom_site_fract_x", "atom_site_fract_y", "atom_site_fract_z", "atom_site_occupancy"]
for key in entry["loop_0"]:
entry[key] = []
seen = {}
for i, cg in enumerate(coordgroups):
for coord in cg:
x = coord[0].to_string()
y = coord[1].to_string()
z = coord[2].to_string()
symbols = struct.assignments.symbollists[i]
ratios = struct.assignments.ratioslist[i]
for occ in range(len(symbols)):
symbol = symbols[occ]
ratio = ratios[occ]
if symbol in seen:
seen[symbol] += 1
idx = seen[symbol]
else:
seen[symbol] = 1
idx = 1
label = symbol+str(idx)
if wyckoffsymbols is not None:
multiplicity = multiplicities[i]
wyckoffsymbol = wyckoffsymbols[i]
data = (label, symbol, multiplicity, wyckoffsymbol, x, y, z, ratio.to_string())
else:
data = (label, symbol, x, y, z, ratio.to_string())
for j in range(len(entry["loop_0"])):
entry[entry["loop_0"][j]] += [data[j]]
if entryid is None:
entryid = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(10))
cifdata = [(entryid, entry)]
return cifdata