diff --git a/.github/workflows/pypi-test-and-publish.yaml b/.github/workflows/pypi-test-and-publish.yaml new file mode 100644 index 00000000..040b8e7e --- /dev/null +++ b/.github/workflows/pypi-test-and-publish.yaml @@ -0,0 +1,77 @@ +name: Build and Publish + +on: + push: + tags: + - "v*" + branches: + - "main" + - "breaking" + pull_request: + +jobs: + build: + name: Build Distribution + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4.2.2 + with: + persist-credentials: false + - name: Set up Python + uses: actions/setup-python@v5.4.0 + with: + python-version: "3.x" + + - name: Install pypa/build + run: python3 -m pip install build --user + - name: Build a binary wheel and a source tarball + run: python3 -m build + - name: Store the distribution packages + uses: actions/upload-artifact@v4.4.3 + with: + name: python-package-distributions + path: dist/ + + publish-to-testpypi: + name: Publish to TestPyPI + needs: [build] + runs-on: ubuntu-latest + environment: + name: testpypi + url: https://test.pypi.org/p/parsnip-cif + permissions: + id-token: write + + steps: + - name: Download all the dists + uses: actions/download-artifact@v4.1.8 + with: + name: python-package-distributions + path: dist/ + - name: Publish distribution to TestPyPI + uses: pypa/gh-action-pypi-publish@release/v1 + with: + repository-url: https://test.pypi.org/legacy/ + verbose: true + skip-existing: true + + publish-to-pypi: + name: Publish to PyPI + if: startsWith(github.ref, 'refs/tags/v') + needs: [build] + runs-on: ubuntu-latest + environment: + name: pypi + url: https://pypi.org/p/parsnip-cif + permissions: + id-token: write + + steps: + - name: Download all the dists + uses: actions/download-artifact@v4.1.8 + with: + name: python-package-distributions + path: dist/ + - name: Publish to PyPI + uses: pypa/gh-action-pypi-publish@v1.12.2 diff --git a/.github/workflows/requirements.yaml b/.github/workflows/requirements.yaml deleted file mode 100644 index 6274176b..00000000 --- a/.github/workflows/requirements.yaml +++ /dev/null @@ -1,23 +0,0 @@ -name: Generate lockfiles - -on: - schedule: [ "0 0 1 * *" ] - -jobs: - install-python-and-run: - runs-on: "ubuntu-20.04" - - strategy: - # Python 3.6 file cannot be generated with `uv`, and is manually resolved - matrix: { python-version: [3.7, 3.8, 3.9, 3.10, 3.11, 3.12, 3.13] } - - steps: - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.python-version }} - - - name: Run generate-files.sh - run: | - uv pip compile --python-version=${{ matrix.python-version}} \ - pyproject.toml tests/requirements.in > .github/requirements-${{ matrix.python-version }}.txt diff --git a/LICENSE b/LICENSE index bbc82170..15498597 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ BSD 3-Clause License -Copyright (c) 2024, Glotzer Group +Copyright (c) 2025, The Regents of the University of Michigan Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/README.rst b/README.rst index fae22baa..1d9d92f8 100644 --- a/README.rst +++ b/README.rst @@ -1,6 +1,6 @@ .. _images: -.. image:: doc/source/_static/parsnip_header_dark.svg +.. image:: https://raw.githubusercontent.com/glotzerlab/parsnip/refs/heads/main/doc/source/_static/parsnip_header_dark.svg :width: 600 @@ -28,10 +28,14 @@ .. _parse: Importing ``parsnip`` allows users to read `CIF 1.1 `_ files, as well as many features from the `CIF 2.0 `_ and `mmCIF `_ formats. -Creating a :class:`~.CifFile` object provides easy access to name-value :attr:`~.CifFile.pairs`, as well -as `loop\_`-delimited :attr:`~.CifFile.tables`. Data entries can be extracted as python primitives or +Creating a `CifFile`_ object provides easy access to name-value `pairs`_, as well +as `loop\_`-delimited `loops`_. Data entries can be extracted as python primitives or numpy arrays for further use. +.. _CifFile: https://parsnip-cif.readthedocs.io/en/latest/package-parse.html#parsnip.parsnip.CifFile +.. _pairs: https://parsnip-cif.readthedocs.io/en/latest/package-parse.html#parsnip.parsnip.CifFile.pairs +.. _loops: https://parsnip-cif.readthedocs.io/en/latest/package-parse.html#parsnip.parsnip.CifFile.loops + .. _installing: Setup @@ -45,14 +49,14 @@ Installation via pip .. code:: bash - python -m pip install parsnip + python -m pip install parsnip-cif Installation via conda-forge ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. code:: bash - conda install -c conda-forge parsnip + conda install -c conda-forge parsnip-cif Installation from source diff --git a/changelog.rst b/changelog.rst index 57ccded9..7f7a3c7e 100644 --- a/changelog.rst +++ b/changelog.rst @@ -4,7 +4,7 @@ Changelog The format is based on `Keep a Changelog `__. This project adheres to `Semantic Versioning `__. -v1.0.0 - 20xx-xx-xx +v0.2.0 - 2025-02-19 ------------------- Added diff --git a/contributoragreement.md b/contributoragreement.md new file mode 100644 index 00000000..743c5cb6 --- /dev/null +++ b/contributoragreement.md @@ -0,0 +1,28 @@ +# Parsnip Contributor Agreement + +These terms apply to your contribution to the Parsnip Open Source Project ("Project") owned or managed by the Regents of the University of Michigan ("Michigan"), and set out the intellectual property rights you grant to Michigan in the contributed materials. If this contribution is on behalf of a company, the term "you" will also mean the company you identify below. If you agree to be bound by these terms, fill in the information requested below and provide your signature. + +1. The term "contribution" means any source code, object code, patch, tool, sample, graphic, specification, manual, documentation, or any other material posted or submitted by you to a project. +2. With respect to any worldwide copyrights, or copyright applications and registrations, in your contribution: + * you hereby assign to Michigan joint ownership, and to the extent that such assignment is or becomes invalid, ineffective or unenforceable, you hereby grant to Michigan a perpetual, irrevocable, non-exclusive, worldwide, no-charge, royalty-free, unrestricted license to exercise all rights under those copyrights. This includes, at Michigan's option, the right to sublicense these same rights to third parties through multiple levels of sublicensees or other licensing arrangements; + * you agree that both Michigan and you can do all things in relation to your contribution as if each of us were the sole owners, and if one of us makes a derivative work of your contribution, the one who makes the derivative work (or has it made) will be the sole owner of that derivative work; + * you agree that you will not assert any moral rights in your contribution against us, our licensees or transferees; + * you agree that we may register a copyright in your contribution and exercise all ownership rights associated with it; and + * you agree that neither of us has any duty to consult with, obtain the consent of, pay or render an accounting to the other for any use or distribution of your contribution. +3. With respect to any patents you own, or that you can license without payment to any third party, you hereby grant to Michigan a perpetual, irrevocable, non-exclusive, worldwide, no-charge, royalty-free license to: + * make, have made, use, sell, offer to sell, import, and otherwise transfer your contribution in whole or in part, alone or in combination with or included in any product, work or materials arising out of the project to which your contribution was submitted; and + * at Michigan's option, to sublicense these same rights to third parties through multiple levels of sublicensees or other licensing arrangements. +4. Except as set out above, you keep all right, title, and interest in your contribution. The rights that you grant to Michigan under these terms are effective on the date you first submitted a contribution to Michigan, even if your submission took place before the date you sign these terms. Any contribution Michigan makes available under any license will also be made available under a suitable Free Software Foundation or Open Source Initiative approved license. +5. With respect to your contribution, you represent that: + * it is an original work and that you can legally grant the rights set out in these terms; + * it does not to the best of your knowledge violate any third party's copyrights, trademarks, patents, or other intellectual property rights; and +you are authorized to sign this contract on behalf of your company (if identified below). +6. The terms will be governed by the laws of the State of Michigan and applicable U.S. Federal Law. Any choice of law rules will not apply. + +**By making contribution, you electronically sign and agree to the terms of the Parsnip Contributor Agreement.** + +![by-sa.png](https://licensebuttons.net/l/by-sa/3.0/88x31.png) + +Based on the Sun Contributor Agreement - version 1.5. +This document is licensed under a Creative Commons Attribution-Share Alike 3.0 Unported License +https://creativecommons.org/licenses/by-sa/3.0/ diff --git a/doc/source/development.rst b/doc/source/development.rst index d572a90a..5dbb73f5 100644 --- a/doc/source/development.rst +++ b/doc/source/development.rst @@ -23,6 +23,7 @@ As portability is a primary feature of **parsnip**, tests are run run on Python .. _NEP 29: https://numpy.org/neps/nep-0029-deprecation_policy.html +Please refer to the `Contributor Agreement `_ for further information. Style Guidelines ---------------- diff --git a/parsnip/__init__.py b/parsnip/__init__.py index ee63096b..b147352d 100644 --- a/parsnip/__init__.py +++ b/parsnip/__init__.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, Glotzer Group +# Copyright (c) 2025, The Regents of the University of Michigan # This file is from the parsnip project, released under the BSD 3-Clause License. """``parsnip``: a package for the simple reading and processing of .cif files.""" diff --git a/parsnip/_errors.py b/parsnip/_errors.py index 2163aba3..d37f76cc 100644 --- a/parsnip/_errors.py +++ b/parsnip/_errors.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, Glotzer Group +# Copyright (c) 2025, The Regents of the University of Michigan # This file is from the parsnip project, released under the BSD 3-Clause License. diff --git a/parsnip/conftest.py b/parsnip/conftest.py index c090e53a..0c86b8a4 100644 --- a/parsnip/conftest.py +++ b/parsnip/conftest.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, Glotzer Group +# Copyright (c) 2025, The Regents of the University of Michigan # This file is from the parsnip project, released under the BSD 3-Clause License. """Configure doctest namespace.""" diff --git a/parsnip/parsnip.py b/parsnip/parsnip.py index b538b7a4..0f7e72e2 100644 --- a/parsnip/parsnip.py +++ b/parsnip/parsnip.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, Glotzer Group +# Copyright (c) 2025, The Regents of the University of Michigan # This file is from the parsnip project, released under the BSD 3-Clause License. r"""An interface for reading `CIF`_ files in Python. @@ -557,7 +557,7 @@ def box(self): Example ------- - This method provides a convinient interface to create box objects. + This method provides a convenient interface to create box objects. >>> box = cif.box >>> print(box) diff --git a/parsnip/patterns.py b/parsnip/patterns.py index b784f43c..4f4e7e57 100644 --- a/parsnip/patterns.py +++ b/parsnip/patterns.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, Glotzer Group +# Copyright (c) 2025, The Regents of the University of Michigan # This file is from the parsnip project, released under the BSD 3-Clause License. """Functions and classes to process string data. diff --git a/pyproject.toml b/pyproject.toml index 52d4343e..1fb899b6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,11 +3,11 @@ requires = ["setuptools","wheel"] build-backend = "setuptools.build_meta" [project] -name = "parsnip" -version = "0.1.0" +name = "parsnip-cif" +version = "0.2.0" requires-python = ">=3.7" description = "Minimal library for parsing CIF & mmCIF files in Python." -readme = "README.md" +readme = "README.rst" license = { file = "LICENSE" } authors = [ {name = "Jen Bradley", email = "jenbrad@umich.edu"}, @@ -31,6 +31,9 @@ dynamic = ["optional-dependencies"] Source = "https://github.com/glotzerlab/parsnip" Issues = "https://github.com/glotzerlab/parsnip/issues" +[tool.setuptools] +packages=["parsnip"] + [tool.setuptools.dynamic] optional-dependencies = {tests = { file = ["tests/requirements.in"] }, doc = { file = ["doc/requirements.txt"] }} @@ -91,3 +94,8 @@ known-first-party = ["parsnip"] [tool.ruff.format] quote-style = "double" indent-style = "space" + +[tool.coverage.run] +omit = [ + "parsnip/coverage.py", +] diff --git a/temp.py b/temp.py deleted file mode 100644 index 78dc8d70..00000000 --- a/temp.py +++ /dev/null @@ -1,74 +0,0 @@ -# Copyright (c) 2024, Glotzer Group -# This file is from the parsnip project, released under the BSD 3-Clause License. - -import freud -import gsd.hoomd -import numpy as np -import rowan -from coxeter.families import ArchimedeanFamily, JohnsonFamily, PlatonicFamily - -from parsnip import CifFile - -gbf = JohnsonFamily.get_shape("Gyrobifastigium") -gbf.volume = 4 -oct = PlatonicFamily.get_shape("Octahedron") -oct.volume = 4 - -ttet = ArchimedeanFamily.get_shape("Truncated Tetrahedron") -ttet.volume = 4 - -# cif = CifFile("../aflow_cif_db/AFLOW/A_tI4_141_a.cif") -# cif = CifFile("../aflow_cif_db/AFLOW/A_cI2_229_a.cif") -cif = CifFile("../aflow_cif_db/AFLOW/A_hR2_166_c.alpha-As.cif") -N = 1 -box, pos = freud.data.UnitCell(cif.box, cif.build_unit_cell()).generate_system(N) -print(pos) - - -# q1 = rowan.from_axis_angle([0,1,0],np.pi) -# q1 = rowan.from_matrix(rowan.to_matrix(rowan.from_axis_angle([0,1,0],np.pi/2)) @ rowan.to_matrix(rowan.from_axis_angle([0,1,0], np.pi/2))) -# q2 = rowan.from_matrix(rowan.to_matrix(rowan.from_axis_angle([0,1,0],np.pi/2)) @ rowan.to_matrix(rowan.from_axis_angle([0,1,0], np.pi))) -# q2 = (rowan.from_axis_angle([0,0,1],np.pi/2)) -# - -# q1 = rowan.from_matrix(rowan.to_matrix(rowan.from_axis_angle([0,1,0.5],np.pi/2)) @ rowan.to_matrix(rowan.from_axis_angle([0,0,1], np.pi))) -# q2 = rowan.from_matrix(rowan.to_matrix(rowan.from_axis_angle([0,1,0.5],np.pi/2)) @ rowan.to_matrix(rowan.from_axis_angle([0,1,0], np.pi))) - - -q1 = rowan.from_matrix( - # rowan.to_matrix(rowan.from_axis_angle([0,1,0],np.pi/2)) @ - # rowan.to_matrix(rowan.from_axis_angle([0,0,1],np.pi/2)) @ - rowan.to_matrix(rowan.from_axis_angle([0, 0, 1], np.pi)) -) -q2 = rowan.from_matrix( - rowan.to_matrix(rowan.from_axis_angle([0, 1, 0], np.pi / 2)) - @ - # rowan.to_matrix(rowan.from_axis_angle([0,0,1],np.pi/2)) @ - rowan.to_matrix(rowan.from_axis_angle([0, 1, 0], np.pi)) -) -# q2 = rowan.from_matrix(rowan.to_matrix(rowan.from_axis_angle([0,1,0],np.pi/2)) @ rowan.to_matrix(rowan.from_axis_angle([0,1,0], np.pi))) - -q1 = [1, 0, 0, 0] -q2 = [np.cos(np.pi / 4), np.sin(np.pi / 4), 0, 0] -# q1 = rowan.from_matrix( -# rowan.to_matrix(rowan.from_axis_angle([0,0,1], np.pi/3)) -# ) -# q2 = rowan.from_matrix( -# rowan.to_matrix(rowan.from_axis_angle([0,1,0],np.pi)) @ -# rowan.to_matrix(rowan.from_axis_angle([0,0,1], np.pi/3)) -# ) - -print("") -frame = gsd.hoomd.Frame() -frame.particles.N = len(pos) -frame.particles.position = pos -# frame.particles.orientation = [q1, q1, q2, q2] -# frame.particles.orientation = [q1, q2] * (len(pos) // 2) -frame.particles.orientation = [q2, q1] * (len(pos) // 2) -# frame.particles.orientation = [q2, q2, q2, q1, q1, q1] -# frame.particles.orientation = [[1,0,0,0]] * len(pos) -frame.configuration.box = [*box.L, box.xy, box.xz, box.yz] -frame.particles.type_shapes = [ttet.gsd_shape_spec] - -with gsd.hoomd.open("lattice.gsd", "w") as f: - f.append(frame)