Skip to content
Prev Previous commit
Next Next commit
Move parameterless read methods to properties
  • Loading branch information
janbridley committed Jan 18, 2025
commit 3a6962a3c129427396e878e530b9cb97d4790158
125 changes: 64 additions & 61 deletions parsnip/parsnip.py
Original file line number Diff line number Diff line change
Expand Up @@ -389,39 +389,6 @@ def angle_is_invalid(x: float):

return tuple(float(v) for v in cell_data) # Return as base python types

def read_symmetry_operations(self):
r"""Extract the symmetry operations from a CIF file.

Returns
-------
:math:`(N,)` :class:`numpy.ndarray[str]`:
An array of strings containing the symmetry operations in a
`parsable algebraic form`_.

.. _`parsable algebraic form`: https://www.iucr.org/__data/iucr/cifdic_html/1/cif_core.dic/Ispace_group_symop_operation_xyz.html
"""
symmetry_keys = (
"_symmetry_equiv_pos_as_xyz",
"_space_group_symop_operation_xyz",
)

# Only one key is valid in each standard, so we only ever get one match.
return self.get_from_loops(symmetry_keys)

def read_wyckoff_positions(self):
r"""Extract symmetry-irreducible, fractional x,y,z coordinates from a CIF file.

Returns
-------
:math:`(N, 3)` :class:`numpy.ndarray[float]`:
Symmetry-irreducible positions of atoms in `fractional coordinates`_.

.. _`fractional coordinates`: https://www.iucr.org/__data/iucr/cifdic_html/1/cif_core.dic/Iatom_site_fract_.html
"""
xyz_keys = ("_atom_site_fract_x", "_atom_site_fract_y", "_atom_site_fract_z")

return cast_array_to_float(arr=self.get_from_loops(xyz_keys), dtype=float)

def build_unit_cell(
self,
fractional: bool = True,
Expand Down Expand Up @@ -468,15 +435,14 @@ def build_unit_cell(
ValueError
If the stored data cannot form a valid box.
"""
fractional_positions = self.read_wyckoff_positions()
fractional_positions = self.wyckoff_positions

# Read the cell params and convert to a matrix of basis vectors
cell = self.read_cell_params(degrees=False, mmcif=False)
cell_matrix = _matrix_from_lengths_and_angles(*cell)

symops = self.read_symmetry_operations()
symops_str = np.array2string(
symops,
self.symops,
separator=",", # Place a comma after each line in the array for eval
threshold=np.inf, # Ensure that every line is included in the string
floatmode="unique", # Ensures strings can uniquely represent each float
Expand Down Expand Up @@ -516,31 +482,6 @@ def build_unit_cell(
pos[unique_indices] if fractional else real_space_positions[unique_indices]
)

@property
def cast_values(self):
"""Bool : Whether to cast "number-like" values to ints & floats.

.. note::

When set to `True` after construction, the values are modified in-place.
This action cannot be reversed.
"""
return self._cast_values

@cast_values.setter
def cast_values(self, cast: bool):
if cast:
self._pairs = {
k: _try_cast_to_numeric(_strip_quotes(v))
for (k, v) in self.pairs.items()
}
else:
warnings.warn(
"Setting cast_values True->False has no effect on stored data.",
category=ParseWarning,
stacklevel=2,
)
self._cast_values = cast

@property
def box(self):
Expand Down Expand Up @@ -579,6 +520,68 @@ def box(self):
*self.read_cell_params(degrees=False, mmcif=False)
)

@property
def symops(self):
r"""Extract the symmetry operations from a CIF file.

Returns
-------
:math:`(N,)` :class:`numpy.ndarray[str]`:
An array of strings containing the symmetry operations in a
`parsable algebraic form`_.

.. _`parsable algebraic form`: https://www.iucr.org/__data/iucr/cifdic_html/1/cif_core.dic/Ispace_group_symop_operation_xyz.html
"""
symmetry_keys = (
"_symmetry_equiv_pos_as_xyz",
"_space_group_symop_operation_xyz",
)

# Only one key is valid in each standard, so we only ever get one match.
return self.get_from_loops(symmetry_keys)

@property
def wyckoff_positions(self):
r"""Extract symmetry-irreducible, fractional x,y,z coordinates from a CIF file.

Returns
-------
:math:`(N, 3)` :class:`numpy.ndarray[float]`:
Symmetry-irreducible positions of atoms in `fractional coordinates`_.

.. _`fractional coordinates`: https://www.iucr.org/__data/iucr/cifdic_html/1/cif_core.dic/Iatom_site_fract_.html
"""
xyz_keys = ("_atom_site_fract_x", "_atom_site_fract_y", "_atom_site_fract_z")

return cast_array_to_float(arr=self.get_from_loops(xyz_keys), dtype=float)


@property
def cast_values(self):
"""Bool : Whether to cast "number-like" values to ints & floats.

.. note::

When set to `True` after construction, the values are modified in-place.
This action cannot be reversed.
"""
return self._cast_values

@cast_values.setter
def cast_values(self, cast: bool):
if cast:
self._pairs = {
k: _try_cast_to_numeric(_strip_quotes(v))
for (k, v) in self.pairs.items()
}
else:
warnings.warn(
"Setting cast_values True->False has no effect on stored data.",
category=ParseWarning,
stacklevel=2,
)
self._cast_values = cast

@classmethod
def structured_to_unstructured(cls, arr: np.ndarray):
"""Convert a structured (column-labeled) array to a standard unstructured array.
Expand Down
4 changes: 2 additions & 2 deletions tests/test_unitcells.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def test_read_wyckoff_positions(cif_data):
if "PDB_4INS_head.cif" in cif_data.filename:
return
keys = ("_atom_site_fract_x", "_atom_site_fract_y", "_atom_site_fract_z")
parsnip_data = cif_data.file.read_wyckoff_positions()
parsnip_data = cif_data.file.wyckoff_positions
gemmi_data = _gemmi_read_table(cif_data.filename, keys)
gemmi_data = [[cif.as_number(val) for val in row] for row in gemmi_data]
np.testing.assert_array_equal(parsnip_data, gemmi_data)
Expand All @@ -45,7 +45,7 @@ def test_read_symmetry_operations(cif_data):
if "PDB_4INS_head.cif" in cif_data.filename:
return # Excerpt of PDB file does not contain symmetry information

parsnip_data = cif_data.file.read_symmetry_operations()
parsnip_data = cif_data.file.symops
gemmi_data = _gemmi_read_table(filename=cif_data.filename, keys=cif_data.symop_keys)
np.testing.assert_array_equal(parsnip_data, gemmi_data)

Expand Down