From b1f5f37fa228977fec3a90d89fc6da2ebeb968ed Mon Sep 17 00:00:00 2001 From: Cian Hughes Date: Wed, 1 May 2024 17:46:03 +0100 Subject: [PATCH] First commit. Separated project out from MTPy. --- .gitignore | 174 ++++++++++++++++++++++++++++++++++ .pre-commit-config.yaml | 34 +++++++ Cargo.toml | 23 +++++ LICENSE | 21 +++++ README.md | 1 + poetry.lock | 171 +++++++++++++++++++++++++++++++++ pyproject.toml | 204 ++++++++++++++++++++++++++++++++++++++++ read_layers.pyi | 11 +++ src/lib.rs | 195 ++++++++++++++++++++++++++++++++++++++ 9 files changed, 834 insertions(+) create mode 100644 .gitignore create mode 100644 .pre-commit-config.yaml create mode 100644 Cargo.toml create mode 100644 LICENSE create mode 100644 README.md create mode 100644 poetry.lock create mode 100644 pyproject.toml create mode 100644 read_layers.pyi create mode 100644 src/lib.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..77e9566 --- /dev/null +++ b/.gitignore @@ -0,0 +1,174 @@ +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ \ No newline at end of file diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..7a266e7 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,34 @@ +default_language_version: + python: python3.11 + +repos: + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.3.4 + hooks: + - id: ruff + types: [python] + exclude: docs/ + - id: ruff-format + types: [python] + + - repo: https://github.com/pre-commit/mirrors-mypy + rev: v1.9.0 + hooks: + - id: mypy + types: [python] + exclude: docs/ + + - repo: https://github.com/doublify/pre-commit-rust + rev: v1.0 + hooks: + - id: fmt + - id: cargo-check + + - repo: https://github.com/python-poetry/poetry + rev: "1.8.0" + hooks: + - id: poetry-check + - id: poetry-install + - id: poetry-lock + - id: poetry-export + args: ["-f", "requirements.txt", "-o", "./requirements.txt"] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..2bbe193 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "read_aconity_layers" +version = "0.1.0" +edition = "2021" + +[lib] +path = "src/lib.rs" +crate-type = ["cdylib"] + +[dependencies] +glob = "*" +ndarray = { version = "0.15.6", features = ["rayon"] } +numpy = "*" +pyo3 = { version = "0.18.1", features = ["extension-module"] } +csv = "*" +ndarray-csv = "*" +rayon = "*" +indicatif = "*" + +[tool.maturin] +profile = "release" +all-features = true +manifest-path = "Cargo.toml" diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..b2ff423 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Cian-H + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..09bcc0f --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +A utility for fast reading of layer data from the aconity mini powder bed fusion machine diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..e78a00d --- /dev/null +++ b/poetry.lock @@ -0,0 +1,171 @@ +# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. + +[[package]] +name = "maturin" +version = "1.5.1" +description = "Build and publish crates with pyo3, rust-cpython and cffi bindings as well as rust binaries as python packages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "maturin-1.5.1-py3-none-linux_armv6l.whl", hash = "sha256:589e9b7024007e130b136ba6f1c2c8393a87e42cf968d12852913ab1e3c69ed3"}, + {file = "maturin-1.5.1-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:a1abda07093b3c8ef897626166c02ed64e3e446c48460b28efb51833abf89cbb"}, + {file = "maturin-1.5.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:48a1fbbdc2514525f27d6d339ab97b098ede28759f8593d110c89cc07bbe40ed"}, + {file = "maturin-1.5.1-py3-none-manylinux_2_12_i686.manylinux2010_i686.musllinux_1_1_i686.whl", hash = "sha256:96d96b1fa3a165db9ca539f764d31da8ebc92e31ca3a1dd6ccd50008d222bd96"}, + {file = "maturin-1.5.1-py3-none-manylinux_2_12_x86_64.manylinux2010_x86_64.musllinux_1_1_x86_64.whl", hash = "sha256:786bf36a98c4e27cbebb1dc8e432c1bcbbb59e7a9719592cbb89e46a0ccd5bcc"}, + {file = "maturin-1.5.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:d821b37da759884ad09cfee4cd9deac10f4132744cc66e4d9190a1972233bc83"}, + {file = "maturin-1.5.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l.whl", hash = "sha256:62133bf690555bbc8cc6b1c18a0c57b0ab2b4d68d3fcd320eb16df941563fe06"}, + {file = "maturin-1.5.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.musllinux_1_1_ppc64le.whl", hash = "sha256:6bff165252b1fcc887679ddf7b71b5cc024327ba96ea893133be38c0ed38f163"}, + {file = "maturin-1.5.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2c42a95466ffc3de0a3940cd20c57cf0c44fe5ea679375d73422afbb00236c64"}, + {file = "maturin-1.5.1-py3-none-win32.whl", hash = "sha256:d09538b4aa0da4b59fd47cb429003b45bfd5d801714adf1db2511bf8bdea532f"}, + {file = "maturin-1.5.1-py3-none-win_amd64.whl", hash = "sha256:a3db9054222ac79275e082b21cfd234b8e036714a4ff227a0a28f6a3ffa3744d"}, + {file = "maturin-1.5.1-py3-none-win_arm64.whl", hash = "sha256:acf528e51413f6ae489473d64116d8c83f140386349004949d29137c16a82193"}, + {file = "maturin-1.5.1.tar.gz", hash = "sha256:3dd834ece80edb866af18cbd4635e0ecac40139c726428d5f1849ae154b26dca"}, +] + +[package.extras] +patchelf = ["patchelf"] +zig = ["ziglang (>=0.10.0,<0.11.0)"] + +[[package]] +name = "mypy" +version = "1.10.0" +description = "Optional static typing for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "mypy-1.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:da1cbf08fb3b851ab3b9523a884c232774008267b1f83371ace57f412fe308c2"}, + {file = "mypy-1.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:12b6bfc1b1a66095ab413160a6e520e1dc076a28f3e22f7fb25ba3b000b4ef99"}, + {file = "mypy-1.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e36fb078cce9904c7989b9693e41cb9711e0600139ce3970c6ef814b6ebc2b2"}, + {file = "mypy-1.10.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2b0695d605ddcd3eb2f736cd8b4e388288c21e7de85001e9f85df9187f2b50f9"}, + {file = "mypy-1.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:cd777b780312ddb135bceb9bc8722a73ec95e042f911cc279e2ec3c667076051"}, + {file = "mypy-1.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3be66771aa5c97602f382230165b856c231d1277c511c9a8dd058be4784472e1"}, + {file = "mypy-1.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8b2cbaca148d0754a54d44121b5825ae71868c7592a53b7292eeb0f3fdae95ee"}, + {file = "mypy-1.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ec404a7cbe9fc0e92cb0e67f55ce0c025014e26d33e54d9e506a0f2d07fe5de"}, + {file = "mypy-1.10.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e22e1527dc3d4aa94311d246b59e47f6455b8729f4968765ac1eacf9a4760bc7"}, + {file = "mypy-1.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:a87dbfa85971e8d59c9cc1fcf534efe664d8949e4c0b6b44e8ca548e746a8d53"}, + {file = "mypy-1.10.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a781f6ad4bab20eef8b65174a57e5203f4be627b46291f4589879bf4e257b97b"}, + {file = "mypy-1.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b808e12113505b97d9023b0b5e0c0705a90571c6feefc6f215c1df9381256e30"}, + {file = "mypy-1.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f55583b12156c399dce2df7d16f8a5095291354f1e839c252ec6c0611e86e2e"}, + {file = "mypy-1.10.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4cf18f9d0efa1b16478c4c129eabec36148032575391095f73cae2e722fcf9d5"}, + {file = "mypy-1.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:bc6ac273b23c6b82da3bb25f4136c4fd42665f17f2cd850771cb600bdd2ebeda"}, + {file = "mypy-1.10.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9fd50226364cd2737351c79807775136b0abe084433b55b2e29181a4c3c878c0"}, + {file = "mypy-1.10.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f90cff89eea89273727d8783fef5d4a934be2fdca11b47def50cf5d311aff727"}, + {file = "mypy-1.10.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fcfc70599efde5c67862a07a1aaf50e55bce629ace26bb19dc17cece5dd31ca4"}, + {file = "mypy-1.10.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:075cbf81f3e134eadaf247de187bd604748171d6b79736fa9b6c9685b4083061"}, + {file = "mypy-1.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:3f298531bca95ff615b6e9f2fc0333aae27fa48052903a0ac90215021cdcfa4f"}, + {file = "mypy-1.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fa7ef5244615a2523b56c034becde4e9e3f9b034854c93639adb667ec9ec2976"}, + {file = "mypy-1.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3236a4c8f535a0631f85f5fcdffba71c7feeef76a6002fcba7c1a8e57c8be1ec"}, + {file = "mypy-1.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a2b5cdbb5dd35aa08ea9114436e0d79aceb2f38e32c21684dcf8e24e1e92821"}, + {file = "mypy-1.10.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:92f93b21c0fe73dc00abf91022234c79d793318b8a96faac147cd579c1671746"}, + {file = "mypy-1.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:28d0e038361b45f099cc086d9dd99c15ff14d0188f44ac883010e172ce86c38a"}, + {file = "mypy-1.10.0-py3-none-any.whl", hash = "sha256:f8c083976eb530019175aabadb60921e73b4f45736760826aa1689dda8208aee"}, + {file = "mypy-1.10.0.tar.gz", hash = "sha256:3d087fcbec056c4ee34974da493a826ce316947485cef3901f511848e687c131"}, +] + +[package.dependencies] +mypy-extensions = ">=1.0.0" +typing-extensions = ">=4.1.0" + +[package.extras] +dmypy = ["psutil (>=4.0)"] +install-types = ["pip"] +mypyc = ["setuptools (>=50)"] +reports = ["lxml"] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." +optional = false +python-versions = ">=3.5" +files = [ + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, +] + +[[package]] +name = "numpy" +version = "1.26.4" +description = "Fundamental package for array computing in Python" +optional = false +python-versions = ">=3.9" +files = [ + {file = "numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0"}, + {file = "numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a"}, + {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4"}, + {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f"}, + {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a"}, + {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2"}, + {file = "numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07"}, + {file = "numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5"}, + {file = "numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71"}, + {file = "numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef"}, + {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e"}, + {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5"}, + {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a"}, + {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a"}, + {file = "numpy-1.26.4-cp311-cp311-win32.whl", hash = "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20"}, + {file = "numpy-1.26.4-cp311-cp311-win_amd64.whl", hash = "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2"}, + {file = "numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218"}, + {file = "numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b"}, + {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b"}, + {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed"}, + {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a"}, + {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0"}, + {file = "numpy-1.26.4-cp312-cp312-win32.whl", hash = "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110"}, + {file = "numpy-1.26.4-cp312-cp312-win_amd64.whl", hash = "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818"}, + {file = "numpy-1.26.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7349ab0fa0c429c82442a27a9673fc802ffdb7c7775fad780226cb234965e53c"}, + {file = "numpy-1.26.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:52b8b60467cd7dd1e9ed082188b4e6bb35aa5cdd01777621a1658910745b90be"}, + {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5241e0a80d808d70546c697135da2c613f30e28251ff8307eb72ba696945764"}, + {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f870204a840a60da0b12273ef34f7051e98c3b5961b61b0c2c1be6dfd64fbcd3"}, + {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:679b0076f67ecc0138fd2ede3a8fd196dddc2ad3254069bcb9faf9a79b1cebcd"}, + {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:47711010ad8555514b434df65f7d7b076bb8261df1ca9bb78f53d3b2db02e95c"}, + {file = "numpy-1.26.4-cp39-cp39-win32.whl", hash = "sha256:a354325ee03388678242a4d7ebcd08b5c727033fcff3b2f536aea978e15ee9e6"}, + {file = "numpy-1.26.4-cp39-cp39-win_amd64.whl", hash = "sha256:3373d5d70a5fe74a2c1bb6d2cfd9609ecf686d47a2d7b1d37a8f3b6bf6003aea"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:afedb719a9dcfc7eaf2287b839d8198e06dcd4cb5d276a3df279231138e83d30"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95a7476c59002f2f6c590b9b7b998306fba6a5aa646b1e22ddfeaf8f78c3a29c"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7e50d0a0cc3189f9cb0aeb3a6a6af18c16f59f004b866cd2be1c14b36134a4a0"}, + {file = "numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010"}, +] + +[[package]] +name = "ruff" +version = "0.4.2" +description = "An extremely fast Python linter and code formatter, written in Rust." +optional = false +python-versions = ">=3.7" +files = [ + {file = "ruff-0.4.2-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:8d14dc8953f8af7e003a485ef560bbefa5f8cc1ad994eebb5b12136049bbccc5"}, + {file = "ruff-0.4.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:24016ed18db3dc9786af103ff49c03bdf408ea253f3cb9e3638f39ac9cf2d483"}, + {file = "ruff-0.4.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e2e06459042ac841ed510196c350ba35a9b24a643e23db60d79b2db92af0c2b"}, + {file = "ruff-0.4.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3afabaf7ba8e9c485a14ad8f4122feff6b2b93cc53cd4dad2fd24ae35112d5c5"}, + {file = "ruff-0.4.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:799eb468ea6bc54b95527143a4ceaf970d5aa3613050c6cff54c85fda3fde480"}, + {file = "ruff-0.4.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:ec4ba9436a51527fb6931a8839af4c36a5481f8c19e8f5e42c2f7ad3a49f5069"}, + {file = "ruff-0.4.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6a2243f8f434e487c2a010c7252150b1fdf019035130f41b77626f5655c9ca22"}, + {file = "ruff-0.4.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8772130a063f3eebdf7095da00c0b9898bd1774c43b336272c3e98667d4fb8fa"}, + {file = "ruff-0.4.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ab165ef5d72392b4ebb85a8b0fbd321f69832a632e07a74794c0e598e7a8376"}, + {file = "ruff-0.4.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:1f32cadf44c2020e75e0c56c3408ed1d32c024766bd41aedef92aa3ca28eef68"}, + {file = "ruff-0.4.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:22e306bf15e09af45ca812bc42fa59b628646fa7c26072555f278994890bc7ac"}, + {file = "ruff-0.4.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:82986bb77ad83a1719c90b9528a9dd663c9206f7c0ab69282af8223566a0c34e"}, + {file = "ruff-0.4.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:652e4ba553e421a6dc2a6d4868bc3b3881311702633eb3672f9f244ded8908cd"}, + {file = "ruff-0.4.2-py3-none-win32.whl", hash = "sha256:7891ee376770ac094da3ad40c116258a381b86c7352552788377c6eb16d784fe"}, + {file = "ruff-0.4.2-py3-none-win_amd64.whl", hash = "sha256:5ec481661fb2fd88a5d6cf1f83403d388ec90f9daaa36e40e2c003de66751798"}, + {file = "ruff-0.4.2-py3-none-win_arm64.whl", hash = "sha256:cbd1e87c71bca14792948c4ccb51ee61c3296e164019d2d484f3eaa2d360dfaf"}, + {file = "ruff-0.4.2.tar.gz", hash = "sha256:33bcc160aee2520664bc0859cfeaebc84bb7323becff3f303b8f1f2d81cb4edc"}, +] + +[[package]] +name = "typing-extensions" +version = "4.11.0" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.11.0-py3-none-any.whl", hash = "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a"}, + {file = "typing_extensions-4.11.0.tar.gz", hash = "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0"}, +] + +[metadata] +lock-version = "2.0" +python-versions = "^3.11" +content-hash = "d42c1045a4073be06cbdd6380b01cb3da87f4a970e2f7dd5128652d96f79481c" diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..59e3842 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,204 @@ +[tool.poetry] +name = "read-aconity-layers" +version = "0.1.0" +description = "A utility for fast reading of layer data from the aconity mini powder bed fusion machine" +authors = ["Cian Hughes "] +license = "MIT" +readme = "README.md" + +[tool.poetry.dependencies] +python = "^3.11" +maturin = "^1.5.1" +numpy = "^1.26.4" + +[tool.poetry.group.dev.dependencies] +ruff = "^0.4.2" +mypy = "^1.10.0" + +[build-system] +requires = ["poetry-core"] +build-backend = "mason" + +[tool.maturin] +# Bindings type +bindings = "pyo3" +# Don't check for manylinux compliance +skip-auditwheel = false +# Python source directory +# python-source = "src/mtpy" +# Strip the library for minimum file size +strip = true +# Source distribution generator, +# supports cargo (default) and git. +sdist-generator = "cargo" + +[tool.ruff] +# Same as Black. +line-length = 100 +# Assume Python 3.11 +target-version = "py311" +exclude = ["docs/", "tests/"] + +[tool.ruff.lint] +# Configure ruff to be *heavily* opinionated. We want to enforce a consistent style across all code. +# Enable checks for pydocstyle (`D`), pycodestyle (`E`, `W`), Pyflakes (`F`), McCabe Complexity (C90) +# isort (`I`), pep8 naming (`N`), flake8 (`A`, `ANN`, `B`, `C4`, `EM`, `FBT`, `ICN`, `INT`, `ISC`, `PT`, +# `PTH`, `RET`, `SIM`, `TCH`, and `TID`), perflint (`PERF`), numpy rules (`NPY`), pandas +# rules (`PD`), pylint (`PL`), ruff rules (`RUF`). +select = [ + "D", + "E", + "W", + "F", + "C90", + "I", + "N", + "A", + "ANN", + "B", + "C4", + "EM", + "FBT", + "ICN", + "INT", + "ISC", + "PT", + "PTH", + "RET", + "SIM", + "TCH", + "TID", + "PERF", + "NPY", + "PD", + "PL", + "RUF", +] +# Reasons for disabling certain rules: +# - PD002: despite its problems we need to use inplace operations for performance reasons. +# - ANN002: as much as i'd like to enforce this using `TypedDict`s and a `__kwargs__` +# dunder, it would tightly couple us to external libraries. +# - ANN003: same as above. +# - PLR0913: as nice as smaller functions are for maintenance and readability, this rule +# is unenforceable in such a data-heavy library. +# - PLR0914: same as above. +# - PLR0917: same as above. +# - TCH001: the `TYPE_CHECKING` blocks either don't work or i'm too stupid to get them to work. +# - TCH002: same as above. +# - TCH003: same as above. +# - ISC001: Personally, i really like this rule but apparently it can cause issues with the ruff formatter. +ignore = [ + "PD002", + "ANN002", + "ANN003", + "PLR0913", + # "PLR0914", + # "PLR0917", + "TCH001", + "TCH002", + "TCH003", + "ISC001", +] +# Allow autofix for all enabled rules (when `--fix`) is provided. +fixable = [ + "A", + "B", + "C", + "D", + "E", + "F", + "G", + "I", + "N", + "Q", + "S", + "T", + "W", + "ANN", + "ARG", + "BLE", + "COM", + "DJ", + "DTZ", + "EM", + "ERA", + "EXE", + "FBT", + "ICN", + "INP", + "ISC", + "NPY", + "PD", + "PGH", + "PIE", + "PL", + "PT", + "PTH", + "PYI", + "RET", + "RSE", + "RUF", + "SIM", + "SLF", + "TCH", + "TID", + "TRY", + "UP", + "YTT", +] +unfixable = [] +exclude = [ + ".bzr", + ".direnv", + ".eggs", + ".git", + ".hg", + ".mypy_cache", + ".nox", + ".pants.d", + ".pytype", + ".ruff_cache", + ".svn", + ".tox", + ".venv", + "__pypackages__", + "_build", + "buck-out", + "build", + "dist", + "node_modules", + "venv", + ".vscode", + "docs", +] +# Allow unused variables when underscore-prefixed. +dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" +extend-select = ["I"] + +[tool.ruff.lint.isort] +force-sort-within-sections = true +lines-after-imports = -1 + +[tool.ruff.lint.pydocstyle] +convention = "google" + +[tool.ruff.format] +quote-style = "double" +indent-style = "space" +line-ending = "auto" + +[tool.ruff.lint.mccabe] +# Unlike Flake8, default to a complexity level of 10. +max-complexity = 10 + +[tool.ruff.lint.flake8-annotations] +mypy-init-return = true + +[tool.mypy] +check_untyped_defs = true +ignore_missing_imports = true +exclude = ["docs/", "tests/"] + +[[tool.mypy.overrides]] +module = ["flet.*", "flet_core.*", "fsspec.*"] +ignore_missing_imports = true diff --git a/read_layers.pyi b/read_layers.pyi new file mode 100644 index 0000000..be105cb --- /dev/null +++ b/read_layers.pyi @@ -0,0 +1,11 @@ +from pathlib import Path +from typing import List, Tuple + +from numpy import ndarray + +def read_selected_layers(file_list: List[Path]) -> ndarray: ... +def read_layer(file: str) -> ndarray: ... +def read_file(filepath: Path) -> Tuple[ndarray, float, int]: ... +def get_z(filepath: Path) -> float: ... +def correct_x(x: float) -> None: ... +def correct_y(y: float) -> None: ... diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..bc15202 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,195 @@ +use numpy::{IntoPyArray, PyArray2}; +use pyo3::prelude::{pymodule, PyModule, PyResult, Python}; +use pyo3::types::{PyList, PyString}; +use std::path::Path; + +#[pymodule] +fn read_aconity_layers(_py: Python<'_>, m: &PyModule) -> PyResult<()> { + #[pyfn(m)] + fn read_layers<'py>(py: Python<'py>, folder: &PyString) -> &'py PyArray2 { + rust_fn::read_layers(folder.to_str().unwrap()).into_pyarray(py) + } + + #[pyfn(m)] + fn read_selected_layers<'py>(py: Python<'py>, file_list: &PyList) -> &'py PyArray2 { + rust_fn::read_selected_layers( + file_list + .iter() + .map(|x| Path::new(&(*x).str().unwrap().to_string()).to_path_buf()) + .collect(), + ) + .into_pyarray(py) + } + + #[pyfn(m)] + fn read_layer<'py>(py: Python<'py>, file: &PyString) -> &'py PyArray2 { + rust_fn::read_layer(file.to_str().unwrap()).into_pyarray(py) + } + + Ok(()) +} + +mod rust_fn { + use csv::ReaderBuilder; + use glob::{glob, GlobError}; + use indicatif::ProgressBar; + use ndarray::{concatenate, Array2, ArrayView2, Axis, Slice}; + use ndarray_csv::Array2Reader; + use rayon::prelude::*; + use std::error::Error; + use std::fs::File; + use std::path::{Path, PathBuf}; + + pub fn read_layers(folder: &str) -> Array2 { + let glob_string: String = folder.to_owned() + "/*.pcd"; + let mut glob_iterator: Vec = glob(glob_string.as_str()) + .expect("Files not found!") + .collect::, GlobError>>() + .unwrap(); + glob_iterator.par_sort_unstable_by(|a, b| get_z(a).partial_cmp(&get_z(b)).unwrap()); + let len: usize = glob_iterator.len(); + let bar = ProgressBar::new(len as u64); + let mut arrays: Vec> = vec![Array2::::zeros((0, 0)); len]; + let mut z_vals: Vec = vec![0.; len]; + let mut z_lens: Vec = vec![0; len]; + glob_iterator + .par_iter() + .zip(arrays.par_iter_mut()) + .zip(z_vals.par_iter_mut()) + .zip(z_lens.par_iter_mut()) + .for_each( + |(((filepath, array_element), z_vals_element), z_lens_element)| { + let (array, z, z_len) = read_file(filepath.to_path_buf()).unwrap(); + *array_element = array; + *z_vals_element = z; + *z_lens_element = z_len; + bar.inc(1) + }, + ); + + let mut padding_arrays: Vec> = Vec::>::new(); + for (z, z_len) in z_vals.iter().zip(z_lens) { + let z_array: Array2 = Array2::from_elem((z_len, 1), *z); + padding_arrays.push(z_array); + } + + let padding_array_views: Vec> = + padding_arrays.iter().map(|x| x.view()).collect(); + let array_views: Vec> = arrays.iter().map(|x| x.view()).collect(); + + let mut out_array = concatenate( + Axis(1), + &[ + concatenate(Axis(0), &array_views) + .unwrap() + .slice_axis(Axis(1), Slice::from(0..2)), + concatenate(Axis(0), &padding_array_views).unwrap().view(), + concatenate(Axis(0), &array_views) + .unwrap() + .slice_axis(Axis(1), Slice::from(2..4)), + ], + ) + .unwrap(); + + out_array.column_mut(0).par_map_inplace(correct_x); + out_array.column_mut(1).par_map_inplace(correct_y); + + out_array + } + + pub fn read_selected_layers(file_list: Vec) -> Array2 { + let len: usize = file_list.len(); + let bar = ProgressBar::new(len as u64); + let mut arrays: Vec> = vec![Array2::::zeros((0, 0)); len]; + let mut z_vals: Vec = vec![0.; len]; + let mut z_lens: Vec = vec![0; len]; + file_list + .par_iter() + .zip(arrays.par_iter_mut()) + .zip(z_vals.par_iter_mut()) + .zip(z_lens.par_iter_mut()) + .for_each( + |(((filepath, array_element), z_vals_element), z_lens_element)| { + let (array, z, z_len) = read_file(filepath.to_path_buf()).unwrap(); + *array_element = array; + *z_vals_element = z; + *z_lens_element = z_len; + bar.inc(1) + }, + ); + + let mut padding_arrays: Vec> = Vec::>::new(); + for (z, z_len) in z_vals.iter().zip(z_lens) { + let z_array: Array2 = Array2::from_elem((z_len, 1), *z); + padding_arrays.push(z_array); + } + + let padding_array_views: Vec> = + padding_arrays.iter().map(|x| x.view()).collect(); + let array_views: Vec> = arrays.iter().map(|x| x.view()).collect(); + + let mut out_array = concatenate( + Axis(1), + &[ + concatenate(Axis(0), &array_views) + .unwrap() + .slice_axis(Axis(1), Slice::from(0..2)), + concatenate(Axis(0), &padding_array_views).unwrap().view(), + concatenate(Axis(0), &array_views) + .unwrap() + .slice_axis(Axis(1), Slice::from(2..4)), + ], + ) + .unwrap(); + + out_array.column_mut(0).par_map_inplace(correct_x); + out_array.column_mut(1).par_map_inplace(correct_y); + + out_array + } + + pub fn read_layer(file: &str) -> Array2 { + let (array, z, z_len) = read_file(Path::new(file).to_path_buf()).unwrap(); + let z_array: Array2 = Array2::from_elem((z_len, 1), z); + let z_array_view: ArrayView2 = z_array.view(); + let array_view: ArrayView2 = array.view(); + + let mut out_array = concatenate(Axis(1), &[array_view, z_array_view]).unwrap(); + + out_array.column_mut(0).par_map_inplace(correct_x); + out_array.column_mut(1).par_map_inplace(correct_y); + + out_array + } + + fn read_file(filepath: PathBuf) -> Result<(Array2, f64, usize), Box> { + let z: f64 = get_z(&filepath); + let file = File::open(filepath)?; + let mut rdr = ReaderBuilder::new() + .has_headers(false) + .delimiter(b' ') + .from_reader(file); + let array_read: Array2 = rdr.deserialize_array2_dynamic()?; + let z_len: usize = array_read.shape()[0]; + + Ok((array_read, z, z_len)) + } + + fn get_z(filepath: &PathBuf) -> f64 { + filepath + .file_stem() + .unwrap() + .to_str() + .unwrap() + .parse::() + .unwrap() + } + + fn correct_x(x: &mut f64) -> () { + *x = -((((*x + 16384.) * 0.009155273) - 87.) / 1.01); + } + + fn correct_y(y: &mut f64) -> () { + *y = (((*y + 16384.) * 0.009155273) - 91.) / 1.02; + } +}