diff --git a/.gitchangelog.rc b/.gitchangelog.rc deleted file mode 100644 index 882a96d5..00000000 --- a/.gitchangelog.rc +++ /dev/null @@ -1,296 +0,0 @@ -# -*- coding: utf-8; mode: python -*- -## -## Format -## -## ACTION: [AUDIENCE:] COMMIT_MSG [!TAG ...] -## -## Description -## -## ACTION is one of 'chg', 'fix', 'new' -## -## Is WHAT the change is about. -## -## 'chg' is for refactor, small improvement, cosmetic changes... -## 'fix' is for bug fixes -## 'new' is for new features, big improvement -## -## AUDIENCE is optional and one of 'dev', 'usr', 'pkg', 'test', 'doc' -## -## Is WHO is concerned by the change. -## -## 'dev' is for developpers (API changes, refactors...) -## 'usr' is for final users (UI changes) -## 'pkg' is for packagers (packaging changes) -## 'test' is for testers (test only related changes) -## 'doc' is for doc guys (doc only changes) -## -## COMMIT_MSG is ... well ... the commit message itself. -## -## TAGs are additionnal adjective as 'refactor' 'minor' 'cosmetic' -## -## They are preceded with a '!' or a '@' (prefer the former, as the -## latter is wrongly interpreted in github.) Commonly used tags are: -## -## 'refactor' is obviously for refactoring code only -## 'minor' is for a very meaningless change (a typo, adding a comment) -## 'cosmetic' is for cosmetic driven change (re-indentation, 80-col...) -## 'wip' is for partial functionality but complete subfunctionality. -## -## Example: -## -## new: usr: support of bazaar implemented -## chg: re-indentend some lines !cosmetic -## new: dev: updated code to be compatible with last version of killer lib. -## fix: pkg: updated year of licence coverage. -## new: test: added a bunch of test around user usability of feature X. -## fix: typo in spelling my name in comment. !minor -## -## Please note that multi-line commit message are supported, and only the -## first line will be considered as the "summary" of the commit message. So -## tags, and other rules only applies to the summary. The body of the commit -## message will be displayed in the changelog without reformatting. -def writefile(lines): - # Develop Full String of Log - log = '' - for line in lines: - log += line - import re - log = re.sub(r'\[\w{1,20}\n? {0,8}\w{1,20}\]','', log) - print(log) - with open('source/changelog.rst','w') as changelog: - changelog.write(log) - print("Update CHANGELOG.rst Complete.") - -## -## ``ignore_regexps`` is a line of regexps -## -## Any commit having its full commit message matching any regexp listed here -## will be ignored and won't be reported in the changelog. -## -ignore_regexps = [ - r'\(private\)', - r'@minor', r'!minor', - r'@cosmetic', r'!cosmetic', - r'@refactor', r'!refactor', - r'@wip', r'!wip', - r'^([cC]hg|[fF]ix|[nN]ew)\s*:\s*[p|P]kg:', - r'^([cC]hg|[fF]ix|[nN]ew)\s*:\s*[d|D]ev:', - r'^(.{3,3}\s*:)?\s*[fF]irst commit.?\s*$', - r'^$', ## ignore commits with empty messages -] - - -## ``section_regexps`` is a list of 2-tuples associating a string label and a -## list of regexp -## -## Commit messages will be classified in sections thanks to this. Section -## titles are the label, and a commit is classified under this section if any -## of the regexps associated is matching. -## -## Please note that ``section_regexps`` will only classify commits and won't -## make any changes to the contents. So you'll probably want to go check -## ``subject_process`` (or ``body_process``) to do some changes to the subject, -## whenever you are tweaking this variable. -## -section_regexps = [ - ('New', [ - r'^[nN]ew\s*:\s*((dev|use?r|pkg|test|doc)\s*:\s*)?([^\n]*)$', - ]), - ('Changes', [ - r'^[cC]hg\s*:\s*((dev|use?r|pkg|test|doc)\s*:\s*)?([^\n]*)$', - ]), - ('Fix', [ - r'^[fF]ix\s*:\s*((dev|use?r|pkg|test|doc)\s*:\s*)?([^\n]*)$', - ]), - - ('Other', None ## Match all lines - ), - -] - - -## ``body_process`` is a callable -## -## This callable will be given the original body and result will -## be used in the changelog. -## -## Available constructs are: -## -## - any python callable that take one txt argument and return txt argument. -## -## - ReSub(pattern, replacement): will apply regexp substitution. -## -## - Indent(chars=" "): will indent the text with the prefix -## Please remember that template engines gets also to modify the text and -## will usually indent themselves the text if needed. -## -## - Wrap(regexp=r"\n\n"): re-wrap text in separate paragraph to fill 80-Columns -## -## - noop: do nothing -## -## - ucfirst: ensure the first letter is uppercase. -## (usually used in the ``subject_process`` pipeline) -## -## - final_dot: ensure text finishes with a dot -## (usually used in the ``subject_process`` pipeline) -## -## - strip: remove any spaces before or after the content of the string -## -## - SetIfEmpty(msg="No commit message."): will set the text to -## whatever given ``msg`` if the current text is empty. -## -## Additionally, you can `pipe` the provided filters, for instance: -#body_process = Wrap(regexp=r'\n(?=\w+\s*:)') | Indent(chars=" ") -#body_process = Wrap(regexp=r'\n(?=\w+\s*:)') -#body_process = noop -body_process = ReSub(r'((^|\n)[A-Z]\w+(-\w+)*: .*(\n\s+.*)*)+$', r'') | strip - - -## ``subject_process`` is a callable -## -## This callable will be given the original subject and result will -## be used in the changelog. -## -## Available constructs are those listed in ``body_process`` doc. -subject_process = (strip | - ReSub(r'^([cC]hg|[fF]ix|[nN]ew)\s*:\s*((dev|use?r|pkg|test|doc)\s*:\s*)?([^\n@]*)(@[a-z]+\s+)*$', r'\4') | - SetIfEmpty("No commit message.") | ucfirst | final_dot) - - -## ``tag_filter_regexp`` is a regexp -## -## Tags that will be used for the changelog must match this regexp. -## -tag_filter_regexp = r'^[0-9]+(\.[0-9]+)?$' - - -## ``unreleased_version_label`` is a string or a callable that outputs a string -## -## This label will be used as the changelog Title of the last set of changes -## between last valid tag and HEAD if any. -unreleased_version_label = "(unreleased)" - - -## ``output_engine`` is a callable -## -## This will change the output format of the generated changelog file -## -## Available choices are: -## -## - rest_py -## -## Legacy pure python engine, outputs ReSTructured text. -## This is the default. -## -## - mustache() -## -## Template name could be any of the available templates in -## ``templates/mustache/*.tpl``. -## Requires python package ``pystache``. -## Examples: -## - mustache("markdown") -## - mustache("restructuredtext") -## -## - makotemplate() -## -## Template name could be any of the available templates in -## ``templates/mako/*.tpl``. -## Requires python package ``mako``. -## Examples: -## - makotemplate("restructuredtext") -## -output_engine = rest_py -#output_engine = mustache("restructuredtext") -#output_engine = mustache("markdown") -#output_engine = makotemplate("restructuredtext") - - -## ``include_merge`` is a boolean -## -## This option tells git-log whether to include merge commits in the log. -## The default is to include them. -include_merge = True - - -## ``log_encoding`` is a string identifier -## -## This option tells gitchangelog what encoding is outputed by ``git log``. -## The default is to be clever about it: it checks ``git config`` for -## ``i18n.logOutputEncoding``, and if not found will default to git's own -## default: ``utf-8``. -#log_encoding = 'utf-8' - - -## ``publish`` is a callable -## -## Sets what ``gitchangelog`` should do with the output generated by -## the output engine. ``publish`` is a callable taking one argument -## that is an interator on lines from the output engine. -## -## Some helper callable are provided: -## -## Available choices are: -## -## - stdout -## -## Outputs directly to standard output -## (This is the default) -## -## - FileInsertAtFirstRegexMatch(file, pattern, idx=lamda m: m.start()) -## -## Creates a callable that will parse given file for the given -## regex pattern and will insert the output in the file. -## ``idx`` is a callable that receive the matching object and -## must return a integer index point where to insert the -## the output in the file. Default is to return the position of -## the start of the matched string. -## -## - FileRegexSubst(file, pattern, replace, flags) -## -## Apply a replace inplace in the given file. Your regex pattern must -## take care of everything and might be more complex. Check the README -## for a complete copy-pastable example. -## -publish = writefile -#publish = stdout - - -## ``revs`` is a list of callable or a list of string -## -## callable will be called to resolve as strings and allow dynamical -## computation of these. The result will be used as revisions for -## gitchangelog (as if directly stated on the command line). This allows -## to filter exaclty which commits will be read by gitchangelog. -## -## To get a full documentation on the format of these strings, please -## refer to the ``git rev-list`` arguments. There are many examples. -## -## Using callables is especially useful, for instance, if you -## are using gitchangelog to generate incrementally your changelog. -## -## Some helpers are provided, you can use them:: -## -## - FileFirstRegexMatch(file, pattern): will return a callable that will -## return the first string match for the given pattern in the given file. -## If you use named sub-patterns in your regex pattern, it'll output only -## the string matching the regex pattern named "rev". -## -## - Caret(rev): will return the rev prefixed by a "^", which is a -## way to remove the given revision and all its ancestor. -## -## Please note that if you provide a rev-list on the command line, it'll -## replace this value (which will then be ignored). -## -## If empty, then ``gitchangelog`` will act as it had to generate a full -## changelog. -## -## The default is to use all commits to make the changelog. -#revs = ["^1.0.3", ] -#revs = [ -# Caret( -# FileFirstRegexMatch( -# "CHANGELOG.rst", -# r"(?P[0-9]+\.[0-9]+(\.[0-9]+)?)\s+\([0-9]+-[0-9]{2}-[0-9]{2}\)\n--+\n")), -# "HEAD" -#] -revs = [] diff --git a/.github/workflows/pydocstyle.yml b/.github/workflows/pydocstyle.yml deleted file mode 100644 index b1c1143a..00000000 --- a/.github/workflows/pydocstyle.yml +++ /dev/null @@ -1,28 +0,0 @@ -name: pydocstyle - -on: [push, pull_request] - -jobs: - build: - - runs-on: ubuntu-latest - strategy: - matrix: - python-version: ["3.11"] - - steps: - - uses: actions/checkout@v3 - - name: Set up Python 3.11 - uses: actions/setup-python@v3 - with: - python-version: "3.11" - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install pydocstyle - pip install . - python3 -c "import electricpy; print('electricpy.__file__')" - - name: Test NumpyDoc Style - run: | - cd electricpy - pydocstyle --convention=numpy \ No newline at end of file diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index 7e62afed..940f9158 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.8", "3.9", "3.10", "3.11"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"] steps: - uses: actions/checkout@v3 @@ -25,9 +25,8 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - if [ -f test/requirements.txt ]; then pip install -r test/requirements.txt; fi - pip install . + pip install .[dev] python3 -c "import electricpy; print('electricpy.__file__')" - name: Test with pytest run: | - pytest --xdoctest + pytest --pydocstyle --xdoctest diff --git a/.github/workflows/sphinx.yml b/.github/workflows/sphinx.yml index 27656c69..09eb27f4 100644 --- a/.github/workflows/sphinx.yml +++ b/.github/workflows/sphinx.yml @@ -17,23 +17,21 @@ jobs: - name: Install Python uses: actions/setup-python@v3 with: - python-version: "3.11" + python-version: "3.12" # I don't know where the "run" thing is documented. - name: Install dependencies run: | python -m pip install --upgrade pip cp logo/ElectricpyLogo.svg docsource/static/ElectricpyLogo.svg - pip install -r docsource/requirements.txt - name: Build Sphinx docs if: success() run: | - pip install . + pip install .[dev] python3 -c "import electricpy; print('electricpy.__file__')" sphinx-build -M html docsource docs - name: Generate Coverage Badge if: success() run: | - pip install -r test/requirements.txt coverage run --source=./electricpy -m pytest coverage-badge -o docs/html/coverage.svg diff --git a/.vscode/settings.json b/.vscode/settings.json index 0bad4d33..078ed0cb 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,6 +2,21 @@ "editor.rulers": [ 80, 120 ], + "cSpell.words": [ + "electricpy", + "epmath", + "fsolve", + "matplotlib", + "parallelz", + "phasor", + "phasorlist", + "phasorplot", + "phasors", + "powerset", + "pyplot", + "scipy", + "visu" + ], "python.testing.pytestArgs": [ "-m", "test" diff --git a/README.md b/README.md index 82adf6b8..112b3e04 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,6 @@ [![sphinx](https://github.com/engineerjoe440/ElectricPy/actions/workflows/sphinx-build.yml/badge.svg?branch=master)](https://github.com/engineerjoe440/ElectricPy/actions/workflows/sphinx-build.yml) [![Documentation Status](https://readthedocs.org/projects/electricpy/badge/?version=latest)](https://electricpy.readthedocs.io/en/latest/?badge=latest) -![Tox Import Test](https://github.com/engineerjoe440/ElectricPy/workflows/Tox%20Tests/badge.svg) [![pytest](https://github.com/engineerjoe440/ElectricPy/actions/workflows/pytest.yml/badge.svg?branch=master)](https://github.com/engineerjoe440/ElectricPy/actions/workflows/pytest.yml) [![pydocstyle](https://github.com/engineerjoe440/ElectricPy/actions/workflows/pydocstyle.yml/badge.svg?branch=master)](https://github.com/engineerjoe440/ElectricPy/actions/workflows/pydocstyle.yml) @@ -32,12 +31,6 @@ development, education, and exploration in the realm of electrical engineering. Check out our full documentation: https://electricpy.readthedocs.io/en/latest/ -Antu dialog-warning **Documentation has recently been updated to use [ReadTheDocs](https://readthedocs.org/)** - -GitHub Pages are still active, and will continue to be for the forseeable -future, but they're intended for developmental updates rather than primary -documentation. - ## Features * Extensive set of common functions and formulas for electrical engineering and diff --git a/conftest.py b/conftest.py new file mode 100644 index 00000000..e36004ec --- /dev/null +++ b/conftest.py @@ -0,0 +1,9 @@ +# Pytest configuration + +import pytest +import numpy as np + +@pytest.fixture(scope="session", autouse=True) +def setup_environment(): + """Use Legacy Numpy Printing Options for consistent test outputs.""" + np.set_printoptions(legacy='1.25') diff --git a/cspell.json b/cspell.json deleted file mode 100644 index 2e7da734..00000000 --- a/cspell.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "version": "0.2", - "ignorePaths": [], - "dictionaryDefinitions": [], - "dictionaries": [], - "words": [ - "electricpy", - "epmath", - "fsolve", - "matplotlib", - "parallelz", - "phasor", - "phasorlist", - "phasorplot", - "phasors", - "powerset", - "pyplot", - "scipy", - "visu" - ], - "ignoreWords": [], - "import": [] -} diff --git a/docsource/additionalresources.md b/docsource/additionalresources.md new file mode 100644 index 00000000..4706e69f --- /dev/null +++ b/docsource/additionalresources.md @@ -0,0 +1,24 @@ +# Additional Resources + + +## Generic and Data Science + +* NumPy: https://numpy.org/ +* SciPy: https://scipy.org/ +* Matplotlib: https://matplotlib.org/ +* SymPy: https://www.sympy.org/en/index.html +* Pyomo: https://www.pyomo.org/ +* Pint: https://pint.readthedocs.io/en/stable/ +* numdifftools: https://numdifftools.readthedocs.io/en/latest/ + +## Electrical Engineering Focus + +* Python COMTRADE File Interpreter: https://github.com/dparrini/python-comtrade +* Python COMTRADE Writer: https://github.com/relihanl/comtradehandlers +* Arc Flash Calculator: https://github.com/LiaungYip/arcflash +* PandaPower: https://www.pandapower.org/start/ +* PyPSA: https://github.com/PyPSA/PyPSA +* PyPower (no longer supported): https://pypi.org/project/PYPOWER/ +* minpower: http://adamgreenhall.github.io/minpower/index.html +* oemof (Open Energy MOdeling Framework): https://oemof.org/ +* PowerGAMA: https://bitbucket.org/harald_g_svendsen/powergama/wiki/Home diff --git a/docsource/additionalresources.rst b/docsource/additionalresources.rst deleted file mode 100644 index d8999ca2..00000000 --- a/docsource/additionalresources.rst +++ /dev/null @@ -1,41 +0,0 @@ -Additional Resources -================================================================================ - - -Generic and Data Science ------------------------- - - * NumPy: https://numpy.org/ - - * SciPy: https://scipy.org/ - - * Matplotlib: https://matplotlib.org/ - - * SymPy: https://www.sympy.org/en/index.html - - * Pyomo: https://www.pyomo.org/ - - * Pint: https://pint.readthedocs.io/en/stable/ - - * numdifftools: https://numdifftools.readthedocs.io/en/latest/ - -Electrical Engineering Focus ----------------------------- - - * Python COMTRADE File Interpreter: https://github.com/dparrini/python-comtrade - - * Python COMTRADE Writer: https://github.com/relihanl/comtradehandlers - - * Arc Flash Calculator: https://github.com/LiaungYip/arcflash - - * PandaPower: https://www.pandapower.org/start/ - - * PyPSA: https://github.com/PyPSA/PyPSA - - * PyPower (no longer supported): https://pypi.org/project/PYPOWER/ - - * minpower: http://adamgreenhall.github.io/minpower/index.html - - * oemof (Open Energy MOdeling Framework): https://oemof.org/ - - * PowerGAMA: https://bitbucket.org/harald_g_svendsen/powergama/wiki/Home diff --git a/docsource/changes.rst b/docsource/changes.rst deleted file mode 100644 index 1dd04244..00000000 --- a/docsource/changes.rst +++ /dev/null @@ -1,4 +0,0 @@ -Recent Changes -================================================================================ - -.. git_changelog:: diff --git a/docsource/conf.py b/docsource/conf.py index 0be00c19..759e1093 100644 --- a/docsource/conf.py +++ b/docsource/conf.py @@ -38,7 +38,7 @@ # -- Project information ----------------------------------------------------- project = 'electricpy' -copyright = '2022, Joe Stanley' +copyright = '2026, Joe Stanley' author = 'Joe Stanley' # The full version, including alpha/beta/rc tags @@ -56,8 +56,6 @@ 'sphinx.ext.mathjax', 'sphinx.ext.autosummary', 'sphinx.ext.viewcode', - 'numpydoc', - 'sphinx_git', 'myst_parser', 'sphinx_immaterial', ] diff --git a/docsource/constants.md b/docsource/constants.md new file mode 100644 index 00000000..34d51bef --- /dev/null +++ b/docsource/constants.md @@ -0,0 +1,22 @@ +# Constants + +In addition to the variety of funcitons provided, several common, and useful, +constants are provided to simplify arithmetic. + + +| Name | Value | +|----------------|----------------------------------------------------------------| +| `pi` | π (derived from numpy.pi) | +| `a` | :math:`1\angle{120}` | +| `p` | 10^-12 (pico) | +| `n` | 10^-9 (nano) | +| `u` | 10^-6 (micro) | +| `m` | 10^-3 (mili) | +| `k` | 10^3 (kila) | +| `M` | 10^6 (mega) | +| `G` | 10^9 (giga) | +| `u0` | :math:`µ0` (mu-not) 4πE-7 | +| `e0` | :math:`ε0` (epsilon-not) 8.854E-12 | +| `carson_r` | 9.869e-7 (Carson's Ristance Constant) | +| `WATTS_PER_HP` | 745.699872 | +| `KWH_PER_BTU` | 3412.14 | diff --git a/docsource/constants.rst b/docsource/constants.rst deleted file mode 100644 index cc98f7f4..00000000 --- a/docsource/constants.rst +++ /dev/null @@ -1,26 +0,0 @@ -Constants -================================================================================ - -.. _constants.py: - -In addition to the variety of funcitons provided, several common, and useful, -constants are provided to simplify arithmetic. - -============== ================================================================= -Name Value -============== ================================================================= -`pi` π (derived from numpy.pi) -`a` :math:`1\angle{120}` -`p` 10^-12 (pico) -`n` 10^-9 (nano) -`u` 10^-6 (micro) -`m` 10^-3 (mili) -`k` 10^3 (kila) -`M` 10^6 (mega) -`G` 10^9 (giga) -`u0` :math:`µ0` (mu-not) 4πE-7 -`e0` :math:`ε0` (epsilon-not) 8.854E-12 -`carson_r` 9.869e-7 (Carson's Ristance Constant) -`WATTS_PER_HP` 745.699872 -`KWH_PER_BTU` 3412.14 -============== ================================================================= \ No newline at end of file diff --git a/docsource/index.rst b/docsource/index.rst index e2346bd2..0aeeae95 100644 --- a/docsource/index.rst +++ b/docsource/index.rst @@ -36,7 +36,6 @@ Contents: electricpyapi constants additionalresources - changes Github PyPI diff --git a/docsource/requirements.txt b/docsource/requirements.txt deleted file mode 100644 index bf7380ac..00000000 --- a/docsource/requirements.txt +++ /dev/null @@ -1,13 +0,0 @@ -wheel -sphinx -numpydoc -myst-parser[linkify] -sphinx-sitemap -sphinx-git -sphinx-immaterial -coverage-badge -numpy -matplotlib -scipy -sympy -numdifftools \ No newline at end of file diff --git a/docsource/static/InductionMotorCircleExample.png b/docsource/static/InductionMotorCircleExample.png index 4c2e29a3..a0461854 100644 Binary files a/docsource/static/InductionMotorCircleExample.png and b/docsource/static/InductionMotorCircleExample.png differ diff --git a/docsource/static/PhasorPlot.png b/docsource/static/PhasorPlot.png index a2e9bbbd..72637d18 100644 Binary files a/docsource/static/PhasorPlot.png and b/docsource/static/PhasorPlot.png differ diff --git a/docsource/static/PowerTriangle.png b/docsource/static/PowerTriangle.png index 25912020..d9049764 100644 Binary files a/docsource/static/PowerTriangle.png and b/docsource/static/PowerTriangle.png differ diff --git a/docsource/static/ReceivingEndPowerCircleExample.png b/docsource/static/ReceivingEndPowerCircleExample.png index e9e450f2..049025c9 100644 Binary files a/docsource/static/ReceivingEndPowerCircleExample.png and b/docsource/static/ReceivingEndPowerCircleExample.png differ diff --git a/docsource/static/ReceivingPowerCircleExample.png b/docsource/static/ReceivingPowerCircleExample.png index e9e450f2..049025c9 100644 Binary files a/docsource/static/ReceivingPowerCircleExample.png and b/docsource/static/ReceivingPowerCircleExample.png differ diff --git a/docsource/static/convbar-example.png b/docsource/static/convbar-example.png index 21e20e76..f838c3cc 100644 Binary files a/docsource/static/convbar-example.png and b/docsource/static/convbar-example.png differ diff --git a/docsource/static/series-rlc-r10-l0.5.png b/docsource/static/series-rlc-r10-l0.5.png new file mode 100644 index 00000000..4d0b8246 Binary files /dev/null and b/docsource/static/series-rlc-r10-l0.5.png differ diff --git a/docsource/static/series-rlc-r5-l0.4.png b/docsource/static/series-rlc-r5-l0.4.png new file mode 100644 index 00000000..5995f327 Binary files /dev/null and b/docsource/static/series-rlc-r5-l0.4.png differ diff --git a/electricpy/compute.py b/electricpy/compute.py index b2549535..92e2fd24 100644 --- a/electricpy/compute.py +++ b/electricpy/compute.py @@ -253,6 +253,14 @@ def string_to_bits(str): Converts a Pythonic string to the string's binary representation. + Examples + -------- + >>> from electricpy import compute as cmp + >>> cmp.string_to_bits("A") + '01000001' + >>> cmp.string_to_bits("Hello") + '0000010010001100101110110011011001101111' + Parameters ---------- str: string @@ -264,7 +272,13 @@ def string_to_bits(str): The binary representation of the input string. """ - data = (''.join(format(ord(x), 'b') for x in str)) + data = ''.join(format(ord(x), 'b') for x in str) + + # Pad to Nearest Byte + offset = len(data) % 8 + if offset != 0: + data = (8 - offset) * '0' + data + return data # END diff --git a/electricpy/geometry/__init__.py b/electricpy/geometry/__init__.py index 9b226519..3a797296 100644 --- a/electricpy/geometry/__init__.py +++ b/electricpy/geometry/__init__.py @@ -16,7 +16,7 @@ import math from dataclasses import dataclass -from typing import Iterable, Iterator, Optional, Tuple, Union +from typing import Iterator, Tuple, Union Number = Union[int, float] @@ -24,6 +24,8 @@ def _as_float(x) -> float: """ + Cast as a float. + Coerce numeric-like values (including complex with ~0 imag) to float. This defends against accidental cmath usage elsewhere in the library. """ @@ -40,7 +42,8 @@ def _is_close(a: float, b: float, *, rel_tol: float = 1e-9, abs_tol: float = 1e- @dataclass(frozen=False) class Point: - """A point in 2D space. + """ + A point in 2D space. Parameters ---------- @@ -49,6 +52,7 @@ class Point: y : float The y coordinate of the point """ + x: float y: float @@ -78,18 +82,18 @@ def is_close(self, other: "Point", *, tol: float = 1e-9) -> bool: return _is_close(self.x, other.x, rel_tol=tol, abs_tol=tol) and _is_close(self.y, other.y, rel_tol=tol, abs_tol=tol) def __repr__(self) -> str: + """Developer representation of the Point.""" return f"Point({self.x}, {self.y})" def __str__(self) -> str: + """Representation of the Point.""" return f"({self.x}, {self.y})" @dataclass(frozen=False) class Line: - """A line in 2D space in the form: + """A line in 2D space in the form (`ax + by + c = 0`).""" - ax + by + c = 0 - """ a: float b: float c: float @@ -149,6 +153,8 @@ def intersection(self, other: object) -> Point: def normalized(self) -> Tuple[float, float, float]: """ + Normalize the geometry coefficients. + Return a normalized (a,b,c) such that sqrt(a^2+b^2)=1 and sign is stable. This helps with comparisons and distances. """ @@ -178,6 +184,8 @@ def is_close(self, other: "Line", *, tol: float = 1e-9) -> bool: def __eq__(self, other: object) -> bool: """ + Evaluate the equality of geometric object. + Exact-ish equality (proportional coefficients), but using normalization for better behavior than raw ratio checks. This is safer than the original. """ @@ -186,10 +194,11 @@ def __eq__(self, other: object) -> bool: return self.is_close(other, tol=1e-9) def __repr__(self) -> str: + """Developer representation of the Line.""" return f"Line({self.a}, {self.b}, {self.c})" def __str__(self) -> str: - # Keep a readable form; avoid division by zero when possible. + """Keep a readable form; avoid division by zero when possible.""" if _is_close(self.a, 0.0): # by + c = 0 => y = -c/b return f"y = {-self.c / self.b}" @@ -220,8 +229,8 @@ def angle_btw_lines(l1: Line, l2: Line) -> float: raise ValueError("Cannot compute angle for degenerate line.") # Clamp to [-1,1] to protect against tiny numeric drift - cosang = max(-1.0, min(1.0, dot / (n1 * n2))) - ang = math.acos(cosang) + cosine_angle = max(-1.0, min(1.0, dot / (n1 * n2))) + ang = math.acos(cosine_angle) # Return acute angle if ang > math.pi / 2: diff --git a/electricpy/geometry/circle.py b/electricpy/geometry/circle.py index ed8e9d90..980bebb6 100644 --- a/electricpy/geometry/circle.py +++ b/electricpy/geometry/circle.py @@ -1,7 +1,6 @@ ################################################################################ """ -electricpy.geometry.circle - Collection of methods which operate on Cartesian -circles. +electricpy.geometry.circle - Collection of methods for Cartesian circles. >>> import electricpy.geometry.circle as circle @@ -13,7 +12,7 @@ from __future__ import annotations from dataclasses import dataclass -from typing import Generator, Iterable, Optional, Tuple, Union, overload +from typing import Generator, Tuple, Union import math from electricpy import geometry @@ -52,6 +51,7 @@ class Circle: radius : float The radius of the circle (must be >= 0) """ + center: Point radius: float @@ -139,9 +139,9 @@ def is_tangent(self, l: Line, *, tol: float = 1e-9) -> bool: def is_normal(self, l: Line, *, tol: float = 1e-9) -> bool: """ - Return True if the line passes through the circle's center (within tolerance). + Line passes through the circle's center (within tolerance). - IMPORTANT + Important --------- A line being a "normal to the circle" is only well-defined at a specific point of contact. This method keeps backward compatibility with the @@ -175,11 +175,11 @@ def equation(self) -> str: B = -2.0 * k C = const - def _term(coeff: float, var: str) -> str: - if _is_close(coeff, 0.0): + def _term(coef: float, var: str) -> str: + if _is_close(coef, 0.0): return "" - sign = " + " if coeff > 0 else " - " - mag = abs(coeff) + sign = " + " if coef > 0 else " - " + mag = abs(coef) # Prefer integer-like display when possible if _is_close(mag, round(mag)): mag_str = str(int(round(mag))) @@ -343,20 +343,25 @@ def intersetion(self, other) -> Union[None, Point, Tuple[Point, Point], str]: # Dunder methods # ------------------------------------------------------------------------- def __repr__(self) -> str: + """Representation of the Circle.""" return f"Circle(center={self.center}, radius={self.radius})" def __str__(self) -> str: + """Form of the Circle.""" return f"Circle(center={self.center}, radius={self.radius})" def __eq__(self, other: object) -> bool: + """Equality comparison for Circle.""" if isinstance(other, Circle): return self.center == other.center and self.radius == other.radius return False def __ne__(self, other: object) -> bool: + """Inequality comparison for Circle.""" return not self == other def __hash__(self) -> int: + """Hash for Circle.""" return hash((self.center, self.radius)) diff --git a/electricpy/geometry/triangle.py b/electricpy/geometry/triangle.py index 39e3accd..1ee192a4 100644 --- a/electricpy/geometry/triangle.py +++ b/electricpy/geometry/triangle.py @@ -1,7 +1,6 @@ ################################################################################ """ -electricpy.geometry.triangle - Collection of methods which operate on Cartesian -triangles. +electricpy.geometry.triangle - Collection of methods for Cartesian triangles. >>> import electricpy.geometry.triangle as triangle @@ -12,7 +11,7 @@ from __future__ import annotations -from typing import Iterable, Optional, Sequence, Tuple, Union +from typing import Tuple, Union import math from electricpy.geometry import Point, Line @@ -42,9 +41,7 @@ def _is_close(a: float, b: float, *, rel_tol: float = 1e-9, abs_tol: float = 1e- def _triangle_twice_area(p0: Point, p1: Point, p2: Point) -> float: - """ - Return twice the signed area (cross product magnitude). - """ + """Return twice the signed area (cross product magnitude).""" return (p1.x - p0.x) * (p2.y - p0.y) - (p1.y - p0.y) * (p2.x - p0.x) @@ -115,9 +112,7 @@ def perimeter(self) -> float: return self.a + self.b + self.c def perimeters(self) -> float: - """ - Backward-compatible alias for perimeter(). - """ + """Backward-compatible alias for perimeter().""" return self.perimeter() def area(self) -> float: @@ -151,9 +146,9 @@ def centroid(self) -> Point: def in_center(self) -> Point: """ - Return the incenter of the triangle. + Return the in_center of the triangle. - The incenter is the weighted average of vertices by the lengths of + The in_center is the weighted average of vertices by the lengths of the opposite sides. With our naming: a = |p0-p1| opposite vertex p2 b = |p1-p2| opposite vertex p0 @@ -172,7 +167,7 @@ def in_center(self) -> Point: def in_radius(self) -> float: """ - Return the inradius of the triangle. + Return the in_radius of the triangle. r = A / s where s is semiperimeter. """ @@ -184,12 +179,12 @@ def in_radius(self) -> float: def ortho_center(self) -> Point: """ - Return the orthocenter of the triangle. + Return the ortho_center of the triangle. Construct two altitudes: - altitude from p0 to line through p1-p2 - altitude from p1 to line through p0-p2 - Their intersection is the orthocenter. + Their intersection is the ortho_center. Requires Line objects returned by geometry.line_equation to support: - foot_perpendicular(Point) -> Point @@ -207,7 +202,7 @@ def ortho_center(self) -> Point: def circum_center(self) -> Point: """ - Return the circumcenter of the triangle. + Return the circum_center of the triangle. Intersection of perpendicular bisectors of two sides. """ @@ -217,13 +212,13 @@ def circum_center(self) -> Point: def circum_radius(self) -> float: """ - Return the circumradius of the triangle. + Return the circum_radius of the triangle. R = abc / (4A) """ A = self.area() if _is_close(A, 0.0, abs_tol=self._tol): - raise ValueError("Degenerate triangle: area is zero, circumradius undefined") + raise ValueError("Degenerate triangle: area is zero, circum_radius undefined") return (self.a * self.b * self.c) / (4.0 * A) # ------------------------------------------------------------------------- @@ -231,7 +226,9 @@ def circum_radius(self) -> float: # ------------------------------------------------------------------------- def __is_valid(self) -> bool: """ - Validate triangle: + Validate triangle. + + Checks: - triangle inequality with tolerance - non-collinear (non-degenerate) using cross-product area test """ diff --git a/electricpy/math.py b/electricpy/math.py index 9188d577..a825b873 100644 --- a/electricpy/math.py +++ b/electricpy/math.py @@ -49,7 +49,7 @@ def step(t): r""" Step Function [ u(t) ]. - Simple implimentation of numpy.heaviside function to provide standard + Simple implementation of numpy.heaviside function to provide standard step-function as specified to be zero at :math:`x < 0`, and one at :math:`x \geq 0`. @@ -75,7 +75,7 @@ def funcrms(func, T): Root-Mean-Square (RMS) Evaluator for Callable Functions. Integral-based RMS calculator, evaluates the RMS value - of a repetative signal (f) given the signal's specific + of a repetitive signal (f) given the signal's specific period (T) Parameters @@ -89,8 +89,7 @@ def funcrms(func, T): ------- RMS: The RMS value of the function (f) over the interval ( 0, T ) """ - fn = lambda x: func(x) ** 2 - integral, _ = integrate(fn, 0, T) + integral, _ = integrate(lambda x: func(x) ** 2, 0, T) return _np.sqrt(1 / T * integral) @@ -140,7 +139,7 @@ def gausdist(x, mu=0, sigma=1): Returns ------- F: numpy.ndarray - Computed distribution of the gausian function at the + Computed distribution of the gaussian function at the points specified by (array) x """ # Define Integrand @@ -149,7 +148,7 @@ def integrand(sq): try: lx = len(x) # Find length of Input - except: + except TypeError: lx = 1 # Length 1 x = [x] # Pack into list F = _np.zeros(lx, dtype=_np.float64) @@ -157,7 +156,7 @@ def integrand(sq): x_tmp = x[i] # Evaluate X (altered by mu and sigma) X = (x_tmp - mu) / sigma - integral = integrate(integrand, _np.NINF, X) # Integrate + integral = integrate(integrand, -_np.inf, X) # Integrate result = 1 / _np.sqrt(2 * _np.pi) * integral[0] # Evaluate Result F[i] = result # Return only the 0-th value if there's only 1 value available @@ -197,7 +196,7 @@ def probdensity(func, x, x0=0, scale=True): sumx = _np.array([]) try: lx = len(x) # Find length of Input - except: + except TypeError: lx = 1 # Length 1 x = [x] # Pack into list # Recursively Find Probability Density @@ -210,7 +209,7 @@ def probdensity(func, x, x0=0, scale=True): if scale: mx = sumx.max() sumx /= mx - elif scale != False: + elif not scale: sumx /= scale return sumx @@ -220,7 +219,7 @@ def rfft(arr, dt=0.01, absolute=True, resample=True): """ RFFT Function. - This function is designed to evaluat the real FFT + This function is designed to evaluate the real FFT of a input signal in the form of an array or list. Parameters @@ -252,13 +251,13 @@ def rfft(arr, dt=0.01, absolute=True, resample=True): # Evaluate the Downsampling Ratio dn = int(dt * len(arr)) # Downsample to remove unnecessary points - fixedfft = filter.dnsample(fourier, dn) - return (fixedfft) + fixed_fft = filter.dnsample(fourier, dn) + return fixed_fft elif not resample: - return (fourier) + return fourier else: # Condition Resample Value resample = int(resample) # Downsample to remove unnecessary points - fixedfft = filter.dnsample(fourier, resample) - return fixedfft + fixed_fft = filter.dnsample(fourier, resample) + return fixed_fft diff --git a/electricpy/passive.py b/electricpy/passive.py index 65024554..bd344801 100644 --- a/electricpy/passive.py +++ b/electricpy/passive.py @@ -126,7 +126,7 @@ def capbacktoback(C1, C2, Lm, VLN=None, VLL=None): Function to calculate the maximum current and the frequency of the inrush current of two capacitors connected in parallel when one (energized) capacitor - is switched into another (non-engergized) capacitor. + is switched into another (non-energized) capacitor. .. note:: This formula is only valid for three-phase systems. @@ -443,7 +443,7 @@ def air_core_inductance(d: float, coil_l: float, n: int): def air_core_required_length(d: float, L: float, n: int): r""" - Compute Required Length of Air Core Inductor + Compute Required Length of Air Core Inductor. .. math:: l = \frac{1000 d^2 n^2 - 457418 d L}{1016127 L} @@ -467,7 +467,7 @@ def air_core_required_length(d: float, L: float, n: int): def air_core_required_diameter(coil_l: float, L: float, n: int): r""" - Compute Diameter of Air Core Inductor + Compute Diameter of Air Core Inductor. .. math:: 1000 n^2 d^2 - 457418 L d - 1016127 L l = 0 @@ -496,7 +496,7 @@ def air_core_required_diameter(coil_l: float, L: float, n: int): def air_core_required_num_turns(d: float, coil_l: float, L: float): r""" - Compute Required Number of Turns of Air Core Inductor + Compute Required Number of Turns of Air Core Inductor. .. math:: n = \sqrt{\frac{L(1016127 l + 457418 d)}{1000 d^2}} diff --git a/electricpy/sim.py b/electricpy/sim.py index f5b44618..bb076101 100644 --- a/electricpy/sim.py +++ b/electricpy/sim.py @@ -79,7 +79,8 @@ def digifiltersim(fin, filter, freqs, NN=1000, dt=0.01, title="", figsize: tuple, optional The figure dimensions for each subplot, default=None """ - if (figsize != None): _plt.figure(figsize=figsize) + if figsize is not None: + _plt.figure(figsize=figsize) flen = len(freqs) for i in range(flen): # Gather frequency @@ -94,7 +95,7 @@ def digifiltersim(fin, filter, freqs, NN=1000, dt=0.01, title="", x[k] = fin(k * dt, freq) # Identify how many rows were provided - sz = filter.size + sz = len(filter) if isinstance(filter, (tuple, list)) else filter.size if (sz < 5): raise ValueError("ERROR: Too few filter arguments provided. " + "Refer to documentation for proper format.") @@ -131,10 +132,11 @@ def digifiltersim(fin, filter, freqs, NN=1000, dt=0.01, title="", _plt.plot(ytime, 'k', label="Output") _plt.title(title) _plt.grid(which='both') - if legend: _plt.legend(title="Frequency = " + str(freq) + "Hz") - if xlim != False: + if legend: + _plt.legend(title="Frequency = " + str(freq) + "Hz") + if not xlim: _plt.xlim(xlim) - elif xmxscale != None: + elif xmxscale is not None: _plt.xlim((0, xmxscale / (freq * dt))) _plt.tight_layout() @@ -210,7 +212,7 @@ def step_response(system, npts=1000, dt=0.01, combine=True, xlim=False, _plt.grid() _plt.legend() _plt.xlabel("Time (seconds)") - if xlim != False: + if not xlim: _plt.xlim(xlim) _plt.subplot(122) _plt.title(errtitle) @@ -218,10 +220,10 @@ def step_response(system, npts=1000, dt=0.01, combine=True, xlim=False, _plt.grid() _plt.legend() _plt.xlabel("Time (seconds)") - if xlim != False: + if not xlim: _plt.xlim(xlim) _plt.subplots_adjust(wspace=0.3) - if filename != None: + if filename is not None: _plt.savefig(filename) return _plt @@ -295,7 +297,7 @@ def ramp_response(system, npts=1000, dt=0.01, combine=True, xlim=False, _plt.grid() _plt.legend() _plt.xlabel("Time (seconds)") - if xlim != False: + if not xlim: _plt.xlim(xlim) _plt.subplot(122) _plt.title(errtitle) @@ -303,10 +305,10 @@ def ramp_response(system, npts=1000, dt=0.01, combine=True, xlim=False, _plt.grid() _plt.legend() _plt.xlabel("Time (seconds)") - if xlim != False: + if not xlim: _plt.xlim(xlim) _plt.subplots_adjust(wspace=0.3) - if filename != None: + if filename is not None: _plt.savefig(filename) return _plt @@ -379,7 +381,7 @@ def parabolic_response(system, npts=1000, dt=0.01, combine=True, xlim=False, _plt.grid() _plt.legend() _plt.xlabel("Time (seconds)") - if xlim != False: + if not xlim: _plt.xlim(xlim) _plt.subplot(122) _plt.title(errtitle) @@ -387,10 +389,10 @@ def parabolic_response(system, npts=1000, dt=0.01, combine=True, xlim=False, _plt.grid() _plt.legend() _plt.xlabel("Time (seconds)") - if xlim != False: + if not xlim: _plt.xlim(xlim) _plt.subplots_adjust(wspace=0.3) - if filename != None: + if filename is not None: _plt.savefig(filename) return _plt @@ -650,58 +652,58 @@ def func_c(self, x): # Concatenated Function # Plot Forcing Functions if (plotforcing): - fffig = _plt.figure("Forcing Functions") + _ = _plt.figure("Forcing Functions") if fnc > 1: for x in range(fnc): _plt.plot(TT, fn_arr[x], label="f" + str(x + 1)) else: _plt.plot(TT, fn_arr, label="f1") - if xlim != False: + if not xlim: _plt.xlim(xlim) - if ylim != False: + if not ylim: _plt.ylim(ylim) _plt.title("Forcing Functions " + title) _plt.xlabel("Time (seconds)") _plt.legend(title="Forcing Functions") _plt.grid() - if filename != None: + if filename is not None: _plt.savefig('Simulation Forcing Functions.png') if plotstate: _plt.show() # Plot each state-variable over time - stvfig = _plt.figure("State Variables") + _ = _plt.figure("State Variables") for x in range(xtim_len): _plt.plot(TT, xtim[x], label="x" + str(x + 1)) - if xlim != False: + if not xlim: _plt.xlim(xlim) - if ylim != False: + if not ylim: _plt.ylim(ylim) _plt.title("Simulated Output Terms " + soltype[solution] + title) _plt.xlabel("Time (seconds)") _plt.legend(title="State Variable") _plt.grid() - if filename != None: + if filename is not None: _plt.savefig('Simulation Terms.png') if plotstate: _plt.show() # Plot combined output if (plotresult and solution == 3): - cofig = _plt.figure("Combined Output") + _ = _plt.figure("Combined Output") C = _np.asarray(C) # convert back to array for operation for i in range(cC): yout = yout + xtim[i] * C[0][i] # Sum all st-space var mult. by their coeff yout = _np.asarray(yout) # convert output to array for plotting purposes _plt.plot(TT, yout[0]) - if xlim != False: + if not xlim: _plt.xlim(xlim) - if ylim != False: + if not ylim: _plt.ylim(ylim) _plt.title("Combined Output " + title) _plt.xlabel("Time (seconds)") _plt.grid() - if filename != None: + if filename is not None: _plt.savefig('Simulation Combined Output.png') if plotresult: _plt.show() @@ -956,7 +958,6 @@ def func_c(self, x): # Concatenated Function Q_funcs = [] P_strgs = [] Q_strgs = [] - Vi_list = [] lists = [P_strgs, Q_strgs] i = 0 # Index ii = 0 # String Index @@ -984,12 +985,12 @@ def func_c(self, x): # Concatenated Function Padd = False Qadd = False for _j in range(N): - if P_list[_k] == None: + if P_list[_k] is None: continue # Don't Generate Requirements for Slack Bus if (_k != _j) and not Padd: # Skip i,i Terms ang_len += 1 Padd = True - if (_k != _j) and (Q_list[_k] != None) and not Qadd: + if (_k != _j) and (Q_list[_k] is not None) and not Qadd: mag_len += 1 Qadd = True Vxdim = ang_len + mag_len @@ -1004,20 +1005,21 @@ def func_c(self, x): # Concatenated Function # Add New Entry To Lists for LST in lists: LST.append(None) - if P_list[_k] == None: + if P_list[_k] is None: continue # Don't Generate Requirements for Slack Bus # Collect Other Terms Yind = "[{}][{}]".format(_k, _j) - if verbose: print("K:", _k, "\tJ:", _j) + if verbose: + print("K:", _k, "\tJ:", _j) # Generate Voltage-Related Strings if _k != _j: # Skip i,i Terms # Generate K-Related Strings - if V_list[_k][0] == None: # The Vk magnitude is unknown + if V_list[_k][0] is None: # The Vk magnitude is unknown Vkm = "Vx[{}]".format(_k + ang_len - magoff * _k) # Use Variable Magnitude Vka = "Vx[{}]".format(_k - angoff) # Use Variable Angle else: # The Vk magnitude is known Vkm = "V_list[{}][0]".format(_k) # Load Magnitude - if V_list[_k][1] == None: # The Vj angle is unknown + if V_list[_k][1] is None: # The Vj angle is unknown Vka = "Vx[{}]".format(_k - angoff) # Use Variable Angle else: Vka = "V_list[{}][1]".format(_k) # Load Angle @@ -1033,7 +1035,8 @@ def func_c(self, x): # Concatenated Function Vja = "V_list[{}][1]".format(_j) # Load Angle # Generate String and Append to List of Functions P_strgs[i] = (Pstr.format(Vkm, Vka, Vjm, Vja, Yind)) - if verbose: print("New P-String:", P_strgs[i]) + if verbose: + print("New P-String:", P_strgs[i]) # Generate Q Requirement if Q_list[_k] is not None: # Generate String and Append to List of Functions @@ -1043,7 +1046,8 @@ def func_c(self, x): # Concatenated Function else: Qgen = "" Q_strgs[i] = (Qstr.format(Vkm, Vka, Vjm, Vja, Yind, Qgen)) - if verbose: print("New Q-String:", Q_strgs[i]) + if verbose: + print("New Q-String:", Q_strgs[i]) # Increment Index at Each Interior Level i += 1 tempPstr = "P_funcs.append(lambda Vx: -P_list[{0}]".format(_k) @@ -1058,10 +1062,12 @@ def func_c(self, x): # Concatenated Function tempPstr += ")" tempQstr += ")" if any(P_strgs[ii:i]): - if verbose: print("Full P-Func Str:", tempPstr) + if verbose: + print("Full P-Func Str:", tempPstr) exec(tempPstr) if any(Q_strgs[ii:i]): - if verbose: print("Full Q-Func Str:", tempQstr) + if verbose: + print("Full Q-Func Str:", tempQstr) exec(tempQstr) ii = i # Increase Lower Index retset = (P_funcs, Q_funcs) diff --git a/pyproject.toml b/pyproject.toml index 2ca60057..93b36e57 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,10 +30,10 @@ classifiers = [ dynamic = ["version"] dependencies = [ - "NumPy", + "numpy", "matplotlib", - "SciPy", - "SymPy", + "scipy", + "sympy", "numdifftools", ] @@ -41,6 +41,35 @@ dependencies = [ numerical = ["numdifftools"] fault = ["arcflash"] full = ["numdifftools", "arcflash"] +dev = [ + "pytest >=2.7.3", + "xdoctest >= 1.1.3", + "pytest-pydocstyle >= 2.3.2", + "pygments >= 2.18.0", + "wheel", + "sphinx", + "myst-parser[linkify]", + "sphinx-sitemap", + "sphinx-git", + "sphinx-immaterial", + "coverage-badge", + "numpy", + "matplotlib", + "scipy", + "sympy", + "numdifftools", +] + +[tool.pytest.ini_options] +testpaths = [ + "test", + "electricpy", +] +addopts = "--pydocstyle --xdoctest" + +[tool.pydocstyle] +convention = "numpy" +match-dir = "electricpy/" [project.urls] Home = "https://electricpy.readthedocs.io/en/latest/index.html" diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index ace01b04..00000000 --- a/requirements.txt +++ /dev/null @@ -1,5 +0,0 @@ -NumPy -matplotlib -SciPy -SymPy -numdifftools diff --git a/test/__init__.py b/test/__init__.py index 7fbe89ec..ea0e4fa7 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -1,6 +1,5 @@ from electricpy.geometry import Point from electricpy.geometry import Line -import numpy as np from numpy.testing import assert_almost_equal def compare_points(p1: Point, p2: Point) -> bool: diff --git a/test/requirements.txt b/test/requirements.txt deleted file mode 100644 index 5225ed33..00000000 --- a/test/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -pytest -xdoctest \ No newline at end of file diff --git a/test/test_compute.py b/test/test_compute.py index 1364baed..4e28d589 100644 --- a/test/test_compute.py +++ b/test/test_compute.py @@ -27,6 +27,17 @@ def test_crc_sender_and_remainder(): assert set(remainder) == {"0"} -def test_string_to_bits(): - bits = compute.string_to_bits("A") - assert bits == "01000001" +@pytest.mark.parametrize("character_string, expected_bits", [ + ("A", "01000001"), + ("B", "01000010"), + ("C", "01000011"), + ("a", "01100001"), + ("b", "01100010"), + ("c", "01100011"), + ("0", "00110000"), + ("1", "00110001"), + ("2", "00110010"), +]) +def test_string_to_bits(character_string, expected_bits): + bits = compute.string_to_bits(character_string) + assert bits == expected_bits diff --git a/test/test_sim.py b/test/test_sim.py index 4ecfafe3..9cd1a8e5 100644 --- a/test/test_sim.py +++ b/test/test_sim.py @@ -7,10 +7,14 @@ def test_digifiltersim_basic(): - fin = lambda t, f: np.sin(2 * np.pi * f * t) - filter_row = [0.0, 0.0, 1.0, 0.0, 0.0] - - plot_module = sim.digifiltersim(fin, filter_row, freqs=[1], NN=10, dt=0.1, legend=False) + plot_module = sim.digifiltersim( + lambda t, f: np.sin(2 * np.pi * f * t), + [0.0, 0.0, 1.0, 0.0, 0.0], + freqs=[1], + NN=10, + dt=0.1, + legend=False + ) assert plot_module is not None