Skip to content

Commit

Permalink
Merge pull request #29 from Baharis/package_optimisation
Browse files Browse the repository at this point in the history
Package optimisation
  • Loading branch information
Baharis authored Jun 9, 2022
2 parents 2a184f8 + ff31b56 commit 00697c5
Show file tree
Hide file tree
Showing 13 changed files with 98 additions and 71 deletions.
46 changes: 28 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,27 +1,30 @@
# hikari

[![PyPI version](https://badge.fury.io/py/hikari-toolkit.svg)](https://badge.fury.io/py/hikari-toolkit)
[![PyPI version](https://img.shields.io/pypi/v/hikari-toolkit)](https://pypi.org/project/hikari-toolkit/)
[![Python version](https://img.shields.io/pypi/pyversions/hikari-toolkit.svg)](https://www.python.org/downloads/release/python-3615/)
[![codecov](https://codecov.io/gh/Baharis/hikari/branch/master/graph/badge.svg?token=SWKKW0LSKQ)](https://codecov.io/gh/Baharis/hikari)
[![CodeFactor](https://www.codefactor.io/repository/github/baharis/hikari/badge)](https://www.codefactor.io/repository/github/baharis/hikari)
[![Documentation Status](https://readthedocs.org/projects/hikari/badge/?version=stable)](https://hikari.readthedocs.io/en/stable/?badge=stable)
[![tests](https://github.com/Baharis/hikari/actions/workflows/codecov.yml/badge.svg?branch=master)](https://github.com/Baharis/hikari/actions/workflows/codecov.yml)

hikari is a simple Python3.6+ package for manipulating basic crystallographic
files: mainly .hkl, but also .res, .cif and, by extension, .fcf.
hikari is a simple Python3.6+ package intended for manipulating and running
scripts on basic crystallographic files:
.hkl, .fcf, .cif, and to some extent .res and .lst.

The following section contains brief explanation of how to install
and use hikari. For full description please see the documentation.
The following section contains a brief explanation of how to install
and use hikari. For a full description please see
[the documentation](https://hikari.readthedocs.io/en/stable/?badge=stable).

## Getting started

Hikari is registered in PyPI under the name `hikari-toolkit`.
In order to start working with the package, simply install it using:
In order to start working with the package, install it using:

$ pip install hikari-toolkit

Since it runs on python version 3.6 or newer and requires specific versions
of some popular packages such as numpy, you might be interested in
using hikari in a virtual environment, which might be created using:
Since it runs on Python 3.6+ and requires specific versions of some popular
packages such as `numpy`, you might be interested in using hikari
in a virtual environment. On Linux, it can be created using `virtualenvwrapper`:

$ mkvirtualenv -p /usr/bin/python3.6 hikari-venv

Expand All @@ -30,20 +33,27 @@ the package should be available in the namespace via `import hikari`.

## Usage

For the sake of usage, hikari is essencially divided into a few sub-modules,
For the sake of usage, hikari is essentially divided into a few sub-modules,
including dataframes, symmetry, utility, and scripts.
Dataframes contain object responsible for basic manipulation of files,
for example the `hikari.dataframes.CifFrame` is responsible for
Dataframes contain objects responsible for basic manipulation of files,
for example, the `hikari.dataframes.CifFrame` is responsible for
reading, modifying and writing the Crystal Information Files.
Scripts, on the other hands, contain ready to use sets of dataframe
instructions and aim to solve a complete problem, like reformatting the file
or evaluating data completeness in certain experiment.
In the majority of cases, you should be more interested in the latter.
Scripts, on the other hand, contain ready to use sets of dataframe
instructions and aim to solve certain problems, like reformatting the file
or evaluating data completeness in an experiment. In the majority of cases,
you will be most likely more interested in the latter.

## Author

This software is made by Daniel Tchoń, [email protected], and distributed
under MIT license. Dr hab. Anna Makal and prof. dr hab. Krzysztof Woźniak are
This software is made by
[Daniel Tchoń](https://www.researchgate.net/profile/Daniel-Tchon),
and distributed under an MIT license. It is in constant development and all
tips, suggestions, or contributions are welcome and can be sent
[here](mailto:[email protected]).
If you have utilised `hikari` in academic work, please consider citing
[this article](https://doi.org/10.1107/S2052252521009532).

Dr hab. Anna Makal and prof. dr hab. Krzysztof Woźniak are
acknowledged for helpful discussions about crystallography behind the subject.
Dr Jarosław Kalinowski and mgr inż. Damian Tchoń are acknowledged
for insightful tips on project structure and code optimisation.
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
author = u'Daniel Tchoń'

# The full version, including alpha/beta/rc tags
release = '0.2.0-alpha'
release = '0.2.0'


# -- General configuration ---------------------------------------------------
Expand Down
4 changes: 2 additions & 2 deletions hikari/dataframes/cif.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,11 +116,11 @@ def __init__(self):

@staticmethod
def _expand_names(dict_):
expanded_items = CifBlock()
expanded_items = OrderedDict()
for data_block_name, data_block in dict_.items():
names = data_block.get('_name', None)
if names:
data_block_without_name_item = OrderedDict()
data_block_without_name_item = CifBlock()
for data_name, data_value in data_block.items():
if data_name is not '_name':
data_block_without_name_item[data_name] = data_value
Expand Down
3 changes: 0 additions & 3 deletions hikari/resources/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,4 @@ def _save_pickle(data, filename):
hkl_mercury_style = get(__name__, 'hkl.msd').decode('utf-8')
characteristic_radiation = _load_json('characteristic_radiation.json')
cif_core_dict = get(__name__, 'cif_core_2.4.5.dic').decode('utf-8')
nacl_cif = get(__name__, 'NaCl.cif').decode('utf-8')
nacl_fcf = get(__name__, 'NaCl.fcf').decode('utf-8')
nacl_hkl = get(__name__, 'NaCl.hkl').decode('utf-8')
Xray_atomic_form_factors = _load_indexed_csv('Xray_atomic_form_factors.csv')
2 changes: 1 addition & 1 deletion hikari/scripts/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@
from .hkl_potency import potency_map, potency_vs_dac_opening_angle, \
potency_violin_plot, dac_potency_around_axis
from .hkl_completeness import completeness_statistics, dac_statistics, \
simulate_dac
simulate_dac, reformat_hkl
from .compare_adps import calculate_similarity_indices
from .r1_map import r1_map
21 changes: 21 additions & 0 deletions hikari/scripts/hkl_completeness.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,27 @@ def dac_statistics(a, b, c, al, be, ga,
' {:9f}'.format(p_eq / q_eq) + ' {:9f}'.format(p_eq / b_eq))


def reformat_hkl(input_path: str,
input_format: str,
output_path: str,
output_format: str):
"""
:param input_path: Relative or absolute path to the input .hkl file.
:param input_format: Format of the .hkl file. For reference
see :attr:`hikari.dataframes.HklIo.format`.
:param output_path: Relative or absolute path to the output .hkl file.
:param output_format: Format of the .hkl file. For reference
see :attr:`hikari.dataframes.HklIo.format`.
:return:
:rtype:
"""
h = HklFrame()
absolute_input_path = make_abspath(input_path)
absolute_output_path = make_abspath(output_path)
h.read(hkl_path=absolute_input_path, hkl_format=input_format)
h.write(hkl_path=absolute_output_path, hkl_format=output_format)


def simulate_dac(a, b, c, al, be, ga,
opening_angle=35,
orientation=((1, 0, 0), (0, 1, 0), (0, 0, 1)),
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

setup(
name='hikari-toolkit',
version='0.2.0-alpha',
version='0.2.0',
author='Daniel Tchoń',
author_email='[email protected]',
packages=find_packages(exclude=('legacy', )),
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
89 changes: 44 additions & 45 deletions test/test_dataframes.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,22 @@

from hikari.dataframes import BaseFrame, CifBlock, CifFrame, HklFrame, \
UBaseFrame
from hikari.resources import nacl_fcf, nacl_hkl, nacl_cif, cif_core_dict
from hikari.dataframes.cif import CifValidator
from hikari.symmetry import PG

rad60 = 1.0471975511965976
rad70 = 1.2217304763960306
rad80 = 1.3962634015954636
rad90 = 1.5707963267948966
RAD60 = 1.0471975511965976
RAD70 = 1.2217304763960306
RAD80 = 1.3962634015954636
RAD90 = 1.5707963267948966

u1 = uncertainties.ufloat(1, 1)
upi = uncertainties.ufloat(np.pi, 0)
u6 = uncertainties.ufloat(6.0, 0.1)
u7 = uncertainties.ufloat(7.0, 0.1)
u8 = uncertainties.ufloat(8.0, 0.1)
u60 = uncertainties.ufloat(60.0, 1.0)
u70 = uncertainties.ufloat(70.0, 1.0)
u80 = uncertainties.ufloat(80.0, 1.0)
u90 = uncertainties.ufloat(90.0, 1.0)
U1 = uncertainties.ufloat(1, 1)
U6 = uncertainties.ufloat(6.0, 0.1)
U7 = uncertainties.ufloat(7.0, 0.1)
U8 = uncertainties.ufloat(8.0, 0.1)
U60 = uncertainties.ufloat(60.0, 1.0)
U70 = uncertainties.ufloat(70.0, 1.0)
U80 = uncertainties.ufloat(80.0, 1.0)
U90 = uncertainties.ufloat(90.0, 1.0)


class TempFile:
Expand All @@ -37,10 +36,9 @@ def __init__(self, name, content):
self.file.write(content)


nacl_cif_file = TempFile('nacl.cif', nacl_cif)
nacl_fcf_file = TempFile('nacl.fcf', nacl_fcf)
nacl_hkl_file = TempFile('nacl.hkl', nacl_hkl)
cif_core_dict_file = TempFile('core.cif', cif_core_dict)
nacl_cif_path = str(pathlib.Path(__file__).parent.joinpath('NaCl.cif'))
nacl_fcf_path = str(pathlib.Path(__file__).parent.joinpath('NaCl.fcf'))
nacl_hkl_path = str(pathlib.Path(__file__).parent.joinpath('NaCl.hkl'))


class TestBaseFrame(unittest.TestCase):
Expand All @@ -53,17 +51,17 @@ def test_init(self):
self.assertAlmostEqual(b.a_d, 1.)
self.assertAlmostEqual(b.b_d, 1.)
self.assertAlmostEqual(b.c_d, 1.)
self.assertAlmostEqual(b.al_d, rad90)
self.assertAlmostEqual(b.be_d, rad90)
self.assertAlmostEqual(b.ga_d, rad90)
self.assertAlmostEqual(b.al_d, RAD90)
self.assertAlmostEqual(b.be_d, RAD90)
self.assertAlmostEqual(b.ga_d, RAD90)

def test_edit_cell(self):
self.assertAlmostEqual(self.b.a_d, 6.)
self.assertAlmostEqual(self.b.b_d, 7.)
self.assertAlmostEqual(self.b.c_d, 8.)
self.assertAlmostEqual(self.b.al_d, rad60)
self.assertAlmostEqual(self.b.be_d, rad70)
self.assertAlmostEqual(self.b.ga_d, rad80)
self.assertAlmostEqual(self.b.al_d, RAD60)
self.assertAlmostEqual(self.b.be_d, RAD70)
self.assertAlmostEqual(self.b.ga_d, RAD80)
with self.assertRaises(KeyError):
BaseFrame().edit_cell(alpha=0.0)

Expand Down Expand Up @@ -115,7 +113,7 @@ def test_volumes(self):

class TestCifBlockRead(unittest.TestCase):
def test_read(self):
CifBlock().read(path=nacl_cif_file.path, block='NaCl')
CifBlock().read(path=nacl_cif_path, block='NaCl')


class TestCifBlockGeneral(unittest.TestCase):
Expand All @@ -124,7 +122,7 @@ class TestCifBlockGeneral(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
super().setUpClass()
cls.b.read(path=nacl_cif_file.path, block='NaCl')
cls.b.read(path=nacl_cif_path, block='NaCl')

def test_access(self):
self.assertIn('_cell_length_a', self.b)
Expand All @@ -140,19 +138,19 @@ def test_get_as_type_single(self):
self.assertEqual(self.b.get_as_type(k, typ=str), '90')
self.assertEqual(self.b.get_as_type(k, typ=int), 90)
u_typ = uncertainties.ufloat_fromstr
self.assertEqual(repr(self.b.get_as_type(k, typ=u_typ)), repr(u90))
self.assertEqual(repr(self.b.get_as_type(k, typ=u_typ)), repr(U90))

def test_get_as_type_list(self):
k = '_atom_site_occupancy'
self.assertEqual(self.b.get_as_type(k, typ=str), ['1', '1'])
self.assertEqual(self.b.get_as_type(k, typ=int), [1, 1])
u_typ = uncertainties.ufloat_fromstr
self.assertEqual(repr(self.b.get_as_type(k, typ=u_typ)), repr([u1, u1]))
self.assertEqual(repr(self.b.get_as_type(k, typ=u_typ)), repr([U1, U1]))

def test_get_as_type_exceptions(self):
with self.assertRaises(KeyError):
_ = self.b.get_as_type('_nonexistent_key', str)
self.b['_cell_angle_beta'] = u90
self.b['_cell_angle_beta'] = U90
with self.assertRaises(TypeError):
_ = self.b.get_as_type('_cell_angle_beta', str)

Expand All @@ -167,36 +165,37 @@ class TestCifBlockInterface(unittest.TestCase):
b = CifBlock()

def test_base_frame_fill_from_cif_block_robust(self):
self.b.read(path=nacl_cif_file.path, block='NaCl')
self.b.read(path=nacl_cif_path, block='NaCl')
base = BaseFrame()
base.fill_from_cif_block(self.b, fragile=False)
self.assertAlmostEqual(base.v_d, 179.51018149594705)
self.assertAlmostEqual(base.orientation[0, 0], -0.0617076000)

def test_base_frame_fill_from_cif_block_fragile(self):
self.b.read(path=nacl_cif_file.path, block='NaCl_olex2_C2/m')
self.b.read(path=nacl_cif_path, block='NaCl_olex2_C2/m')
base = BaseFrame()
with self.assertRaises(KeyError):
base.fill_from_cif_block(self.b, fragile=True)


class TestCifValidator(unittest.TestCase):
def test_validator(self):
c = CifValidator()
self.assertIn('_atom_site_label', c)
self.assertIsInstance(c['_atom_site_label'], CifBlock)


class TestCifFrame(unittest.TestCase):
c_cif = CifFrame()
c_dic = CifFrame()
c_fcf = CifFrame()

def test_read_cif_file(self):
self.c_cif.read(nacl_fcf_file.path)
self.c_cif.read(nacl_cif_path)
self.assertIn('NaCl', self.c_cif)
self.assertIsInstance(self.c_cif['NaCl'], CifBlock)

def test_read_dic_file(self):
self.c_dic.read(cif_core_dict_file.path)
self.assertIn('atom_site_label', self.c_dic)
self.assertIsInstance(self.c_dic['atom_site_label'], CifBlock)

def test_read_fcf_file(self):
self.c_fcf.read(nacl_fcf_file.path)
self.c_fcf.read(nacl_fcf_path)
self.assertIn('NaCl', self.c_fcf)
self.assertIsInstance(self.c_fcf['NaCl'], CifBlock)

Expand All @@ -207,7 +206,7 @@ class TestHklFrame(unittest.TestCase):

@classmethod
def setUpClass(cls) -> None:
cls.h1.read(nacl_hkl_file.path, hkl_format='shelx_4')
cls.h1.read(nacl_hkl_path, hkl_format='shelx_4')
cls.h1.edit_cell(a=5.64109, b=5.64109, c=5.64109)

def setUp(self) -> None:
Expand All @@ -228,7 +227,7 @@ def test_str(self):
self.assertIn('419.465', str_h[-300:])

def test_read(self):
self.h2.read(nacl_hkl_file.path, hkl_format='free_4')
self.h2.read(nacl_hkl_path, hkl_format='free_4')
self.assertEqual(self.h2.table.__len__(), 8578)

def test_la_and_r_lim(self):
Expand Down Expand Up @@ -288,7 +287,7 @@ class TestUBaseFrame(unittest.TestCase):

def setUp(self) -> None:
self.b = UBaseFrame()
self.b.edit_cell(a=u6, b=u7, c=u8, al=u60, be=u70, ga=u80)
self.b.edit_cell(a=U6, b=U7, c=U8, al=U60, be=U70, ga=U80)

def test_init(self):
b = UBaseFrame()
Expand All @@ -303,9 +302,9 @@ def test_edit_cell(self):
self.assertAlmostEqual(self.b.a_d.n, 6)
self.assertAlmostEqual(self.b.b_d.n, 7)
self.assertAlmostEqual(self.b.c_d.n, 8)
self.assertAlmostEqual(self.b.al_d.n, rad60)
self.assertAlmostEqual(self.b.be_d.n, rad70)
self.assertAlmostEqual(self.b.ga_d.n, rad80)
self.assertAlmostEqual(self.b.al_d.n, RAD60)
self.assertAlmostEqual(self.b.be_d.n, RAD70)
self.assertAlmostEqual(self.b.ga_d.n, RAD80)
with self.assertRaises(KeyError):
BaseFrame().edit_cell(alpha=0.0)

Expand Down

0 comments on commit 00697c5

Please sign in to comment.