Repository: adtzlr/contique (language: python)

Issue:
Outdated config files, missing DOI: Metadata Overhaul



Discussion / hints:

Fix patch:
diff --git a/.flake8 b/.flake8
new file mode 100644
index 0000000..1d035ef
--- /dev/null
+++ b/.flake8
@@ -0,0 +1,4 @@
+[flake8]
+max-line-length = 88
+extend-ignore = E203
+exclude = tests
\ No newline at end of file
diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml
new file mode 100644
index 0000000..0c805d4
--- /dev/null
+++ b/.github/workflows/coverage.yml
@@ -0,0 +1,29 @@
+name: Codecov
+
+on:
+  push:
+    branches:
+    - main
+  pull_request:
+    branches:
+    - main
+
+jobs:
+  run:
+    name: Upload coverage report to codecov.io
+    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"]
+    steps:
+    - uses: actions/setup-python@v4
+      with:
+        python-version: ${{ matrix.python-version }}
+    - uses: actions/checkout@v3
+    - name: Test with tox
+      run: |
+        pip install tox
+        tox -- --cov contique --cov-report xml --cov-report term
+    - name: Upload coverage to Codecov
+      uses: codecov/codecov-action@v3
+      if: ${{ matrix.python-version == '3.11' }}
\ No newline at end of file
diff --git a/.github/workflows/publish-to-pypi.yml b/.github/workflows/publish-to-pypi.yml
index ce37409..e60021d 100644
--- a/.github/workflows/publish-to-pypi.yml
+++ b/.github/workflows/publish-to-pypi.yml
@@ -8,9 +8,9 @@ jobs:
     runs-on: ubuntu-latest
 
     steps:
-    - uses: actions/checkout@v2
+    - uses: actions/checkout@v3
     - name: Setup Python
-      uses: actions/setup-python@v2
+      uses: actions/setup-python@v4
       with:
         python-version: '3.x'
         architecture: 'x64'
@@ -32,5 +32,4 @@ jobs:
       if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
       uses: pypa/gh-action-pypi-publish@release/v1
       with:
-        user: __token__
         password: ${{ secrets.PYPI_API_TOKEN }}
\ No newline at end of file
diff --git a/CITATION.cff b/CITATION.cff
new file mode 100644
index 0000000..1e144f0
--- /dev/null
+++ b/CITATION.cff
@@ -0,0 +1,11 @@
+authors:
+- family-names: "Dutzler"
+  given-names: "Andreas"
+  orcid: "https://orcid.org/0000-0002-9383-9686"
+cff-version: 1.2.0
+message: "If you use this software, please cite it using these metadata."
+title: "contique: Numerical continuation of nonlinear equilibrium equations."
+type: software
+doi: 10.5281/zenodo.341116057
+url: https://github.com/adtzlr/contique
+license: GPL-3.0
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
index f288702..81f0d3e 100644
--- a/LICENSE
+++ b/LICENSE
@@ -631,8 +631,8 @@ to attach them to the start of each source file to most effectively
 state the exclusion of warranty; and each file should have at least
 the "copyright" line and a pointer to where the full notice is found.
 
-    <one line to give the program's name and a brief idea of what it does.>
-    Copyright (C) <year>  <name of author>
+    Numerical continuation of nonlinear equilibrium equations.
+    Copyright (C) 2023  Andreas Dutzler
 
     This program is free software: you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
@@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail.
   If the program does terminal interaction, make it output a short
 notice like this when it starts in an interactive mode:
 
-    <program>  Copyright (C) <year>  <name of author>
+    contique  Copyright (C) 2023  Andreas Dutzler
     This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
     This is free software, and you are welcome to redistribute it
     under certain conditions; type `show c' for details.
diff --git a/MANIFEST.in b/MANIFEST.in
deleted file mode 100644
index 74215c3..0000000
--- a/MANIFEST.in
+++ /dev/null
@@ -1,2 +0,0 @@
-include README.md
-include LICENSE
\ No newline at end of file
diff --git a/README.md b/README.md
index 75035b9..56673e7 100644
--- a/README.md
+++ b/README.md
@@ -1,20 +1,28 @@
-# contique
-Numeric **conti**nuation of nonlinear e**qu**ilibrium **e**quations
+<p align="center">
+  <img src="https://github.com/adtzlr/contique/assets/5793153/86662194-2e2f-4dd6-b7de-5adf3270105d" height="80px"/>
+  <p align="center">Numerical continuation of nonlinear equilibrium equations.</p>
+</p>
 
 [![PyPI version shields.io](https://img.shields.io/pypi/v/contique.svg)](https://pypi.python.org/pypi/contique/)
 [![PyPI pyversions](https://img.shields.io/pypi/pyversions/contique.svg)](https://pypi.python.org/pypi/contique/)
 ![Made with love in Graz](https://madewithlove.now.sh/at?heart=true&colorA=%233b3b3b&colorB=%231f744f&text=Graz)
 [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
+[![DOI](https://zenodo.org/badge/341116057.svg)](https://zenodo.org/badge/latestdoi/341116057)
+[![codecov](https://codecov.io/gh/adtzlr/contique/branch/main/graph/badge.svg?token=CXKRL8TLQY)](https://codecov.io/gh/adtzlr/contique)
 
-<img src="https://raw.githubusercontent.com/adtzlr/contique/main/test/test_archimedean_spiral.svg" width="75%">
-
-Fig. 1 [Archimedean spiral](https://en.wikipedia.org/wiki/Archimedean_spiral) equation solved with [contique](https://github.com/adtzlr/contique/blob/main/test/test_archimedean_spiral.py)
+Contique is a Python 3.7+ package that provides methods for numeric continuation.
 
 ## Theory of `contique`'s numeric continuation
 
 A solution curve for `(n)` equilibrium equations `fun` in terms of `(n)` unknowns `x` and a load-proportionality-factor `lpf` should be found by numeric continuation from an initial equilibrium state `fun(x0, lpf0) = 0`. Contique's numeric continuation method is best classified as a 
+
 - **component-based continuation** with an adaptive 
 - **magnitude-based control-component switching**.
+
+
+<img src="https://raw.githubusercontent.com/adtzlr/contique/main/test/test_archimedean_spiral.svg" width="75%">
+
+Fig. 1 [Archimedean spiral](https://en.wikipedia.org/wiki/Archimedean_spiral) equation solved with [contique](https://github.com/adtzlr/contique/blob/main/test/test_archimedean_spiral.py)
   
 ### Extended equilibrium equations
 The `lpf` value is appended to the unknows `x` which gives the so-called extended unknowns `y = [x, lpf]`. One additional control equation is added to the equilibrium equations to ensure `(n+1)` equations in terms of `(n+1)` extended unknowns (see next section). This reduces the solution to a point on the initial solution curve.
@@ -39,8 +47,9 @@ A given set of equilibrium equations in terms of `x` and `lpf` (a.k.a. load-prop
 ### Function definition
 ```python
 def fun(x, lpf, a, b):
-    return np.array([-a * np.sin(x[0]) + x[1]**2 + lpf, 
-                     -b * np.cos(x[1]) * x[1]    + lpf])
+    return np.array(
+        [-a * np.sin(x[0]) + x[1] ** 2 + lpf, -b * np.cos(x[1]) * x[1] + lpf]
+    )
 ```
 
 with its initial solution
@@ -69,7 +78,7 @@ Res = contique.solve(
     maxcycles=4,
     maxiter=20,
     tol=1e-8,
-    overshoot=1.05
+    overshoot=1.05,
 )
 ```
 
@@ -107,12 +116,20 @@ and plot the solution curve.
 import matplotlib.pyplot as plt
 
 plt.plot(X[:, 0], X[:, 1], "C0.-")
-plt.xlabel('$x_1$')
-plt.ylabel('$x_2$')
-plt.plot([0],[0],'C0o',lw=3)
-plt.arrow(X[-2,0],X[-2,1],X[-1,0]-X[-2,0],X[-1,1]-X[-2,1],
-          head_width=0.075, head_length=0.15, fc='C0', ec='C0')
-plt.gca().set_aspect('equal')
+plt.xlabel("$x_1$")
+plt.ylabel("$x_2$")
+plt.plot([0], [0], "C0o", lw=3)
+plt.arrow(
+    X[-2, 0],
+    X[-2, 1],
+    X[-1, 0] - X[-2, 0],
+    X[-1, 1] - X[-2, 1],
+    head_width=0.075,
+    head_length=0.15,
+    fc="C0",
+    ec="C0",
+)
+plt.gca().set_aspect("equal")
 ```
 
 <img src="https://raw.githubusercontent.com/adtzlr/contique/main/test/test_sincos.svg" width="75%">
diff --git a/contique/__about__.py b/contique/__about__.py
deleted file mode 100644
index f3b4574..0000000
--- a/contique/__about__.py
+++ /dev/null
@@ -1,1 +0,0 @@
-__version__ = "0.1.15"
diff --git a/docs/logo/logo.png b/docs/logo/logo.png
new file mode 100644
index 0000000..42a97d7
Binary files /dev/null and b/docs/logo/logo.png differ
diff --git a/docs/logo/logo.svg b/docs/logo/logo.svg
new file mode 100644
index 0000000..ca0dd93
--- /dev/null
+++ b/docs/logo/logo.svg
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   width="400"
+   height="80"
+   viewBox="0 0 105.83333 21.166667"
+   version="1.1"
+   id="svg5"
+   inkscape:version="1.1.2 (b8e25be833, 2022-02-05)"
+   sodipodi:docname="logo.svg"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:svg="http://www.w3.org/2000/svg">
+  <sodipodi:namedview
+     id="namedview7"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageshadow="2"
+     inkscape:pageopacity="0.0"
+     inkscape:pagecheckerboard="0"
+     inkscape:document-units="mm"
+     showgrid="false"
+     width="320px"
+     units="px"
+     inkscape:zoom="1.050338"
+     inkscape:cx="100.44386"
+     inkscape:cy="-23.801862"
+     inkscape:window-width="1920"
+     inkscape:window-height="1017"
+     inkscape:window-x="-8"
+     inkscape:window-y="-8"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="layer1" />
+  <defs
+     id="defs2">
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient21837">
+      <stop
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0"
+         id="stop21833" />
+      <stop
+         style="stop-color:#000000;stop-opacity:0;"
+         offset="1"
+         id="stop21835" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient21793"
+       inkscape:swatch="solid">
+      <stop
+         style="stop-color:#504416;stop-opacity:1;"
+         offset="0"
+         id="stop21791" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient21837"
+       id="linearGradient21839"
+       x1="2.3309135"
+       y1="12.094236"
+       x2="67.837479"
+       y2="12.094236"
+       gradientUnits="userSpaceOnUse" />
+    <clipPath
+       clipPathUnits="userSpaceOnUse"
+       id="clipPath12603">
+      <text
+         xml:space="preserve"
+         style="font-style:normal;font-weight:normal;font-size:26.811px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
+         x="1.889273"
+         y="22.293415"
+         id="text12607"><tspan
+           sodipodi:role="line"
+           id="tspan12605"
+           style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:26.811px;font-family:Hawk;-inkscape-font-specification:Hawk;fill:#000000;fill-opacity:1;stroke-width:0.264583"
+           x="1.889273"
+           y="22.293415">contique</tspan></text>
+    </clipPath>
+  </defs>
+  <g
+     inkscape:label="Ebene 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(-2.465976,-2.244688)">
+    <image
+       width="105.83334"
+       height="21.166666"
+       preserveAspectRatio="none"
+       style="image-rendering:optimizeSpeed"
+       xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAY4AAAAwCAYAAAAGs53NAAAABHNCSVQICAgIfAhkiAAAApNJREFU eJzt1U3O2zAMRdFHOt8SupFuoftfUsgOSP246ITzewDDlEQpiR3g2e9ff9L8I/mPzD8y/5Gs7z0+ 6161rd5P9dhH6rU1J39k9tQ+c8kf6fnUnD3d//RneO3Z893f47pbz/uZd5fMrz1rrFObSW6Su/Kq ZXatVZ1ukknyU6f32FSXS3IpLRW7ltKzLlv391zsNfU9rrVVh7TGu6drq/X0kCyucZ45r/2yUOrM y1LadUi6xynznrOUKeR9jinlFpKHTCGzGnvXZnGNT/262/c1fvZ8zT36nrXVp+p7rr3P2qvV99Wj 3Ges2q++Wks9ivotWmtdZ++R+p51per3du0KWWb1ZfXYqrP+bpZrbvVUbfm+V++Zs57zkExWPXGv 2X6FltZ9+zXL066zbO+tV2zrte7+VSve38HOX6DqyPc4s89NWaS0+q9eRa91rd2X13itR62tnl7L POO89uR1bkYo7z19RvaZZ7zOq566zvdd52TvzX5uuzfPWZHZ66kI7d7oz8jU7t/11Zu79//jUH+P Piv+6Xs91jiPNUMK5fvxXo/09ej7sd/rr8ff535TirTz6nr9e9UuAAAGCA4AwAjBAQAYITgAACME BwBghOAAAIwQHACAEYIDADBCcAAARggOAMAIwQEAGCE4AAAjBAcAYITgAACMEBwAgBGCAwAwQnAA AEYIDgDACMEBABghOAAAIwQHAGCE4AAAjBAcAIARggMAMEJwAABGCA4AwAjBAQAYITgAACMEBwBg hOAAAIwQHACAEYIDADBCcAAARggOAMAIwQEAGCE4AAAjBAcAYITgAACMEBwAgBGCAwAwQnAAAEYI DgDACMEBABghOAAAIwQHAGCE4AAAjBAcAICRv6AM4vPvFSvvAAAAAElFTkSuQmCC "
+       id="image30769"
+       x="2.465976"
+       y="2.244688"
+       clip-path="url(#clipPath12603)" />
+  </g>
+</svg>
diff --git a/pyproject.toml b/pyproject.toml
index 1b68d94..11ad8c6 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,3 +1,46 @@
 [build-system]
-requires = ["setuptools>=42", "wheel"]
-build-backend = "setuptools.build_meta"
\ No newline at end of file
+requires = ["setuptools>=61"]
+build-backend = "setuptools.build_meta"
+
+[tool.isort]
+profile = "black"
+
+[project]
+name = "contique"
+authors = [
+  {email = "a.dutzler@gmail.com"},
+  {name = "Andreas Dutzler"}
+]
+description = "Numerical continuation of nonlinear equilibrium equations."
+readme = "README.md"
+license = {file = "LICENSE"}
+keywords = [
+  "python", 
+  "numeric-continuation",
+  "scientific-computing",
+]
+classifiers = [
+  "Development Status :: 4 - Beta",
+  "License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)",
+  "Operating System :: OS Independent",
+  "Programming Language :: Python",
+  "Programming Language :: Python :: 3",
+  "Programming Language :: Python :: 3.7",
+  "Programming Language :: Python :: 3.8",
+  "Programming Language :: Python :: 3.9",
+  "Programming Language :: Python :: 3.10",
+  "Programming Language :: Python :: 3.11",
+  "Topic :: Scientific/Engineering",
+  "Topic :: Scientific/Engineering :: Mathematics",
+  "Topic :: Utilities"
+]
+dynamic = ["version"]
+requires-python = ">=3.7"
+dependencies = ["numpy", "scipy"]
+
+[tool.setuptools.dynamic]
+version = {attr = "contique.__about__.__version__"}
+
+[project.urls]
+Code = "https://github.com/adtzlr/contique"
+Issues = "https://github.com/adtzlr/contique/issues"
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
deleted file mode 100644
index 5576e19..0000000
--- a/requirements.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-numpy
-scipy
\ No newline at end of file
diff --git a/setup.cfg b/setup.cfg
deleted file mode 100644
index 7fa2205..0000000
--- a/setup.cfg
+++ /dev/null
@@ -1,35 +0,0 @@
-[metadata]
-name = contique
-version = 0.1.15
-author = Andreas Dutzler
-author_email = a.dutzler@gmail.com
-description = Numerical continuation of nonlinear equilibrium equations
-url = https://github.com/adtzlr/contique
-project_urls =
-    Code=https://github.com/adtzlr/contique
-    Issues=https://github.com/adtzlr/contique/issues
-long_description = file: README.md
-long_description_content_type = text/markdown
-license = GPL-3.0-or-later
-license_file = LICENSE
-classifiers =
-    Development Status :: 4 - Beta
-    Intended Audience :: Science/Research
-    License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
-    Operating System :: OS Independent
-    Programming Language :: Python
-    Programming Language :: Python :: 3
-    Programming Language :: Python :: 3.6
-    Programming Language :: Python :: 3.7
-    Programming Language :: Python :: 3.8
-    Programming Language :: Python :: 3.9
-    Programming Language :: Python :: 3.10
-    Topic :: Scientific/Engineering
-    Topic :: Scientific/Engineering :: Mathematics
-
-[options]
-packages = find:
-install_requires =
-    numpy
-    scipy
-python_requires = >=3.6
\ No newline at end of file
diff --git a/src/contique/__about__.py b/src/contique/__about__.py
new file mode 100644
index 0000000..970659c
--- /dev/null
+++ b/src/contique/__about__.py
@@ -0,0 +1,1 @@
+__version__ = "0.1.16"
diff --git a/contique/__init__.py b/src/contique/__init__.py
similarity index 89%
rename from contique/__init__.py
rename to src/contique/__init__.py
index 7f58960..91634b2 100644
--- a/contique/__init__.py
+++ b/src/contique/__init__.py
@@ -4,5 +4,6 @@
 
 __all__ = [
     "__version__",
+    "jacobian",
     "solve",
 ]
diff --git a/contique/helpers.py b/src/contique/helpers.py
similarity index 74%
rename from contique/helpers.py
rename to src/contique/helpers.py
index 57d56c6..0d11c5c 100644
--- a/contique/helpers.py
+++ b/src/contique/helpers.py
@@ -1,24 +1,6 @@
-# -*- coding: utf-8 -*-
 """
-Created on Wed Feb 17 14:31:04 2021
-
-@author: adtzlr
-
-Contique - Numeric continuation of equilibrium equations
-Copyright (C) 2022 Andreas Dutzler
-
-This program 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 <https://www.gnu.org/licenses/>.
+contique: Numerical continuation of nonlinear equilibrium equations.
+Andreas Dutzler, 2023
 """
 
 import numpy as np
@@ -96,7 +78,7 @@ def argparser(fun):
     "Function decorator for the handling of function arguments."
 
     def inner(x, *args, **kwargs):
-        "Pass \*args and \*\*kwargs to a function if they are not None."
+        "Pass `*args` and `**kwargs` to a function if they are not None."
         no_args = (len(args) == 1 and args[0] is None) or not bool(args)
         no_kwargs = not bool(kwargs)
 
@@ -118,7 +100,7 @@ def argparser2(fun):
     with 2 primary arguments followed by other args."""
 
     def inner2(x, lpf, *args, **kwargs):
-        "Pass \*args and \*\*kwargs to a function if they are not None."
+        "Pass `*args` and `**kwargs` to a function if they are not None."
         no_args = (len(args) == 1 and args[0] is None) or not bool(args)
         no_kwargs = not bool(kwargs)
 
diff --git a/contique/jacobian.py b/src/contique/jacobian.py
similarity index 76%
rename from contique/jacobian.py
rename to src/contique/jacobian.py
index 01e68f5..b40647b 100644
--- a/contique/jacobian.py
+++ b/src/contique/jacobian.py
@@ -1,25 +1,8 @@
-# -*- coding: utf-8 -*-
 """
-Created on Wed Feb 17 14:31:04 2021
-
-@author: adtzlr
-
-Contique - Numeric continuation of equilibrium equations
-Copyright (C) 2021 Andreas Dutzler
-
-This program 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 <https://www.gnu.org/licenses/>.
+contique: Numerical continuation of nonlinear equilibrium equations.
+Andreas Dutzler, 2023
 """
+
 import copy
 import numpy as np
 
diff --git a/contique/newton.py b/src/contique/newton.py
similarity index 78%
rename from contique/newton.py
rename to src/contique/newton.py
index b1a5296..5d07410 100644
--- a/contique/newton.py
+++ b/src/contique/newton.py
@@ -1,24 +1,6 @@
-# -*- coding: utf-8 -*-
 """
-Created on Wed Feb 17 14:31:04 2021
-
-@author: adtzlr
-
-Contique - Numeric continuation of equilibrium equations
-Copyright (C) 2021 Andreas Dutzler
-
-This program 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 <https://www.gnu.org/licenses/>.
+contique: Numerical continuation of nonlinear equilibrium equations.
+Andreas Dutzler, 2023
 """
 import numpy as np
 from scipy import sparse
@@ -38,7 +20,8 @@ class NewtonResult:
     message : str
         message for the state
     status : int
-        integer representig the status of the solution (0 if not converged, 1 if converged).
+        integer representig the status of the solution (0 if not converged, 1 if
+        converged).
     niterations : int
         number of performed iterations
     x : ndarray
@@ -63,7 +46,8 @@ def __init__(self, fun, x0, jac, args):
             function returning the jacobian of the equilibrium equations
         args : tuple, optional
             Optional tuple of arguments which are passed to the function. Eeven if only
-            one argument is passed, it has to be encapsulated in a tuple (default is (None,)).
+            one argument is passed, it has to be encapsulated in a tuple (default is
+            (None,)).
 
         """
         self.success = False
@@ -91,7 +75,8 @@ def newtonrhapson(fun, x0, jac, args=(None,), maxiter=8, tol=1e-8, solve=None):
         jacobian of fun w.r.t. the unknows x
     args : tuple, optional
         Optional tuple of arguments which are passed to the function. Eeven if only
-        one argument is passed, it has to be encapsulated in a tuple (default is (None,)).
+        one argument is passed, it has to be encapsulated in a tuple (default is
+        (None,)).
     maxiter : int, optional
         maximum number of iterations (default is 8)
     tol : float, optional
@@ -123,7 +108,7 @@ def newtonrhapson(fun, x0, jac, args=(None,), maxiter=8, tol=1e-8, solve=None):
         # solve linear equation system
         try:
             res.x += solve(res.jac, -res.fun)
-        except:
+        except:  # NOQA: E722
             res.x *= np.nan
 
         # calculate function at updated x
@@ -144,7 +129,12 @@ def newtonrhapson(fun, x0, jac, args=(None,), maxiter=8, tol=1e-8, solve=None):
     # check if newton process failed
     if not res.success:
         if maxiter == 1:
-            res.message = "Calculated linear solution because of input parameter `maxiter=1` (not converged)."
+            res.message = " ".join(
+                [
+                    "Calculated linear solution",
+                    "because of input parameter `maxiter=1` (not converged).",
+                ]
+            )
         else:
             res.message = "Newton-R. process failed."
 
diff --git a/contique/newtonxt.py b/src/contique/newtonxt.py
similarity index 87%
rename from contique/newtonxt.py
rename to src/contique/newtonxt.py
index afade0e..4b99b3f 100644
--- a/contique/newtonxt.py
+++ b/src/contique/newtonxt.py
@@ -1,24 +1,6 @@
-# -*- coding: utf-8 -*-
 """
-Created on Wed Feb 17 14:31:04 2021
-
-@author: adtzlr
-
-Contique - Numeric continuation of equilibrium equations
-Copyright (C) 2022 Andreas Dutzler
-
-This program 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 <https://www.gnu.org/licenses/>.
+contique: Numerical continuation of nonlinear equilibrium equations.
+Andreas Dutzler, 2023
 """
 import numpy as np
 from scipy import sparse
@@ -60,10 +42,10 @@ def funxt(y, needle_vector, ymax, fun, jac=None, jacmode=3, jaceps=None, args=(N
     """
 
     # split the unknowns
-    x, l = y[:-1], y[-1]
+    x, lpf = y[:-1], y[-1]
 
     # evaluate the given function
-    f = fun(x, l, *args)
+    f = fun(x, lpf, *args)
 
     if sparse.issparse(f):
         # convert function vector to array
@@ -176,7 +158,8 @@ def newtonxt(
         user-specified stepwidth (if None, this defaults to eps^(1/mode))
     args : tuple, optional
         Optional tuple of arguments which are passed to the function. Eeven if only
-        one argument is passed, it has to be encapsulated in a tuple (default is (None,)).
+        one argument is passed, it has to be encapsulated in a tuple (default is
+        (None,)).
     maxiter : int, optional
         max. number of Newton-iterations per cycle
     tol : float, optional
diff --git a/contique/numcont.py b/src/contique/numcont.py
similarity index 88%
rename from contique/numcont.py
rename to src/contique/numcont.py
index a02d82e..fc73012 100644
--- a/contique/numcont.py
+++ b/src/contique/numcont.py
@@ -1,28 +1,9 @@
-# -*- coding: utf-8 -*-
 """
-Created on Wed Feb 17 14:31:04 2021
-
-@author: adtzlr
-
-Contique - Numeric continuation of equilibrium equations
-Copyright (C) 2021 Andreas Dutzler
-
-This program 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 <https://www.gnu.org/licenses/>.
+contique: Numerical continuation of nonlinear equilibrium equations.
+Andreas Dutzler, 2023
 """
 
 import numpy as np
-from scipy.sparse.linalg import spsolve
 
 from .helpers import argparser2
 from .newtonxt import newtonxt
@@ -69,7 +50,8 @@ def solve(
         jacobian of fun w.r.t. the unknows x
     args : tuple, optional
         Optional tuple of arguments which are passed to the function. Eeven if only
-        one argument is passed, it has to be encapsulated in a tuple (default is (None,)).
+        one argument is passed, it has to be encapsulated in a tuple (default is
+        (None,)).
     dxmax : float, optional
         max. allowed absolute incremental increase of unknowns per step
     dlpfmax : float, optional
@@ -155,7 +137,7 @@ def solve(
 
     # Step loop.
     for step in 1 + np.arange(maxsteps):
-        ## pre-identification of control component
+        # pre-identification of control component
         res = newtonxt(
             fun,
             jac,
diff --git a/contique/printinfo.py b/src/contique/printinfo.py
similarity index 59%
rename from contique/printinfo.py
rename to src/contique/printinfo.py
index f846eb9..f39f4b8 100644
--- a/contique/printinfo.py
+++ b/src/contique/printinfo.py
@@ -1,24 +1,6 @@
-# -*- coding: utf-8 -*-
 """
-Created on Wed Feb 17 14:31:04 2021
-
-@author: adtzlr
-
-Contique - Numeric continuation of equilibrium equations
-Copyright (C) 2021 Andreas Dutzler
-
-This program 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 <https://www.gnu.org/licenses/>.
+contique: Numerical continuation of nonlinear equilibrium equations.
+Andreas Dutzler, 2023
 """
 import numpy as np
 
@@ -46,7 +28,7 @@ def cycle(step, cycl, control0, control, status, fnorm, niterations, overshootco
     sign0 = f"{control0[1]:+d}"[0]
     sign = f"{control[1]:+d}"[0]
     print(
-        "|{0:4s}{1:1d} |{2:6d}{3:s}  =>{4:6d}{5:s} | {6:.1e} ({7:2d}#) |{8:13s}|".format(
+        "|{0:4s}{1:1d} |{2:6d}{3:s}  =>{4:6d}{5:s} | {6:.1e} ({7:2d}#) |{8:13s}|".format(  # NOQA: E501
             stp,
             cycl,
             control0[0],
diff --git a/tox.ini b/tox.ini
new file mode 100644
index 0000000..b740ee1
--- /dev/null
+++ b/tox.ini
@@ -0,0 +1,11 @@
+[tox]
+envlist = py3
+isolated_build = True
+
+[testenv]
+deps =
+    pytest
+    pytest-cov
+    matplotlib
+commands =
+    pytest {posargs}
\ No newline at end of file


Test patch:
diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/tests/coverage.yml b/tests/coverage.yml
new file mode 100644
index 0000000..80a8e21
--- /dev/null
+++ b/tests/coverage.yml
@@ -0,0 +1,29 @@
+name: Codecov
+
+on:
+  push:
+    branches:
+    - main
+  pull_request:
+    branches:
+    - main
+
+jobs:
+  run:
+    name: Upload coverage report to codecov.io
+    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"]
+    steps:
+    - uses: actions/setup-python@v4
+      with:
+        python-version: ${{ matrix.python-version }}
+    - uses: actions/checkout@v3
+    - name: Test with tox
+      run: |
+        pip install tox
+        tox -- --cov tensortrax --cov-report xml --cov-report term
+    - name: Upload coverage to Codecov
+      uses: codecov/codecov-action@v3
+      if: ${{ matrix.python-version == '3.11' }}
\ No newline at end of file
diff --git a/test/test_archimedean_spiral.py b/tests/test_archimedean_spiral.py
similarity index 100%
rename from test/test_archimedean_spiral.py
rename to tests/test_archimedean_spiral.py
diff --git a/test/test_archimedean_spiral.svg b/tests/test_archimedean_spiral.svg
similarity index 100%
rename from test/test_archimedean_spiral.svg
rename to tests/test_archimedean_spiral.svg
diff --git a/test/test_archimedean_spiral_noargs.py b/tests/test_archimedean_spiral_noargs.py
similarity index 100%
rename from test/test_archimedean_spiral_noargs.py
rename to tests/test_archimedean_spiral_noargs.py
diff --git a/test/test_archimedean_spiral_noargs.svg b/tests/test_archimedean_spiral_noargs.svg
similarity index 100%
rename from test/test_archimedean_spiral_noargs.svg
rename to tests/test_archimedean_spiral_noargs.svg
diff --git a/test/test_bratu.py b/tests/test_bratu.py
similarity index 92%
rename from test/test_bratu.py
rename to tests/test_bratu.py
index b1e1379..330a876 100644
--- a/test/test_bratu.py
+++ b/tests/test_bratu.py
@@ -8,9 +8,9 @@
 def fun(x, lpf):
     n = len(x)
     h = 1 / (n - 1)
-    A = np.diag(2 * np.ones_like(x) / h ** 2)
+    A = np.diag(2 * np.ones_like(x) / h**2)
     for i in [1, -1]:
-        A -= np.diag(np.ones_like(x[:-1]) / h ** 2, i)
+        A -= np.diag(np.ones_like(x[:-1]) / h**2, i)
     f = -A.dot(x) + lpf * np.exp(x)
     for i in [0, -1]:
         f[i] = x[i]
diff --git a/test/test_bratu.svg b/tests/test_bratu.svg
similarity index 100%
rename from test/test_bratu.svg
rename to tests/test_bratu.svg
diff --git a/test/test_lituus_spiral.py b/tests/test_lituus_spiral.py
similarity index 100%
rename from test/test_lituus_spiral.py
rename to tests/test_lituus_spiral.py
diff --git a/test/test_lituus_spiral.svg b/tests/test_lituus_spiral.svg
similarity index 100%
rename from test/test_lituus_spiral.svg
rename to tests/test_lituus_spiral.svg
diff --git a/test/test_log_spiral.py b/tests/test_log_spiral.py
similarity index 100%
rename from test/test_log_spiral.py
rename to tests/test_log_spiral.py
diff --git a/test/test_log_spiral.svg b/tests/test_log_spiral.svg
similarity index 100%
rename from test/test_log_spiral.svg
rename to tests/test_log_spiral.svg
diff --git a/test/test_sin_rebalance.py b/tests/test_sin_rebalance.py
similarity index 100%
rename from test/test_sin_rebalance.py
rename to tests/test_sin_rebalance.py
diff --git a/test/test_sin_rebalance.svg b/tests/test_sin_rebalance.svg
similarity index 100%
rename from test/test_sin_rebalance.svg
rename to tests/test_sin_rebalance.svg
diff --git a/test/test_sincos.py b/tests/test_sincos.py
similarity index 100%
rename from test/test_sincos.py
rename to tests/test_sincos.py
diff --git a/test/test_sincos.svg b/tests/test_sincos.svg
similarity index 100%
rename from test/test_sincos.svg
rename to tests/test_sincos.svg
diff --git a/test/test_twotruss.py b/tests/test_twotruss.py
similarity index 94%
rename from test/test_twotruss.py
rename to tests/test_twotruss.py
index 2fbae34..68b457c 100644
--- a/test/test_twotruss.py
+++ b/tests/test_twotruss.py
@@ -7,7 +7,7 @@
 
 def fun(x, lpf, a, L, EA):
     WL = -x[0] / L
-    lL = np.sqrt(1 - 2 * np.sin(a) * WL + WL ** 2)
+    lL = np.sqrt(1 - 2 * np.sin(a) * WL + WL**2)
     N = EA * (lL - 1)
     return np.array([2 * N * (np.sin(a) - WL) + lpf])
 
diff --git a/test/test_twotruss.svg b/tests/test_twotruss.svg
similarity index 100%
rename from test/test_twotruss.svg
rename to tests/test_twotruss.svg