Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,4 @@ The optional `-- format` and `-- write` arguments (see above) attempt to correct
## Further checks:
- [ ] The documentation builds: `$ nox -s docs`.
- [ ] Code is commented, particularly in hard-to-understand areas.
- [ ] Tests are added that prove fix is effective or that feature works.
- [ ] Tests are added that prove fix is effective or that feature works.
52 changes: 52 additions & 0 deletions .github/workflows/codecov.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
name: codecov

on:
push:
paths-ignore:
- "*.md"
- "LICENSE"
- "docs/**"
- "images/**"
- ".github/ISSUE_TEMPLATE/**"

pull_request:
branches: [main]
paths-ignore:
- "*.md"
- "LICENSE"
- "docs/**"
- "images/**"
- ".github/ISSUE_TEMPLATE/**"

jobs:
tests:
name: Collect and upload coverage
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.14"

- name: Install thevenin
run: |
python -m pip install --upgrade pip
pip install .[tests]

- name: Pytest with coverage
run: pytest --cov=thevenin --cov-report=xml tests/

- name: Upload to codecov
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
verbose: true
flags: unittests
env_vars: OS,PYTHON
files: ./coverage.xml
name: codecov-umbrella
fail_ci_if_error: false
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
- `initial_state` was renamed to `state0` in `sim.pre()` ([#14](https://github.com/NatLabRockies/thevenin/pull/14))

### Chores
- Switch linter over to `ruff`, enable `codecov` workflow, update README badges ([#31](https://github.com/NatLabRockies/thevenin/pull/31))
- Make GitHub hyperlinks reference new org name `NREL` -> `NatLabRockies` ([#30](https://github.com/NatLabRockies/thevenin/pull/30))
- Allow single backticks for sphinx inline code (`default_role = 'literal'`) ([#28](https://github.com/NatLabRockies/thevenin/pull/28))
- Rebrand NREL to NLR, and include name change for Alliance as well ([#27](https://github.com/NatLabRockies/thevenin/pull/27))
Expand Down
18 changes: 5 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,11 @@

# thevenin

[![CI][ci-b]][ci-l]  
![tests][test-b]  
![coverage][cov-b]  
[![pep8][pep-b]][pep-l]

[ci-b]: https://github.com/NatLabRockies/thevenin/actions/workflows/ci.yml/badge.svg
[ci-l]: https://github.com/NatLabRockies/thevenin/actions/workflows/ci.yml

[test-b]: https://github.com/NatLabRockies/thevenin/blob/main/images/tests.svg?raw=true
[cov-b]: https://github.com/NatLabRockies/thevenin/blob/main/images/coverage.svg?raw=true

[pep-b]: https://img.shields.io/badge/code%20style-pep8-orange.svg
[pep-l]: https://www.python.org/dev/peps/pep-0008
[![ci](https://github.com/NatLabRockies/thevenin/actions/workflows/ci.yml/badge.svg)](https://github.com/NatLabRockies/thevenin/actions/workflows/ci.yml)  
[![codecov](https://codecov.io/gh/NatLabRockies/thevenin/graph/badge.svg?token=XM2VF9JVEW)](https://codecov.io/gh/NatLabRockies/thevenin)  
[![license](https://img.shields.io/badge/license-BSD--3-blue.svg)](https://github.com/NatLabRockies/thevenin/blob/main/LICENSE)  
[![downloads](https://static.pepy.tech/personalized-badge/thevenin?period=total&units=INTERNATIONAL_SYSTEM&left_color=GREY&right_color=GREEN&left_text=downloads)](https://pepy.tech/projects/thevenin)  
[![pypi](https://img.shields.io/pypi/v/thevenin)](https://pypi.org/project/thevenin)

## Summary
This package is a wrapper for the well-known Thevenin equivalent circuit model. The model is comprised of a single series resistor followed by any number of parallel RC pairs. Figure 1 below illustrates a circuit with 2 RC pairs; however, the model can be run with as few as zero, and as many as $N$.
Expand Down
4 changes: 2 additions & 2 deletions docs/source/development/code_style_and_linting.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ We adhere to `PEP8 <https://peps.python.org/pep-0008/>`_ with minimal exceptions

Code Formatting
---------------
While `black <https://black.readthedocs.io/en/stable/>`_ is a popular auto-formatting package, we do not permit it to be used for this codebase. Although it adheres to the same PEP8 standards that we follow, the `black` styling can be a bit more opinionated at times and does not always help improve clarity. For those still looking for auto-formatting, we permit the use of `autopep8 <https://github.com/hhatto/autopep8>`_ is when paired with the `.flake8` configuration file found in `.github/linters`. IDEs supporting `autopep8` should be configured accordingly. Developers can also run the formatter manually using::
While `black <https://black.readthedocs.io/en/stable/>`_ is a popular auto-formatting package, we do not permit it to be used for this codebase. Although it adheres to the same PEP8 standards that we follow, the `black` styling is also much more opinionated and does not always help improve clarity. For those still looking for auto-formatting, we permit the use of `autopep8 <https://github.com/hhatto/autopep8>`_ when paired with the `.flake8` configuration file found in `.github/linters`. However, we do not automatically include and configure `autopep8` as a dependency. If you'd like to use it in your IDE, you should install it and configure it with the provided `.flake8` file. We also provide a `nox` session that uses a pre-configured `ruff` linter to apply minimal fixes to the codebase that is well-aligned with our style guidelines. To run this session, use the following command::

nox -s linter -- format

When used with the optional `format` argument, this `nox` command will first run the auto-formatter and then check for errors. This means that is errors persist, then `autopep8` was unable to address them and that they must be addressed manually.
When used with the optional `format` argument, this `nox` command will run `ruff check` with the `--fix` option enabled. Note that there is also an option to run `nox -s linter -- stats` if you want to see a summary of the linting errors rather than a full report. Lastly, you can run `nox -s linter -- format-unsafe` to apply auto-fixes that the linter considers "unsafe". These fixes may potentially change the behavior of the code, so they should be used with caution and reviewed carefully. The `stats` and `format` or `format-unsafe` options can also be used together to see the summary of errors that remain after applying fixes. If you do not lint and format your code locally, the CI will catch any issues during the push process. Failed tests may result in a delayed reviewer assignments when you open pull requests.

Enforcement
-----------
Expand Down
51 changes: 25 additions & 26 deletions noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@

@nox.session(name='cleanup', python=False)
def run_cleanup(_) -> None:
"""Use os/shutil to remove some files/directories"""

"""Use os/shutil to remove some files/directories."""
if os.path.exists('.coverage'):
os.remove('.coverage')

Expand All @@ -21,34 +20,38 @@ def run_cleanup(_) -> None:


@nox.session(name='linter', python=False)
def run_flake8(session: nox.Session) -> None:
def run_ruff(session: nox.Session) -> None:
"""
Run flake8 with the github config file
Run ruff to check for linting errors.

Use the optional 'format' argument to run autopep8 prior to the linter.
Use the optional 'format' or 'format-unsafe' arguments to run ruff with the
--fix or --unsafe-fixes option prior to the linter. You can also use 'stats'
to show a summary of the found errors rather than a full report.

"""
session.run('pip', 'install', '--upgrade', '--quiet', 'ruff')

session.run('pip', 'install', '--upgrade', '--quiet', 'flake8')
session.run('pip', 'install', '--upgrade', '--quiet', 'autopep8')
command = ['ruff', 'check']
if 'stats' in session.posargs:
command.append('--statistics')

if 'format' in session.posargs:
session.run('autopep8', '.', '--in-place', '--recursive',
'--global-config=.github/linters/.flake8')
command.append('--fix')
elif 'format-unsafe' in session.posargs:
command.extend(['--fix', '--unsafe-fixes'])

session.run('flake8', '--config=.github/linters/.flake8')
session.run(*command)


@nox.session(name='codespell', python=False)
def run_codespell(session: nox.Session) -> None:
"""
Run codespell with the github config file
Run codespell with the github config file.

Use the optional 'write' argument to write the corrections directly into
the files. Otherwise, you will only see a summary of the found errors.

"""

session.run('pip', 'install', '--upgrade', '--quiet', 'codespell')

command = ['codespell', '--config=.github/linters/.codespellrc']
Expand All @@ -61,13 +64,12 @@ def run_codespell(session: nox.Session) -> None:
@nox.session(name='spellcheck', python=False)
def run_spellcheck(session: nox.Session) -> None:
"""
Run codespell with docs files included
Run codespell with docs files included.

Use the optional 'write' argument to write the corrections directly into
the files. Otherwise, you will only see a summary of the found errors.

"""

run_codespell(session)

command = ['codespell', '--config=.github/linters/.codespellrc']
Expand All @@ -80,14 +82,13 @@ def run_spellcheck(session: nox.Session) -> None:
@nox.session(name='tests', python=False)
def run_pytest(session: nox.Session) -> None:
"""
Run pytest and generate test/coverage reports
Run pytest and generate test/coverage reports.

Use the optional 'parallel' argument to run the tests in parallel. As just
a flag, the number of workers will be determined automatically. Otherwise,
you can specify the number of workers using an int, e.g., parallel=4.

"""

package = importlib.util.find_spec('thevenin')
coverage_folder = os.path.dirname(package.origin)

Expand All @@ -98,6 +99,8 @@ def run_pytest(session: nox.Session) -> None:
'tests/',
]
else:
os.makedirs('reports', exist_ok=True)

command = [
'pytest',
'--cov=src/thevenin',
Expand All @@ -115,15 +118,13 @@ def run_pytest(session: nox.Session) -> None:
elif arg.startswith('parallel'):
command[1:1] = ['-n', 'auto']

os.environ['MPLBACKEND'] = 'Agg'
session.run(*command)

run_cleanup(session)


@nox.session(name='badges', python=False)
def run_genbadge(session: nox.Session) -> None:
"""Run genbadge to make test/coverage badges"""

"""Run genbadge to make test/coverage badges."""
session.run(
'genbadge', 'coverage', '-l',
'-i', 'reports/coverage.xml',
Expand All @@ -140,15 +141,14 @@ def run_genbadge(session: nox.Session) -> None:
@nox.session(name='docs', python=False)
def run_sphinx(session: nox.Session) -> None:
"""
Run spellcheck and then use sphinx to build docs
Run spellcheck and then use sphinx to build docs.

Use the optional 'clean' argument to remove everything under the 'build'
and 'source/api' folders prior to re-building the docs. This is important
in cases where the api module names have been changed or when some navbars
are not showing new pages. In general, try without 'clean' first.

"""

if 'clean' in session.posargs:
os.chdir('docs')
session.run('make', 'clean')
Expand All @@ -169,14 +169,13 @@ def run_sphinx(session: nox.Session) -> None:
@nox.session(name='pre-commit', python=False)
def run_pre_commit(session: nox.Session) -> None:
"""
Run all linters/tests and make new badges
Run all linters/tests and make new badges.

Order of sessions: flake8, spellcheck, pytest, genbadge. Using 'format' for
Order of sessions: ruff, spellcheck, pytest, genbadge. Using 'format' for
linter, 'write' for spellcheck, and/or 'parallel' for pytest is permitted.

"""

run_flake8(session)
run_ruff(session)
run_spellcheck(session)

run_pytest(session)
Expand Down
28 changes: 26 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,31 @@ where = ["src"]
[tool.setuptools.package-data]
thevenin = ["_resources/*.yaml"]

[tool.ruff]
line-length = 80
exclude = ["build", "docs", "images", "reports"]

[tool.ruff.lint]
preview = true
select = [
"E", # All pycodestyle errors
"W", # All pycodestyle warnings
"F", # All pyflakes errors
"B", # All flake8-bugbear checks
"D", # All pydocstyle checks
]
extend-select = [
"E302", # Expected 2 blank lines, found 1 (requires preview=true, above)
]
ignore = [
"B007", "B009", "B028", "B905",
"D100", "D103", "D104", "D203", "D205", "D212", "D401",
"E201", "E202", "E226", "E241", "E731",
]

[tool.ruff.lint.per-file-ignores]
"tests/**/*" = ["D"]

[project.optional-dependencies]
docs = [
"sphinx",
Expand All @@ -64,8 +89,7 @@ tests = [
]
dev = [
"nox",
"flake8",
"autopep8",
"ruff",
"codespell",
"genbadge[all]",
"thevenin[docs,tests]",
Expand Down
3 changes: 0 additions & 3 deletions scripts/version_checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ def get_latest_version(package: str, prefix: str = None) -> str:
Failed to fetch PyPI data for requested package.

"""

url = f"https://pypi.org/pypi/{package}/json"

response = requests.get(url)
Expand Down Expand Up @@ -72,7 +71,6 @@ def check_against_pypi(pypi: str, local: str) -> None:
Local package is older than PyPI.

"""

pypi = Version(pypi)
local = Version(local)

Expand Down Expand Up @@ -103,7 +101,6 @@ def check_against_tag(tag: str, local: str) -> None:
Version mismatch: tag differs from local.

"""

tag = Version(tag)
local = Version(local)

Expand Down
2 changes: 1 addition & 1 deletion src/thevenin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
the documentation by visiting the website, hosted on Read the Docs. The website
includes search functionality and more detailed examples.

"""
""" # noqa: D400, D415 (ignore missing punctuation on first line)

# core package
from ._experiment import Experiment
Expand Down
5 changes: 0 additions & 5 deletions src/thevenin/_basemodel.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ def calculated_current(voltage, ocv, hyst, eta_j, R0) -> float | np.ndarray:
Calculated current at a single or multiple times [A].

"""

if eta_j.ndim == 1:
return -(voltage - ocv - hyst + np.sum(eta_j)) / R0
elif eta_j.ndim == 2:
Expand Down Expand Up @@ -83,7 +82,6 @@ def calculated_voltage(current, ocv, hyst, eta_j, R0) -> float | np.ndarray:
Calculated voltage at a single or multiple times [V].

"""

if eta_j.ndim == 1:
return ocv + hyst - np.sum(eta_j) - current*R0
elif eta_j.ndim == 2:
Expand Down Expand Up @@ -223,7 +221,6 @@ def __repr__(self) -> str: # pragma: no cover
A console-readable instance representation.

"""

keys = self._repr_keys
values = [getattr(self, k) for k in keys]

Expand Down Expand Up @@ -253,7 +250,6 @@ def _classname(self) -> str:
@property
def _get_params_dict(self) -> dict:
"""Return the params dictionary needed to initialize a new instance."""

params = {}
for k in self._repr_keys:
params[k] = getattr(self, k)
Expand All @@ -275,7 +271,6 @@ def pre(self, *args, **kwargs) -> None: # pragma: no cover

def _check_RC_pairs(self) -> None:
"""Verify correct attributes are set based on num_RC_pairs."""

missing_attrs = []
for j in range(1, self.num_RC_pairs + 1):
if not hasattr(self, 'R' + str(j)):
Expand Down
Loading