From b52ed1e4dd47ad53b06ae6d2371ba612e97da290 Mon Sep 17 00:00:00 2001 From: Antonios Sarikas Date: Mon, 6 May 2024 23:05:49 +0300 Subject: [PATCH] Fix docs and changelog Additional changes: 1. Fixed bug in `visualize.py` where plots didn't render. 2. Change workflow to build only when tags are pushed. --- .github/workflows/python-publish.yaml | 6 ++-- docs/source/changes.rst | 6 +++- src/moxel/utils.py | 45 +++++++++++++++----------- src/moxel/visualize.py | 6 +++- tests/test_cli.py | 46 --------------------------- tests/test_visualize.py | 12 +++++++ 6 files changed, 51 insertions(+), 70 deletions(-) delete mode 100644 tests/test_cli.py create mode 100644 tests/test_visualize.py diff --git a/.github/workflows/python-publish.yaml b/.github/workflows/python-publish.yaml index 641adc5..b55aef6 100644 --- a/.github/workflows/python-publish.yaml +++ b/.github/workflows/python-publish.yaml @@ -1,10 +1,11 @@ -name: Publish Python 🐍 distribution 📦 to PyPI and TestPyPI +name: Publish Python 🐍 distribution 📦 to PyPI on: push jobs: build: name: Build distribution 📦 + if: startsWith(github.ref, 'refs/tags/') # only build on tag pushes runs-on: ubuntu-latest steps: @@ -28,8 +29,7 @@ jobs: path: dist/ publish-to-pypi: - name: >- - Publish Python 🐍 distribution 📦 to PyPI + name: Publish Python 🐍 distribution 📦 to PyPI if: startsWith(github.ref, 'refs/tags/') # only publish to PyPI on tag pushes needs: - build diff --git a/docs/source/changes.rst b/docs/source/changes.rst index 01cb2f4..a8a9e3a 100644 --- a/docs/source/changes.rst +++ b/docs/source/changes.rst @@ -8,9 +8,13 @@ Version 0.1.0 * :func:`moxel.utils.voxels_from_files` and :func:`moxel.utils.voxels_from_dir` - 1. Now they store the names of the materials, so users don't need to do it. + 1. Now they store the names of the materials as a :class:`list`, + so users don't need to do it. 2. Parameter ``out_pathname`` now must be specified (no longer optional). + * The usage of the CLI is now ``moxel `` instead of ``python -m + moxel``. + .. versionremoved:: 0.1.0 * Easy imports, such as ``from moxel import Grid``. diff --git a/src/moxel/utils.py b/src/moxel/utils.py index 18613e7..23c9720 100644 --- a/src/moxel/utils.py +++ b/src/moxel/utils.py @@ -119,8 +119,8 @@ def __init__(self, grid_size=25, cutoff=10, epsilon=50, sigma=2.5): def load_structure(self, pathname): r""" - Load a crystal structure from a file in a format supported by pymatgen - (CIF, JSON, etc.) + Load a crystal structure from a file in a format supported by + :meth:`pymatgen.core.Structure.from_file`. Parameters ---------- @@ -179,9 +179,10 @@ def calculate(self, cubic_box=False, length=30, potential='lj', n_jobs=None): self._simulation_box = self.structure * scale if potential == 'lj': - # Cache LJ parameters for all atoms in the simulation box + # Cache LJ parameters for all atoms in the simulation box. self._lj_params = np.array([lj_params[atom.species_string] for atom in self._simulation_box]) - # Cache fractional coordinates, since this is a slow function in pymatgen + + # Cache fractional coordinates since this is a slow function in pymatgen. self._frac_coords = self._simulation_box.frac_coords # Embarrassingly parallel. @@ -214,20 +215,24 @@ def lj_potential(self, coords): else: cartesian_coords = self._simulation_box.lattice.get_cartesian_coords(coords) - _, r_ij, indices, _ = self._simulation_box._lattice.get_points_in_sphere(self._frac_coords, cartesian_coords, self.cutoff, zip_results=False) + _, r_ij, indices, _ = self._simulation_box._lattice.get_points_in_sphere( + self._frac_coords, cartesian_coords, + self.cutoff, zip_results=False, + ) - # No neighbor, zero energy - # Need to check for length of r_ij because of https://github.com/materialsproject/pymatgen/issues/3794 - if len(r_ij) == 0: + ''' + Need to check for length of r_ij because of + https://github.com/materialsproject/pymatgen/issues/3794 + ''' + if len(r_ij) == 0: # No neighbor, zero energy. return 1. - # Close contact, infinite energy - if np.any(r_ij < 1e-3): + if np.any(r_ij < 1e-3): # Close contact, infinite energy. return 0. es_j = self._lj_params[indices] - x = (0.5 * (es_j[:,1] + self.sigma)) / r_ij - e = 4 * np.sqrt(es_j[:,0] * self.epsilon) + x = (0.5 * (es_j[:, 1] + self.sigma)) / r_ij + e = 4 * np.sqrt(es_j[:, 0] * self.epsilon) energy = sum(e * (x**12 - x**6)) # This should be changed with clipping in future versions. @@ -303,8 +308,8 @@ def voxels_from_files( ├──voxels.npy └──names.json - The file ``names.json`` stores the names of the materials, which might be - useful for later indexing. + The file ``names.json`` stores the names of the materials as a + :class:`list`, which might be useful for later indexing. Parameters ---------- @@ -325,8 +330,9 @@ def voxels_from_files( length : float, default=30 The size of the cubic box in Å. Takes effect only if ``cubic_box == True``. n_jobs : int, optional - Number of jobs to run in parallel. If ``None``, all processors are used. - + Number of jobs to run in parallel. If ``None``, then the number returned + by ``os.cpu_count()`` is used. + Notes ----- * Samples in output array follow the order in ``cif_pathnames``. @@ -376,8 +382,8 @@ def voxels_from_dir( ├──voxels.npy └──names.json - The file ``names.json`` stores the names of the materials, which might be - useful for later indexing. + The file ``names.json`` stores the names of the materials as a + :class:`list`, which might be useful for later indexing. Parameters ---------- @@ -398,7 +404,8 @@ def voxels_from_dir( length : float, default=30 The size of the cubic box in Å. Takes effect only if ``cubic_box == True``. n_jobs : int, optional - Number of jobs to run in parallel. If ``None``, all processors are used. + Number of jobs to run in parallel. If ``None``, then the number returned + by ``os.cpu_count()`` is used. Notes ----- diff --git a/src/moxel/visualize.py b/src/moxel/visualize.py index a4bf461..a2802ea 100644 --- a/src/moxel/visualize.py +++ b/src/moxel/visualize.py @@ -24,6 +24,7 @@ import numpy as np import pyvista as pv import matplotlib as mpl +import matplotlib.pyplot as plt def plot_voxels_mpl(voxels, *, fill_pattern=None, colorbar=True, cmap='viridis', **kwargs): @@ -58,7 +59,9 @@ def plot_voxels_mpl(voxels, *, fill_pattern=None, colorbar=True, cmap='viridis', Examples -------- + >>> voxels = np.random.randn(5, 5, 5) >>> fig = plot_voxels_mpl(voxels, cmap='coolwarm') + >>> plt.show() # Not needed for Jupyter. """ if np.all(fill_pattern) == None: @@ -72,7 +75,7 @@ def plot_voxels_mpl(voxels, *, fill_pattern=None, colorbar=True, cmap='viridis', _ = ax.voxels(filled=fill_pattern, facecolors=colors, **kwargs) if colorbar: - mappable = mpl.cm.ScalarMappable(norm=mpl.colors.Normalize(), cmap=cmap) + mappable = mpl.cm.ScalarMappable(norm=norm, cmap=cmap) cbar = fig.colorbar( mappable, ax=ax, ticks=[], extend='max', shrink=0.7, pad=0.1, @@ -99,6 +102,7 @@ def plot_voxels_pv(voxels, **kwargs): Examples -------- + >>> voxels = np.random.randn(5, 5, 5) >>> plot_voxels_pv(voxels, cmap='coolwarm') """ pl = pv.Plotter() diff --git a/tests/test_cli.py b/tests/test_cli.py deleted file mode 100644 index 8a9c138..0000000 --- a/tests/test_cli.py +++ /dev/null @@ -1,46 +0,0 @@ -# This file is part of MOXελ. -# Copyright (C) 2023 Antonios P. Sarikas - -# MOXελ is free software: you can redistribute it and/or modify -# it under the terms of the GNU 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 General Public License for more details. - -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -""" -Run tests from the project's root. - -python -m unittest tests. -""" - -import os -import unittest -import tempfile - - -@unittest.skip -class TestMoxelCLI(unittest.TestCase): - - def test_help(self): - exit_status = os.system('python -m src.moxel -h') - self.assertEqual(exit_status, 0) - - def test_run(self): - with tempfile.TemporaryDirectory() as dir_path: - file_name = 'voxels.npy' - exit_status = os.system( - f'python -m src.moxel tests/CIFs/foo\ - -n 5 -o {dir_path}/{file_name}' - ) - self.assertEqual(exit_status, 0) - - -if __name__ == '__main__': - unittest.main() diff --git a/tests/test_visualize.py b/tests/test_visualize.py new file mode 100644 index 0000000..3110964 --- /dev/null +++ b/tests/test_visualize.py @@ -0,0 +1,12 @@ +import unittest +import doctest +import moxel.visualize + + +def load_tests(loader, tests, ignore): + tests.addTests(doctest.DocTestSuite(moxel.visualize)) + return tests + + +if __name__ == '__main__': + unittest.main()