Repository: PetroFit/petrofit (language: python)

Issue:
readme badges
Some badges are missing from the readme:

- [x] A [repostatus.org](https://www.repostatus.org/) badge should be added to the readme file.
- [x] A badge listing supported python versions (automatically obtained from the repo metadata) should be added to the readme file.

This issue is part of my PyOpenSci review. For review pass/fail purposes, resolving it is required.
include example(s) in readme
The PyOpenSci guidelines specify that the readme should include: "Descriptive links to all vignettes. If the package is small, there may only be a need for one vignette which could be placed in the README.md file."

This seems perhaps a bit too stringent as a requirement, but either directly including a simple example or linking to a "quickstart guide" type page in the documentation would be a good idea.

This issue is part of my PyOpenSci review. For review pass/fail purposes, resolving it is required.


Discussion / hints:

Fix patch:
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..179dfba
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,20 @@
+# Editor Configuration (http://editorconfig.org)
+root = true
+
+[*]
+charset = utf-8
+indent_style = space
+indent_size = 4
+end_of_line = lf
+insert_final_newline = true
+trim_trailing_whitespace = true
+max_line_length = 88
+
+[*.{json,yml,yaml}]
+indent_size = 2
+
+[*.{md,rst}]
+trim_trailing_whitespace = false
+
+[Makefile]
+indent_style = tab
diff --git a/.github/ISSUE_TEMPLATE/01-bug-report.yml b/.github/ISSUE_TEMPLATE/01-bug-report.yml
new file mode 100644
index 0000000..9f772e5
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/01-bug-report.yml
@@ -0,0 +1,64 @@
+name: Bug report
+description: Report a problem to help improve this project
+title: "[BUG] "
+labels: [bug, triage]
+body:
+  - type: textarea
+    attributes:
+      label: Problem description
+      description: |
+        A concise description of what you're experiencing.
+
+        Please explain:
+
+          * **what** you tried to achieve,
+          * **how** you went about it (referring to the code sample), and
+          * **why** the current behaviour is a problem and what output you expected instead.
+    validations:
+      required: false
+
+  - type: textarea
+    attributes:
+      label: Code sample
+      description: >
+        Create a [minimal, complete, verifiable example](https://stackoverflow.com/help/mcve).
+        Please, paste your code between the ``` tickmarks below or link to a [gist](https://gist.github.com/).
+      value: |
+        Code run:
+
+        ```python
+
+
+        ```
+
+        Traceback:
+
+        ```text
+
+        
+        ```
+    validations:
+      required: false
+
+  - type: textarea
+    attributes:
+      label: Environment
+      description: >
+        Please paste the output of running `depinfo --markdown petrofit`
+        in your environment between the `details` tags below.
+      value: |
+        <details>
+
+        </details>
+    validations:
+      required: true
+
+  - type: textarea
+    attributes:
+      label: Anything else?
+      description: |
+        Links? References? Anything that will give us more context about the issue you are encountering!
+
+        Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.
+    validations:
+      required: false
diff --git a/.github/ISSUE_TEMPLATE/02-question.yml b/.github/ISSUE_TEMPLATE/02-question.yml
new file mode 100644
index 0000000..3c0abc2
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/02-question.yml
@@ -0,0 +1,11 @@
+name: Question
+description: Ask a question
+title: "[Question] "
+labels: [question]
+body:
+  - type: textarea
+    attributes:
+      label: Question
+      description: Please ask your question
+    validations:
+      required: true
diff --git a/.github/ISSUE_TEMPLATE/03-feature-request.yml b/.github/ISSUE_TEMPLATE/03-feature-request.yml
new file mode 100644
index 0000000..308eca2
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/03-feature-request.yml
@@ -0,0 +1,22 @@
+name: Feature request
+description: Suggest an idea for this project
+title: "[Feature] "
+labels: [enhancement]
+body:
+  - type: textarea
+    attributes:
+      label: Feature or Problem
+      description: >
+        A clear and concise description of what you are trying to achieve.
+      placeholder: >
+        "I want to be able to [...] but I can't because [...]".
+    validations:
+      required: false
+
+  - type: textarea
+    attributes:
+      label: Anything else?
+      description: >
+        Provide any additional context, screenshots, tracebacks, etc. about the feature here.
+    validations:
+      required: false
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 0000000..760b5b1
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1,5 @@
+blank_issues_enabled: true
+contact_links:
+  - name: Community Support
+    url: https://github.com/PetroFit/petrofit/discussions
+    about: Please ask and answer questions here.
diff --git a/.github/SUPPORT.md b/.github/SUPPORT.md
new file mode 100644
index 0000000..e282b83
--- /dev/null
+++ b/.github/SUPPORT.md
@@ -0,0 +1,4 @@
+# Support
+
+* Start a [discussion topic](https://github.com/PetroFit/petrofit/discussions)
+* Email the authors or maintainers
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
new file mode 100644
index 0000000..c12049f
--- /dev/null
+++ b/.github/workflows/main.yml
@@ -0,0 +1,13 @@
+name: CI
+
+on:
+  push:
+    branches:
+    - '*' 
+  pull_request:
+    branches:
+    - '*' 
+
+jobs:
+  prerequisites:
+    uses: ./.github/workflows/test.yml
diff --git a/.gitignore b/.gitignore
index d313431..29b494c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -164,3 +164,5 @@ pip-wheel-metadata/
 # Mac OSX
 .DS_Store
 
+# Hatch-VCS
+_version.py
diff --git a/.readthedocs.yaml b/.readthedocs.yaml
index 174a04b..09a31ca 100644
--- a/.readthedocs.yaml
+++ b/.readthedocs.yaml
@@ -5,21 +5,17 @@
 # Required
 version: 2
 
-build:
-  os: ubuntu-22.04
-  tools:
-    python: "mambaforge-4.10"
-
-conda:
-  environment: environment.yml
-
 sphinx:
   builder: html
   configuration: docs/conf.py  # This should point to your conf.py
 
-python:
-  install:
-    - method: pip
-      path: .
-      extra_requirements:
-        - docs
\ No newline at end of file
+build:
+  os: ubuntu-22.04
+  tools:
+    python: "3.12"
+  apt_packages:
+    - pandoc
+  commands:
+    - pip install --upgrade pip setuptools wheel
+    - pip install hatch
+    - hatch run docs:buildrtd
diff --git a/Dockerfile b/Dockerfile
deleted file mode 100644
index 24814d8..0000000
--- a/Dockerfile
+++ /dev/null
@@ -1,42 +0,0 @@
-#FROM jupyter/scipy-notebook:db3ee82ad08a
-FROM jupyter/scipy-notebook:4d9c9bd9ced0
-#FROM continuumio/miniconda3
-
-#########
-# Setup #
-#########
-
-ENV HOME /home/jovyan
-WORKDIR $HOME
-
-# Clone PetroFit repo
-# RUN git clone https://github.com/PetroFit/petrofit.git
-
-USER root
-COPY --chown=$NB_USER:users . $HOME/petrofit/
-USER $NB_USER
-
-ENV PETROFITDIR $HOME/petrofit
-
-########################################
-# Setup Conda and Install Requirements #
-########################################
-
-RUN conda env create -f $PETROFITDIR/environment.yml
-ENV PATH /opt/conda/envs/petrofit/bin:$PATH
-RUN echo "source activate petrofit" >> $HOME/.bashrc
-
-ENV CONDA_DEFAULT_ENV petrofit
-
-WORKDIR $HOME
-
-####################
-# Install PetroFit #
-####################
-
-WORKDIR $PETROFITDIR
-
-RUN python setup.py develop
-
-WORKDIR $HOME
-
diff --git a/README.rst b/README.rst
index 97fa86a..3e8c69d 100644
--- a/README.rst
+++ b/README.rst
@@ -4,7 +4,7 @@
 
 PetroFit
 --------
-|CI tag| |rtd tag| |PyPI tag| |AJ tag| |zonodo tag| |astropy tag| |photutils tag|
+|CI tag| |rtd tag| |PyPI tag| |AJ tag| |zonodo tag| |python version tag| |repostatus tag| |astropy tag| |photutils tag| 
 
 PetroFit is a package for calculating Petrosian properties, such as radii and concentration indices, as well as fitting
 galaxy light profiles. In particular, PetroFit includes tools for performing accurate photometry, segmentations,
@@ -16,9 +16,82 @@ for installation instructions and a guide to the ``petrofit`` module.
 
 Installation
 ------------
+
 You can install PetroFit using ``pip install petrofit``. Please see
 the `petrofit documentation <https://petrofit.readthedocs.io/en/latest/>`_ for detailed installation instructions.
 
+Examples
+--------
+
+Please see the `petrofit documentation <https://petrofit.readthedocs.io/en/latest/>`_
+for detailed examples, a quick stat guide, and instructions.
+
+**Image Fitting**: 
+
+PetroFit can be used with ``astropy`` models to fit psf convolved galaxy light profiles. Given a 2D image (``image``) and a psf (``PSF``), the following code snippet demonstrates how to fit a Sérsic model to the image:
+
+.. code-block:: python
+
+    import petrofit as pf
+    from astropy.modeling import models
+
+    sersic_model = models.Sersic2D(
+            amplitude=1,
+            r_eff=10,
+            n=4,
+            x_0=0, y_0=0,
+            ellip=0.2,
+            theta=25,
+            bounds=pf.get_default_sersic_bounds(),
+    )
+
+    psf_sersic_model = pf.PSFConvolvedModel2D(
+        sersic_model, psf=PSF, oversample=4, psf_oversample=1
+    )
+
+    fitted_model, fit_info = pf.fit_model(
+        image, psf_sersic_model,
+    )
+
+**Photometry and Petrosian**: 
+
+Given a 2D image (``image``) the following code snippet demonstrates how to create a Petrosian profile:
+
+.. code-block:: python
+
+    import petrofit as pf
+    
+    # Make a segmentation map and 
+    # catalog using Photutils wrapper
+    cat, segm, segm_deblend = pf.make_catalog(
+        image,
+        threshold=image.std()*3,
+        wcs=None, deblend=True,
+        npixels=npixels, nlevels=30, contrast=0.001,
+    )
+
+    # Photomerty on first source in catalog
+    r_list = pf.make_radius_list(max_pix=50, n=50)
+    flux_arr, area_arr, error_arr = pf.source_photometry(
+        cat[0], # Source (`photutils.segmentation.catalog.SourceCatalog`)
+        image, # Image as 2D array
+        segm_deblend, # Deblended segmentation map of image
+        r_list, # list of aperture radii
+        cutout_size=max(r_list)*2, # Cutout out size, set to double the max radius
+    )
+
+    # Make a Petrosian profile 
+    p = pf.Petrosian(r_list, area_arr, flux_arr)
+    print("{:0.4f} pix".format(p.r_half_light))
+
+
+Citation
+--------
+
+Please see the `petrofit documentation <https://petrofit.readthedocs.io/en/latest/>`_
+for citation instructions. This information is also available in the `CITATION.rst`` 
+file in the PetroFit repo.
+
 License
 -------
 
@@ -62,7 +135,6 @@ petrofit based on its use in the README file for the
 `MetPy project <https://github.com/Unidata/MetPy>`_.
 
 
-
 .. |CI tag| image:: https://github.com/PetroFit/petrofit/actions/workflows/ci_tests.yml/badge.svg?branch=main
     :target: https://github.com/PetroFit/petrofit/actions/workflows/ci_tests.yml
     :alt: PetroFit CI status
@@ -90,3 +162,11 @@ petrofit based on its use in the README file for the
 .. |zonodo tag| image:: http://img.shields.io/badge/zenodo-10.5281/zenodo.6386991-blue.svg?style=flat
     :target: https://zenodo.org/badge/latestdoi/348478663
     :alt: PetroFit Zenodo DOI
+
+.. |repostatus tag| image:: https://www.repostatus.org/badges/latest/active.svg
+   :alt: Project Status: Active – The project has reached a stable, usable state and is being actively developed.
+   :target: https://www.repostatus.org/#active
+
+.. |python version tag| image:: https://img.shields.io/python/required-version-toml?tomlFilePath=https%3A%2F%2Fraw.githubusercontent.com%2Fpetrofit%2Fpetrofit%2Fmain%2Fpyproject.toml
+   :alt: Python Version from PEP 621 TOML
+   :target: https://www.python.org/downloads/
diff --git a/docker.py b/docker.py
deleted file mode 100644
index 31d66d1..0000000
--- a/docker.py
+++ /dev/null
@@ -1,58 +0,0 @@
-import os
-from sys import argv
-
-
-def mount_bind_command(src, dst):
-    return "--mount type=bind,src={},dst={}".format(src, dst)
-
-
-if __name__ == '__main__':
-    if len(argv) < 2:
-        print('Command not supplied')
-        print('python docker.py build')
-        print('python docker.py run [extra_mount_paths]')
-        print('python docker.py run_safe # (does not mount the host copy of petrofit dir)')
-
-    # Build
-    # -----
-    # Command to build the Dockerfile
-    elif argv[1] == 'build' or argv[1].replace("-", "") == "b":
-        os.system('docker build -t petrofit .')
-
-    # Mount and Run
-    # -------------
-    # Command to run the docker image in a container.
-    # This will mount the host version of the repo so any changes to the code
-    # in the host repo will be available in the docker container and the environment.
-    # Thus the docker container will use the host's version of petrofit module since the
-    # environment in the container is installed using `python setup.py develop`.
-    # (you are able to update code on host without having to reinstall in the container)
-    elif argv[1] == 'run' or argv[1].replace("-", "") == "r":
-        path_to_this_dir = os.path.dirname(os.path.abspath(__file__))
-        command = "docker run"
-        command += " " + mount_bind_command(path_to_this_dir, "/home/jovyan/petrofit")
-
-        # This section mounts (bind) any number of volumes on the host into the docker container.
-        # The volumes are mounted into the `mount` folder inside the container.
-        # Any edits there will also apply to the host version of the files.
-        mounted_list = []
-        if len(argv) > 2:
-            for path in argv[2:]:
-                src = os.path.abspath(path)
-                dst = "/home/jovyan/mount/{}".format(os.path.basename(path))
-                if dst in mounted_list:
-                    raise Exception("Mount request includes multiple files or folders with the same name")
-                mounted_list.append(dst)
-                command += " " + mount_bind_command(src, dst)
-
-        command += " " + "-it -p 8888:8888 petrofit"
-
-        print(">>>", command, "\n")
-
-        os.system(command)
-
-    # Regular Run
-    # -----------
-    # Nothing is mounted on to the container.
-    elif argv[1] == 'run_safe':
-        os.system('docker run -it -p 8888:8888 petrofit')
diff --git a/docs/conf.py b/docs/conf.py
index 105cf18..69a63cb 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -13,11 +13,10 @@
     sys.exit(1)
 
 # Get configuration information from setup.cfg
-from configparser import ConfigParser
-conf = ConfigParser()
+import tomllib
 
-conf.read([os.path.join(os.path.dirname(__file__), '..', 'setup.cfg')])
-setup_cfg = dict(conf.items('metadata'))
+with open(os.path.join(os.path.dirname(__file__), '..', 'pyproject.toml'), "rb") as f:
+    setup_cfg = tomllib.load(f)
 
 # -- General configuration ----------------------------------------------------
 
@@ -36,11 +35,12 @@
 # -- Project information ------------------------------------------------------
 
 project = "PetroFit"
-author = setup_cfg['author']
+author = setup_cfg["project"]['authors'][0]['name']
+module_name = setup_cfg["project"]['name']
 copyright = '{0}, {1}'.format(
-    datetime.datetime.now().year, setup_cfg['author'])
-import_module(setup_cfg['name'])
-package = sys.modules[setup_cfg['name']]
+    datetime.datetime.now().year, author)
+import_module(module_name)
+package = sys.modules[module_name]
 release = package.__version__
 version = release
 if "dev" in release:
@@ -70,18 +70,9 @@
 man_pages = [('index', project.lower(), project + u' Documentation',
               [author], 1)]
 
-# -- Options for the edit_on_github extension ---------------------------------
-
-if setup_cfg.get('edit_on_github').lower() == 'true':
-    extensions += ['sphinx_astropy.ext.edit_on_github']
-    edit_on_github_project = setup_cfg['github_project']
-    edit_on_github_branch = "main"
-    edit_on_github_source_root = ""
-    edit_on_github_doc_root = "docs"
-
 # -- Resolving issue number to links in changelog -----------------------------
 
-github_issues_url = 'https://github.com/{0}/issues/'.format(setup_cfg['github_project'])
+github_issues_url = setup_cfg['project']['urls']['Bug Tracker']
 
 
 # -- Options for linkcheck output -------------------------------------------
diff --git a/environment.yml b/environment.yml
index 8289697..1f5d795 100644
--- a/environment.yml
+++ b/environment.yml
@@ -31,5 +31,6 @@ dependencies:
   - pip
   - python>=3.12
   - Pandoc
+  - scikit-image
   - pip:
-    - -r requirements.txt
+    - -e .
diff --git a/petrofit/__init__.py b/petrofit/__init__.py
index 5e86a8c..035b2cf 100644
--- a/petrofit/__init__.py
+++ b/petrofit/__init__.py
@@ -1,10 +1,8 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst
-
-# Packages may add whatever they like to this file, but
-# should keep this content at the top.
-# ----------------------------------------------------------------------------
-from ._astropy_init import *   # noqa
-# ----------------------------------------------------------------------------
+try:
+    from ._version import __version__
+except ImportError:
+    __version__ = "N.A"
 
 from .petrosian import *
 from .photometry import *
diff --git a/petrofit/_astropy_init.py b/petrofit/_astropy_init.py
deleted file mode 100644
index 26a39e1..0000000
--- a/petrofit/_astropy_init.py
+++ /dev/null
@@ -1,33 +0,0 @@
-# Licensed under a 3-clause BSD style license - see LICENSE.rst
-
-__all__ = ['__version__', '__githash__']
-
-# this indicates whether or not we are in the package's setup.py
-try:
-    _ASTROPY_SETUP_
-except NameError:
-    from sys import version_info
-    if version_info[0] >= 3:
-        import builtins
-    else:
-        import __builtin__ as builtins
-    builtins._ASTROPY_SETUP_ = False
-
-try:
-    from .version import version as __version__
-except ImportError:
-    __version__ = ''
-try:
-    from .version import githash as __githash__
-except ImportError:
-    __githash__ = ''
-
-
-if not _ASTROPY_SETUP_:  # noqa
-    import os
-
-    # Create the test function for self test
-    from astropy.tests.runner import TestRunner
-    test = TestRunner.make_test_runner_in(os.path.dirname(__file__))
-    test.__test__ = False
-    __all__ += ['test']
\ No newline at end of file
diff --git a/petrofit/data/README.rst b/petrofit/data/README.rst
deleted file mode 100644
index 382f6e7..0000000
--- a/petrofit/data/README.rst
+++ /dev/null
@@ -1,6 +0,0 @@
-Data directory
-==============
-
-This directory contains data files included with the package source
-code distribution. Note that this is intended only for relatively small files
-- large files should be externally hosted and downloaded as needed.
diff --git a/pyproject.toml b/pyproject.toml
index 7729065..47cb0b4 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,7 +1,271 @@
+# [build-system]
+
+# requires = ["setuptools>=72",
+#             "setuptools_scm",
+#             "wheel"]
+
+# build-backend = 'setuptools.build_meta'
+
+
+################################################################################
+# Build Configuration
+################################################################################
+
 [build-system]
+build-backend = "hatchling.build"
+requires = ["hatchling", "hatch-vcs", "setuptools", "setuptools_scm", "wheel"]
+
+################################################################################
+# Project Configuration
+################################################################################
+
+[project]
+name = "petrofit"
+
+dynamic = ["version"]
+
+description = "Python package for calculating Petrosian properties and fitting galaxy light profiles."
+authors = [
+    { name = "The PetroFit Team"},
+]
+license = "BSD-3-Clause"
+readme = {"file" = "README.rst", "content-type" = "text/x-rst"}
+requires-python = ">=3.12"
+# Please consult https://pypi.org/classifiers/ for a full list.
+classifiers = [
+    "Development Status :: 3 - Alpha",
+    "Intended Audience :: Science/Research",
+    "License :: OSI Approved :: BSD License",
+    "Natural Language :: English",
+    "Operating System :: OS Independent",
+    "Programming Language :: Python :: 3 :: Only",
+    "Programming Language :: Python :: 3.12",
+    "Programming Language :: Python :: 3.13",
+    "Topic :: Scientific/Engineering",
+]
+
+keywords = ["fitting", "galaxy", "light", "profile", "petrosian", "photometry"]
+dependencies = [
+    # Core Dependencies
+    "pyyaml",
+    'Cython',
+    'numpy>=1.13',
+    'scipy',
+    'matplotlib>=3.1.1',
+    'astropy>=5.2',
+    'jupyter',
+    'notebook',
+    'regions',
+    'photutils>=1.13',
+    'ipywidgets',
+    'scikit-image',
+    'scikit-learn',
+    'extension_helpers',
+    'pytest-astropy-header',
+]
+
+[project.urls]
+Homepage = "https://github.com/PetroFit/petrofit"
+"Source Code" = "https://github.com/PetroFit/petrofit"
+"Bug Tracker" = "https://github.com/PetroFit/petrofit/issues"
+Documentation = "https://petrofit.readthedocs.io"
+Download = "https://pypi.org/project/petrofit/#files"
+
+[project.optional-dependencies]
+dev = [
+    "hatch",
+]
+
+################################################################################
+# Tool Configuration
+################################################################################
+[tool.setuptools_scm]
+write_to = "petrofit/_version.py"
+
+[tool.hatch.build]
+only-packages = true
+
+[tool.hatch.build.targets.wheel]
+packages = ["petrofit"]
+[tool.hatch.build.hooks.vcs]
+version-file = "petrofit/_version.py"
+
+[tool.hatch.version]
+source = "vcs"
+[tool.pytest.ini_options]
+testpaths = ["petrofit"]
+markers = ["raises"]
+astropy_header = true
+doctest_plus = "enabled"
+text_file_format = "rst"
+
+[tool.coverage.paths]
+source = [
+    "petrofit",
+    "*/site-packages/petrofit",
+]
+
+[tool.coverage.run]
+branch = true
+parallel = true
+omit = [
+    "petrofit/_version.py",
+]
+
+[tool.coverage.report]
+exclude_lines = ["pragma: no cover"]
+precision = 2
+[tool.ruff]
+line-length = 88
+
+[tool.ruff.lint]
+select = ["ALL"]
+ignore = [
+    "D107",  # 'Missing docstring in __init__' ignored because pydoclint wants us to document the class instead.
+    "D203",  # '1 blank line required before class docstring' ignored because we want no blank line.
+    "D212",  # 'Multi-line docstring summary should start at the first line' ignored because we want the summary to start on the second line.
+    "D407",  # 'Missing dashed underline after section' ignored because Google style docstrings don't underline.
+    "ANN001",  # Ignore missing type annotations for arguments
+    "ANN101",  # Ignore missing type annotation for `self` in methods
+    "ANN201",  # Ignore missing type annotation for return values
+    "ANN002",  # 'Missing type annotation for {*args} in method'.
+    "ANN003",  # 'Missing type annotation for {*kwargs} in method'.
+    "ANN101",  # 'Missing type annotation for {self} in method'.
+    "ANN102",  # 'Missing type annotation for {cls} in classmethod'.
+    "Q000", # 'Remove bad quotes' ignored because we want to use double quotes.
+    "FBT002",  # Boolean default positional argument in function definition"
+
+]
+exclude = [
+    "petrofit/_version.py",
+    "docs/conf.py",
+]
+
+[tool.ruff.lint.extend-per-file-ignores]
+"__init__.py" = [
+    "E401",  # 'Multiple imports on one line'
+    "E402",  # 'Module level import not at top of file'
+    "F401",  # 'Imported but unused'
+    "I001",  # 'Import block is un-sorted or un-formatted' ignored because we may have to import in a particular, not-alphabetical order.
+]
+"tests/**/*.py" = [
+    "S101",  # 'Use of assert detected' ignored because we are using pytest.
+    "INP001",  # 'Insecure input' ignored because we are testing.
+    "ANN201",  # 'Missing type annotation for {return}' ignored because all tests return `None`.
+]
+
+[tool.ruff.lint.isort]
+case-sensitive = true
+known-first-party = ["src", "petrofit"]
+lines-after-imports = 1
+
+[tool.pydoclint]
+style = "numpy"
+arg-type-hints-in-docstring = false
+check-return-types = false
+check-yield-types = false
+exclude = "_version.py"
+# TODO: Adjust mypy configuration.
+#[tool.mypy]
+#plugins = [
+#    "pydantic.mypy",
+#]
+
+# Stop mypy from complaining about missing types from imports.
+#[[tool.mypy.overrides]]
+#module = [
+#    "pandas",
+#]
+#ignore_missing_imports = true
+
+#[tool.pydantic-mypy]
+#init_forbid_extra = true
+#init_typed = true
+#warn_required_dynamic_aliases = true
+################################################################################
+# Hatch Environments
+################################################################################
+[tool.hatch.envs.style]
+description = """Check the style of the codebase."""
+dependencies = [
+    "pydoclint",
+    "ruff",
+]
+detached = true
+
+[tool.hatch.envs.style.scripts]
+docstrings = "pydoclint"
+code = "ruff check {args}"
+format = "ruff format {args}"
+check = ["docstrings", "code"]
+
+[tool.hatch.envs.audit]
+description = """Check dependencies for security vulnerabilities."""
+extra-dependencies = [
+    "pip-audit",
+]
+
+[tool.hatch.envs.audit.scripts]
+check = ["pip-audit"]
+[tool.hatch.envs.types]
+description = """Check the static types of the codebase."""
+dependencies = [
+    "mypy",
+]
+
+[tool.hatch.envs.types.scripts]
+check = "mypy petrofit"
+
+[tool.hatch.envs.docs]
+description = """Build or serve the documentation."""
+dependencies = [
+    "pypandoc",
+    "pydata_sphinx_theme ~=0.16",
+    "myst-parser ~=4.0",
+    "Sphinx ~=8.0",
+    "sphinx-autobuild ==2024.10.3",
+    "sphinx-astropy",
+    "ipykernel",
+    "nbsphinx",
+    "sphinx_rtd_theme",
+    "ipywidgets",
+    'docutils',
+    'nbconvert',
+    'sphinx_astropy',
+    'sphinx-automodapi',
+]
+
+[tool.hatch.envs.docs.scripts]
+build = ["sphinx-build {args:-W -b html docs docs/_build}"]
+serve = ["sphinx-autobuild docs --watch petrofit {args:-b html docs/_build/serve}"]
+buildrtd = ["sphinx-build {args:-W -b html docs $READTHEDOCS_OUTPUT/html/}"]
+
+[tool.hatch.envs.install]
+description = """Test the installation the package."""
+dependencies = [
+    "pip",
+    "twine",
+]
+detached = true
+
+[tool.hatch.envs.install.scripts]
+check = [
+    "pip check",
+    "hatch build {args:--clean}",
+    "twine check dist/*",
+]
+[tool.hatch.envs.test]
+description = """Run the test suite."""
+extra-dependencies = [
+    "pytest",
+    "pytest-cov",
+    "pytest-raises",
+    "pytest-randomly",
+    "pytest-xdist",
+]
 
-requires = ["setuptools>=72",
-            "setuptools_scm",
-            "wheel"]
+[[tool.hatch.envs.test.matrix]]
+python = ["3.12", "3.13"]
 
-build-backend = 'setuptools.build_meta'
+[tool.hatch.envs.test.scripts]
+run = "pytest {args:--cov=petrofit --cov-report=term-missing}"
diff --git a/requirements.txt b/requirements.txt
deleted file mode 100644
index 7826ca1..0000000
--- a/requirements.txt
+++ /dev/null
@@ -1,27 +0,0 @@
-# Core Dependencies
-Cython
-numpy>=1.13
-scipy
-matplotlib>=3.1.1
-astropy>=5.2
-jupyter
-notebook
-regions
-photutils>=1.13
-ipywidgets
-scikit-image
-scikit-learn
-extension_helpers
-
-# docs
-sphinx
-docutils
-nbsphinx
-nbconvert
-sphinx_astropy
-sphinx_rtd_theme
-sphinx-automodapi
-
-# Tests
-tox
-pytest-astropy-header
diff --git a/setup.cfg b/setup.cfg
deleted file mode 100644
index 3bcea99..0000000
--- a/setup.cfg
+++ /dev/null
@@ -1,82 +0,0 @@
-[metadata]
-name = petrofit
-author = The PetroFit Team
-author_email = 
-license = BSD 3-Clause
-license_file = licenses/LICENSE.rst
-url = https://github.com/PetroFit/petrofit
-description = Petrosian library
-long_description = file: README.rst
-long_description_content_type = text/x-rst
-edit_on_github = False
-github_project = PetroFit/petrofit
-
-[options]
-zip_safe = False
-packages = find:
-python_requires = >=3.12
-setup_requires = setuptools_scm
-install_requires =
-    Cython
-    numpy
-    matplotlib
-    astropy
-    scipy
-    pyyaml
-    scikit-learn
-    scikit-image
-    photutils
-
-[options.extras_require]
-test =
-    pytest-astropy
-docs =
-    sphinx
-    sphinx-astropy
-    ipykernel
-    nbsphinx
-    sphinx_rtd_theme
-    ipywidgets
-
-[options.package_data]
-petrofit = data/*
-
-[tool:pytest]
-testpaths = "petrofit" "docs"
-astropy_header = true
-doctest_plus = enabled
-text_file_format = rst
-addopts = --doctest-rst
-
-[coverage:run]
-omit =
-    petrofit/_astropy_init*
-    petrofit/conftest.py
-    petrofit/*setup_package*
-    petrofit/tests/*
-    petrofit/*/tests/*
-    petrofit/extern/*
-    petrofit/version*
-    */petrofit/_astropy_init*
-    */petrofit/conftest.py
-    */petrofit/*setup_package*
-    */petrofit/tests/*
-    */petrofit/*/tests/*
-    */petrofit/extern/*
-    */petrofit/version*
-
-[coverage:report]
-exclude_lines =
-    # Have to re-enable the standard pragma
-    pragma: no cover
-    # Don't complain about packages we have installed
-    except ImportError
-    # Don't complain if tests don't hit assertions
-    raise AssertionError
-    raise NotImplementedError
-    # Don't complain about script hooks
-    def main\(.*\):
-    # Ignore branches that don't pertain to this version of Python
-    pragma: py{ignore_python_version}
-    # Don't complain about IPython completion helper
-    def _ipython_key_completions_
diff --git a/setup.py b/setup.py
deleted file mode 100644
index bd39be3..0000000
--- a/setup.py
+++ /dev/null
@@ -1,82 +0,0 @@
-#!/usr/bin/env python
-# Licensed under a 3-clause BSD style license - see LICENSE.rst
-
-# NOTE: The configuration for the package, including the name, version, and
-# other information are set in the setup.cfg file.
-
-import os
-import sys
-
-from setuptools import setup
-
-
-
-# First provide helpful messages if contributors try and run legacy commands
-# for tests or docs.
-
-TEST_HELP = """
-Note: running tests is no longer done using 'python setup.py test'. Instead
-you will need to run:
-
-    tox -e test
-
-If you don't already have tox installed, you can install it with:
-
-    pip install tox
-
-If you only want to run part of the test suite, you can also use pytest
-directly with::
-
-    pip install -e .[test]
-    pytest
-
-For more information, see:
-
-  http://docs.astropy.org/en/latest/development/testguide.html#running-tests
-"""
-
-if 'test' in sys.argv:
-    print(TEST_HELP)
-    sys.exit(1)
-
-DOCS_HELP = """
-Note: building the documentation is no longer done using
-'python setup.py build_docs'. Instead you will need to run:
-
-    tox -e build_docs
-
-If you don't already have tox installed, you can install it with:
-
-    pip install tox
-
-You can also build the documentation with Sphinx directly using::
-
-    pip install -e .[docs]
-    cd docs
-    make html
-
-For more information, see:
-
-  http://docs.astropy.org/en/latest/install.html#builddocs
-"""
-
-if 'build_docs' in sys.argv or 'build_sphinx' in sys.argv:
-    print(DOCS_HELP)
-    sys.exit(1)
-
-VERSION_TEMPLATE = """
-# Note that we need to fall back to the hard-coded version if either
-# setuptools_scm can't be imported or setuptools_scm can't determine the
-# version, so we catch the generic 'Exception'.
-try:
-    from setuptools_scm import get_version
-    version = get_version(root='..', relative_to=__file__)
-except Exception:
-    version = '{version}'
-""".lstrip()
-
-setup(use_scm_version={'write_to': os.path.join('petrofit', 'version.py'),
-                       'write_to_template': VERSION_TEMPLATE},
-include_package_data=True
-
-      )
diff --git a/tox.ini b/tox.ini
deleted file mode 100644
index e320abd..0000000
--- a/tox.ini
+++ /dev/null
@@ -1,74 +0,0 @@
-[tox]
-envlist =
-    py{312}-test{-alldeps}
-    py{312}-test-numpy{2}
-    py{312}-test-astropy{61}
-    build_docs
-    linkcheck
-
-requires =
-    setuptools >= 72
-    pip >= 24.0.0
-
-isolated_build = true
-
-indexserver =
-    NIGHTLY = https://pypi.anaconda.org/scipy-wheels-nightly/simple
-
-[testenv]
-
-# Suppress display of matplotlib plots generated during docs build
-setenv = MPLBACKEND=agg
-
-# Pass through the following environment variables which may be needed for the CI
-passenv = *
-
-# Run the tests in a temporary directory to make sure that we don't import
-# this package from the source tree
-changedir = .tmp/{envname}
-
-# tox environments are constructed with so-called 'factors' (or terms)
-# separated by hyphens, e.g. test-devdeps-cov. Lines below starting with factor:
-# will only take effect if that factor is included in the environment name. To
-# see a list of example environments that can be run, along with a description,
-# run:
-#
-#     tox -l -v
-#
-description =
-    run tests
-    alldeps: with all optional dependencies
-    numpy2: with numpy 2.0.*
-    astropy61: with astropy 6.1.*
-    astropylts: with the latest astropy LTS
-
-# The following provides some specific pinnings for key packages
-deps =
-    -r{toxinidir}/requirements.txt
-    numpy2: numpy==2.0.*
-    astropy61: astropy==6.1.*
-
-# The following indicates which extras_require from setup.cfg will be installed
-extras =
-    test
-    alldeps: all
-
-commands =
-    pip freeze
-    pytest --pyargs petrofit {toxinidir}/docs
-
-[testenv:build_docs]
-changedir = docs
-description = invoke sphinx-build to build the HTML docs
-extras = docs
-commands =
-    pip freeze
-    sphinx-build -W -b html . _build/html
-
-[testenv:linkcheck]
-changedir = docs
-description = check the links in the HTML docs
-extras = docs
-commands =
-    pip freeze
-    sphinx-build -W -b linkcheck . _build/html


Test patch:
diff --git a/.github/workflows/ci_tests.yml b/.github/workflows/ci_tests.yml
deleted file mode 100644
index f43d2b8..0000000
--- a/.github/workflows/ci_tests.yml
+++ /dev/null
@@ -1,61 +0,0 @@
-# This workflow will install Python dependencies, run tests and lint with a variety of Python versions
-# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python
-
-name: CI Tests
-
-on:
-  push:
-    branches:
-      - main
-    tags:
-      - '*'
-  pull_request:
-
-jobs:
-  tests:
-    name: ${{ matrix.prefix }} ${{ matrix.os }}, ${{ matrix.tox_env }}
-    runs-on: ${{ matrix.os }}
-    continue-on-error: false
-    strategy:
-      matrix:
-        include:
-          - os: ubuntu-latest
-            python: '3.12'
-            tox_env: 'py312-test-alldeps'
-            prefix: 'py312-test-alldeps'
-
-          - os: ubuntu-latest
-            python: '3.12'
-            tox_env: 'py312-test-astropy61'
-            prefix: 'py312-test-astropy61'
-
-          - os: ubuntu-latest
-            python: '3.12'
-            tox_env: 'py312-test-numpy2'
-            prefix: 'py312-test-numpy2'
-
-          - os: ubuntu-latest
-            python: '3.12'
-            tox_env: 'build_docs'
-            prefix: 'build_docs'
-            
-    steps:
-    - uses: actions/checkout@v3
-    - name: Set up Python ${{ matrix.python }}
-      uses: actions/setup-python@v3
-      with:
-        python-version: ${{ matrix.python }}
-    - name: Install Pandoc for build_docs
-      if: matrix.tox_env == 'build_docs'
-      run: sudo apt-get install -y pandoc
-    - name: Install dependencies
-      run: |
-        python -m pip install --upgrade pip
-        python -m pip install tox setuptools
-        python -m pip install --upgrade setuptools
-    - name: Test with Tox
-      run: |
-        export PY_COLORS=1
-        tox -e ${{ matrix.tox_env }}
-      env:
-        PY_COLORS: '1'
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
new file mode 100644
index 0000000..d40d307
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -0,0 +1,89 @@
+name: Test Suite
+
+on:
+  workflow_dispatch: {}
+  workflow_call: {}
+
+jobs:
+  lint:
+    runs-on: ${{ matrix.os }}
+    strategy:
+      fail-fast: false
+      matrix:
+        os: [ubuntu-latest]
+        python-version: ["3.12"]
+
+    steps:
+      - uses: actions/checkout@v4
+
+      - name: Set up Python ${{ matrix.python-version }}
+        uses: actions/setup-python@v5
+        with:
+          python-version: ${{ matrix.python-version }}
+
+      - name: Install hatch
+        uses: pypa/hatch@install
+
+      - name: Check code style
+        run: hatch run style:code
+        continue-on-error: true
+
+      - name: Check docstrings
+        run: hatch run style:docstrings
+        continue-on-error: true
+
+  docs:
+    runs-on: ${{ matrix.os }}
+    strategy:
+      fail-fast: false
+      matrix:
+        os: [ubuntu-latest]
+        python-version: ["3.12"]
+    steps:
+      - uses: actions/checkout@v4
+
+      - name: Set up Python ${{ matrix.python-version }}
+        uses: actions/setup-python@v5
+        with:
+          python-version: ${{ matrix.python-version }}
+          
+      - name: Install Pandoc
+        run: sudo apt-get install pandoc
+
+      - name: Install hatch
+        uses: pypa/hatch@install
+
+      - name: Build documentation
+        run: hatch run docs:build
+        
+  test:
+    runs-on: ${{ matrix.os }}
+    strategy:
+      fail-fast: false
+      matrix:
+        # os: [ubuntu-latest, macos-latest, windows-latest]
+        os: [ubuntu-latest]
+        python-version: ["3.12"]
+
+    steps:
+    - uses: actions/checkout@v4
+
+    - name: Set up Python ${{ matrix.python-version }}
+      uses: actions/setup-python@v5
+      with:
+        python-version: ${{ matrix.python-version }}
+    - name: Install hatch
+      uses: pypa/hatch@install
+
+    - name: Check installation
+      run: hatch run install:check
+
+    - name: Check dependencies
+      run: hatch run audit:check
+
+    - name: Test suite
+      run: hatch run +py=${{ matrix.python-version }} test:run
+
+    - name: Report coverage
+      shell: bash
+      run: bash <(curl -s https://codecov.io/bash)