From 17ec964f9ed0ab3d0fc410909611bbb43487f1a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Tcho=C5=84?= Date: Wed, 18 Jun 2025 18:08:07 +0200 Subject: [PATCH 1/2] :white_check_mark: Reading from cif should work even if not all atoms are in `aniso` table --- tests/ferrocene2.cif | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tests/ferrocene2.cif b/tests/ferrocene2.cif index aa55d89..6700503 100644 --- a/tests/ferrocene2.cif +++ b/tests/ferrocene2.cif @@ -77,8 +77,3 @@ C(12) .02 .02 .02 .0 .0 .0 C(13) .02 .02 .02 .0 .0 .0 C(14) .02 .02 .02 .0 .0 .0 C(15) .02 .02 .02 .0 .0 .0 -H(11) .03 .03 .03 .0 .0 .0 -H(12) .03 .03 .03 .0 .0 .0 -H(13) .03 .03 .03 .0 .0 .0 -H(14) .03 .03 .03 .0 .0 .0 -H(15) .03 .03 .03 .0 .0 .0 From bf2936864ebb7849d3b5bce3f711e3678b76c691 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Tcho=C5=84?= Date: Wed, 18 Jun 2025 18:12:26 +0200 Subject: [PATCH 2/2] :bug: Allow reading ADPs if they are present only for some of the atoms --- picometer/atom.py | 51 +++++++++++++++++++++++--------------------- picometer/utility.py | 8 +++++++ 2 files changed, 35 insertions(+), 24 deletions(-) diff --git a/picometer/atom.py b/picometer/atom.py index 60d514b..4eaee9a 100644 --- a/picometer/atom.py +++ b/picometer/atom.py @@ -9,7 +9,7 @@ from picometer.shapes import (are_synparallel, degrees_between, Line, Plane, Shape, Vector3) -from picometer.utility import ustr2float +from picometer.utility import ustr2float, ustr2floats try: @@ -83,29 +83,32 @@ def from_cif(cls, cif_path: str, block_name: str = None) -> 'AtomSet': al=ustr2float(cb['_cell_angle_alpha']), be=ustr2float(cb['_cell_angle_beta']), ga=ustr2float(cb['_cell_angle_gamma'])) - try: - atoms_dict = { - 'label': cb['_atom_site_label'], - 'fract_x': [ustr2float(v) for v in cb['_atom_site_fract_x']], - 'fract_y': [ustr2float(v) for v in cb['_atom_site_fract_y']], - 'fract_z': [ustr2float(v) for v in cb['_atom_site_fract_z']], - } - try: - atoms_dict['Uiso'] = [ustr2float(v) for v in cb['_atom_site_U_iso_or_equiv']] - except KeyError: - pass - try: - atoms_dict['U11'] = [ustr2float(v) for v in cb['_atom_site_aniso_U_11']] - atoms_dict['U22'] = [ustr2float(v) for v in cb['_atom_site_aniso_U_22']] - atoms_dict['U33'] = [ustr2float(v) for v in cb['_atom_site_aniso_U_33']] - atoms_dict['U23'] = [ustr2float(v) for v in cb['_atom_site_aniso_U_23']] - atoms_dict['U13'] = [ustr2float(v) for v in cb['_atom_site_aniso_U_13']] - atoms_dict['U12'] = [ustr2float(v) for v in cb['_atom_site_aniso_U_12']] - except KeyError: - pass - atoms = pd.DataFrame.from_records(atoms_dict).set_index('label') - except KeyError: - atoms = pd.DataFrame() + + atoms = pd.DataFrame() + + atom_labels = cb.get('_atom_site_label', []) + atom_xs = ustr2floats(cb.get('_atom_site_fract_x', [])) + atom_ys = ustr2floats(cb.get('_atom_site_fract_y', [])) + atom_zs = ustr2floats(cb.get('_atom_site_fract_z', [])) + atom_u_isos = ustr2floats(cb.get('_atom_site_U_iso_or_equiv', [])) + for label, x, y, z in zip(atom_labels, atom_xs, atom_ys, atom_zs): + atoms.loc[label, ['fract_x', 'fract_y', 'fract_z']] = [x, y, z] + for label, u_iso in zip(atom_labels, atom_u_isos): + atoms.loc[label, 'Uiso'] = u_iso + + atom_labels = cb.get('_atom_site_aniso_label', []) + atom_u11s = ustr2floats(cb.get('_atom_site_aniso_U_11', [])) + atom_u22s = ustr2floats(cb.get('_atom_site_aniso_U_22', [])) + atom_u33s = ustr2floats(cb.get('_atom_site_aniso_U_33', [])) + atom_u12s = ustr2floats(cb.get('_atom_site_aniso_U_12', [])) + atom_u13s = ustr2floats(cb.get('_atom_site_aniso_U_13', [])) + atom_u23s = ustr2floats(cb.get('_atom_site_aniso_U_23', [])) + atom_us = zip(atom_u11s, atom_u22s, atom_u33s, atom_u12s, atom_u13s, atom_u23s) + for label, us in zip(atom_labels, atom_us): + atoms.loc[label, ['U11', 'U22', 'U33', 'U12', 'U13', 'U23']] = list(us) + + for col in atoms.columns: + atoms[col] = pd.to_numeric(atoms[col], errors='coerce') return AtomSet(bf, atoms) @property diff --git a/picometer/utility.py b/picometer/utility.py index 951f579..0f75129 100644 --- a/picometer/utility.py +++ b/picometer/utility.py @@ -1,5 +1,13 @@ +from typing import Iterable, List + import uncertainties as uc def ustr2float(s: str) -> float: + """Convert a string "1.23(4)" to float `1.23`, stripping uncertainty.""" return uc.ufloat_fromstr(s).nominal_value + + +def ustr2floats(s: Iterable[str]) -> List[float]: + """Convenience function to convert an iterable of u-strings to floats.""" + return [ustr2float(s) for s in s]