diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 0000000..0675901 --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,49 @@ +# Contributing to gearpy + +## Reporting issues + +When reporting issues please include as much detail as possible about +your operating system, python version, dependencies version and geapy +version. +Whenever possible, please also include a brief, self-contained code +example that demonstrates the problem. + +## Contributing code + +Thanks for your interest in contributing code to gearpy! + +Please be sure to follow the convention for commit messages, similar to +the [numpy commit message convention](https://numpy.org/devdocs/dev/development_workflow.html#writing-the-commit-message). +Commit messages should be clear and follow a few basic rules. Example: + +``` +ENH: add functionality X gearpy.. + +The first line of the commit message starts with a capitalized acronym +(options listed below) indicating what type of commit this is. Then a +blank line, then more text if needed. Lines shouldn't be longer than 72 +characters. If the commit is related to a ticket, indicate that with +"See #3456", "See ticket 3456", "Closes #3456" or similar. +``` + +Describing the motivation for a change, the nature of a bug for bug +fixes or some details on what an enhancement does are also good to +include in a commit message. Messages should be understandable without +looking at the code changes. A commit message like +`MNT: fixed another one` is an example of what not to do; the reader has +to go look for context elsewhere. +Standard acronyms to start the commit message with are: + +``` +BUG: bug fix +DEP: deprecate something or remove a deprecated object +DEV: development tool or utility +DOC: documentation +ENH: enhancement +MNT: maintenance commit (refactoring, typos, etc.) +REV: revert an earlier commit +STY: style fix (whitespace, PEP8) +TST: addition or modification of tests +REL: related to releasing gearpy +MRG: merging commit +``` diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..9681c74 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,50 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: bug +assignees: '' + +--- + +Your issue may already be reported! +Please search on the [issue tracker](../) before creating one. + +## Bug Description + + +## Expected Behavior + + + +## Current Behavior + + + +## Possible Solution + + + +## Steps to Reproduce + + +1. +2. +3. +4. + +## Context + + + +## Your Environment + +* Operating System and version: +* Python version: +* Package version: +* Dependencies version: +* gearpy installation type: + - [ ] `pip install gearpy` + - [ ] from source (.tar.gz) + - [ ] `git clone` + \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..74088cd --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: enhancement +assignees: '' + +--- + +## Is your feature request related to a problem? + + +## Describe the solution you'd like + + +## Describe alternatives you've considered + + +## Teachability, Documentation, Adoption + diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..908978d --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,28 @@ + + +## Description + + +## Motivation and Context + + + +## How has this been tested? + + + + +## Types of changes + +- [ ] Bug fix (non-breaking change which fixes an issue) +- [ ] New feature (non-breaking change which adds functionality) +- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) + +## Checklist: + + +- [ ] My code follows the code style of this project +- [ ] My change requires a change to the documentation +- [ ] I have updated the documentation accordingly +- [ ] I have added tests to cover my changes +- [ ] All new and existing tests passed diff --git a/README.md b/README.md index 66b90e3..681378a 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,11 @@ -A python package for mechanical transmission analysis +# Mechanical Transmission Analysis + +[![PyPi](https://img.shields.io/pypi/v/gearpy)](https://pypi.org/project/gearpy/) +[![PyPi](https://img.shields.io/pypi/pyversions/gearpy.svg)](https://pypi.org/project/gearpy/) +[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://github.com/AndreaBlengino/gearpy/blob/v0.1.0/LICENSE) [![Tests](https://github.com/AndreaBlengino/gearpy/actions/workflows/tests.yml/badge.svg?branch=master)](https://github.com/AndreaBlengino/gearpy/actions/workflows/tests.yml) +[![Documentation Status](https://readthedocs.org/projects/gearpy/badge/?version=latest)](https://gearpy.readthedocs.io/en/latest/?badge=latest) [![codecov](https://codecov.io/gh/AndreaBlengino/gearpy/graph/badge.svg?token=4JHzii1LrK)](https://codecov.io/gh/AndreaBlengino/gearpy) + +**gearpy** is a python package for mechanical transmission analysis. diff --git a/docs/source/conf.py b/docs/source/conf.py index b7538bb..bd686e0 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -3,26 +3,35 @@ # For the full list of built-in configuration values, see the documentation: # https://www.sphinx-doc.org/en/master/usage/configuration.html +import os +import sys +import subprocess + +sys.path.insert(0, os.path.abspath('../..')) + + # -- Project information ----------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information project = 'gearpy' copyright = '2023, Andrea Blengino' author = 'Andrea Blengino' -release = '0.0.1' +version = subprocess.run(['git', 'describe', '--tags'], stdout = subprocess.PIPE).stdout.decode('utf-8').split('-')[0] + # -- General configuration --------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration -extensions = [] +extensions = ['sphinx.ext.autodoc', 'm2r2'] templates_path = ['_templates'] exclude_patterns = [] - # -- Options for HTML output ------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output -html_theme = 'alabaster' +html_theme = 'furo' html_static_path = ['_static'] +add_module_names = False +html_title = 'gearpy' diff --git a/docs/source/gear/SpurGear/angular_acceleration.rst b/docs/source/gear/SpurGear/angular_acceleration.rst new file mode 100644 index 0000000..5b4044e --- /dev/null +++ b/docs/source/gear/SpurGear/angular_acceleration.rst @@ -0,0 +1,7 @@ +angular_acceleration +==================== + + +.. currentmodule:: gearpy.gear.spur_gear + +.. autoproperty:: SpurGear.angular_acceleration diff --git a/docs/source/gear/SpurGear/angular_position.rst b/docs/source/gear/SpurGear/angular_position.rst new file mode 100644 index 0000000..a1fd430 --- /dev/null +++ b/docs/source/gear/SpurGear/angular_position.rst @@ -0,0 +1,7 @@ +angular_position +================ + + +.. currentmodule:: gearpy.gear.spur_gear + +.. autoproperty:: SpurGear.angular_position diff --git a/docs/source/gear/SpurGear/angular_speed.rst b/docs/source/gear/SpurGear/angular_speed.rst new file mode 100644 index 0000000..bd12b97 --- /dev/null +++ b/docs/source/gear/SpurGear/angular_speed.rst @@ -0,0 +1,7 @@ +angular_speed +============= + + +.. currentmodule:: gearpy.gear.spur_gear + +.. autoproperty:: SpurGear.angular_speed diff --git a/docs/source/gear/SpurGear/driven_by.rst b/docs/source/gear/SpurGear/driven_by.rst new file mode 100644 index 0000000..35cac07 --- /dev/null +++ b/docs/source/gear/SpurGear/driven_by.rst @@ -0,0 +1,7 @@ +driven_by +========= + + +.. currentmodule:: gearpy.gear.spur_gear + +.. autoproperty:: SpurGear.driven_by diff --git a/docs/source/gear/SpurGear/drives.rst b/docs/source/gear/SpurGear/drives.rst new file mode 100644 index 0000000..ada8b11 --- /dev/null +++ b/docs/source/gear/SpurGear/drives.rst @@ -0,0 +1,7 @@ +drives +====== + + +.. currentmodule:: gearpy.gear.spur_gear + +.. autoproperty:: SpurGear.drives diff --git a/docs/source/gear/SpurGear/driving_torque.rst b/docs/source/gear/SpurGear/driving_torque.rst new file mode 100644 index 0000000..5f1a208 --- /dev/null +++ b/docs/source/gear/SpurGear/driving_torque.rst @@ -0,0 +1,7 @@ +driving_torque +============== + + +.. currentmodule:: gearpy.gear.spur_gear + +.. autoproperty:: SpurGear.driving_torque diff --git a/docs/source/gear/SpurGear/external_torque.rst b/docs/source/gear/SpurGear/external_torque.rst new file mode 100644 index 0000000..6672824 --- /dev/null +++ b/docs/source/gear/SpurGear/external_torque.rst @@ -0,0 +1,7 @@ +external_torque +=============== + + +.. currentmodule:: gearpy.gear.spur_gear + +.. autoproperty:: SpurGear.driving_torque diff --git a/docs/source/gear/SpurGear/index.rst b/docs/source/gear/SpurGear/index.rst new file mode 100644 index 0000000..9384739 --- /dev/null +++ b/docs/source/gear/SpurGear/index.rst @@ -0,0 +1,31 @@ +SpurGear +======== + + +.. currentmodule:: gearpy.gear +.. autoclass:: gearpy.gear.spur_gear.SpurGear + :members: + :undoc-members: + :show-inheritance: + :no-index: + + +.. toctree:: + :hidden: + + angular_acceleration + angular_position + angular_speed + driven_by + drives + driving_torque + external_torque + inertia_moment + load_torque + master_gear_efficiency + master_gear_ratio + n_teeth + name + time_variables + torque + update_time_variables diff --git a/docs/source/gear/SpurGear/inertia_moment.rst b/docs/source/gear/SpurGear/inertia_moment.rst new file mode 100644 index 0000000..e1e0ca0 --- /dev/null +++ b/docs/source/gear/SpurGear/inertia_moment.rst @@ -0,0 +1,7 @@ +inertia_moment +============== + + +.. currentmodule:: gearpy.gear.spur_gear + +.. autoproperty:: SpurGear.inertia_moment diff --git a/docs/source/gear/SpurGear/load_torque.rst b/docs/source/gear/SpurGear/load_torque.rst new file mode 100644 index 0000000..d88afda --- /dev/null +++ b/docs/source/gear/SpurGear/load_torque.rst @@ -0,0 +1,7 @@ +load_torque +=========== + + +.. currentmodule:: gearpy.gear.spur_gear + +.. autoproperty:: SpurGear.load_torque diff --git a/docs/source/gear/SpurGear/master_gear_efficiency.rst b/docs/source/gear/SpurGear/master_gear_efficiency.rst new file mode 100644 index 0000000..771d454 --- /dev/null +++ b/docs/source/gear/SpurGear/master_gear_efficiency.rst @@ -0,0 +1,7 @@ +master_gear_efficiency +====================== + + +.. currentmodule:: gearpy.gear.spur_gear + +.. autoproperty:: SpurGear.master_gear_efficiency diff --git a/docs/source/gear/SpurGear/master_gear_ratio.rst b/docs/source/gear/SpurGear/master_gear_ratio.rst new file mode 100644 index 0000000..f46d585 --- /dev/null +++ b/docs/source/gear/SpurGear/master_gear_ratio.rst @@ -0,0 +1,7 @@ +master_gear_ratio +================= + + +.. currentmodule:: gearpy.gear.spur_gear + +.. autoproperty:: SpurGear.master_gear_ratio diff --git a/docs/source/gear/SpurGear/n_teeth.rst b/docs/source/gear/SpurGear/n_teeth.rst new file mode 100644 index 0000000..8203ac6 --- /dev/null +++ b/docs/source/gear/SpurGear/n_teeth.rst @@ -0,0 +1,7 @@ +n_teeth +======= + + +.. currentmodule:: gearpy.gear.spur_gear + +.. autoproperty:: SpurGear.n_teeth diff --git a/docs/source/gear/SpurGear/name.rst b/docs/source/gear/SpurGear/name.rst new file mode 100644 index 0000000..c5b1841 --- /dev/null +++ b/docs/source/gear/SpurGear/name.rst @@ -0,0 +1,7 @@ +name +==== + + +.. currentmodule:: gearpy.gear.spur_gear + +.. autoproperty:: SpurGear.name diff --git a/docs/source/gear/SpurGear/time_variables.rst b/docs/source/gear/SpurGear/time_variables.rst new file mode 100644 index 0000000..4203162 --- /dev/null +++ b/docs/source/gear/SpurGear/time_variables.rst @@ -0,0 +1,7 @@ +time_variables +============== + + +.. currentmodule:: gearpy.gear.spur_gear + +.. autoproperty:: SpurGear.time_variables diff --git a/docs/source/gear/SpurGear/torque.rst b/docs/source/gear/SpurGear/torque.rst new file mode 100644 index 0000000..da048c6 --- /dev/null +++ b/docs/source/gear/SpurGear/torque.rst @@ -0,0 +1,7 @@ +torque +====== + + +.. currentmodule:: gearpy.gear.spur_gear + +.. autoproperty:: SpurGear.torque diff --git a/docs/source/gear/SpurGear/update_time_variables.rst b/docs/source/gear/SpurGear/update_time_variables.rst new file mode 100644 index 0000000..73a9185 --- /dev/null +++ b/docs/source/gear/SpurGear/update_time_variables.rst @@ -0,0 +1,7 @@ +update_time_variables +===================== + + +.. currentmodule:: gearpy.gear.spur_gear + +.. automethod:: SpurGear.update_time_variables diff --git a/docs/source/gear/index.rst b/docs/source/gear/index.rst new file mode 100644 index 0000000..8c6fa18 --- /dev/null +++ b/docs/source/gear/index.rst @@ -0,0 +1,16 @@ +gear +==== + + +.. currentmodule:: gearpy.gear +.. autoclass:: gearpy.gear.spur_gear.SpurGear + :members: + :undoc-members: + :show-inheritance: + :no-index: + + +.. toctree:: + :hidden: + + SpurGear/index diff --git a/docs/source/gearpy.rst b/docs/source/gearpy.rst new file mode 100644 index 0000000..c16cb85 --- /dev/null +++ b/docs/source/gearpy.rst @@ -0,0 +1,13 @@ +gearpy +====== + + +.. toctree:: + :hidden: + + gear/index + motor/index + solver/index + transmission/index + units/index + utils/index \ No newline at end of file diff --git a/docs/source/index.rst b/docs/source/index.rst index d16ecce..207a199 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -1,15 +1,35 @@ -Welcome to gearpy's documentation! -================================== +******************** +gearpy documentation +******************** + + +**Date**: |today| + +**Version**: |version| + + +.. toctree:: + :hidden: + :caption: Module + + gearpy + .. toctree:: - :maxdepth: 2 - :caption: Contents: + :hidden: + :caption: References + + Source Code + PyPI + Issue Tracker + license + +.. mdinclude:: ../../README.md Indices and tables ================== -* :ref:`genindex` * :ref:`modindex` * :ref:`search` diff --git a/docs/source/license.rst b/docs/source/license.rst new file mode 100644 index 0000000..d363ed4 --- /dev/null +++ b/docs/source/license.rst @@ -0,0 +1,10 @@ +License +======= + + +GNU General Public License v3.0 +------------------------------- + + +.. include:: ../../LICENSE + :literal: diff --git a/docs/source/motor/DCMotor/angular_acceleration.rst b/docs/source/motor/DCMotor/angular_acceleration.rst new file mode 100644 index 0000000..9ccb018 --- /dev/null +++ b/docs/source/motor/DCMotor/angular_acceleration.rst @@ -0,0 +1,7 @@ +angular_acceleration +==================== + + +.. currentmodule:: gearpy.motor.dc_motor + +.. autoproperty:: DCMotor.angular_acceleration diff --git a/docs/source/motor/DCMotor/angular_position.rst b/docs/source/motor/DCMotor/angular_position.rst new file mode 100644 index 0000000..e015d79 --- /dev/null +++ b/docs/source/motor/DCMotor/angular_position.rst @@ -0,0 +1,7 @@ +angular_position +================ + + +.. currentmodule:: gearpy.motor.dc_motor + +.. autoproperty:: DCMotor.angular_position diff --git a/docs/source/motor/DCMotor/angular_speed.rst b/docs/source/motor/DCMotor/angular_speed.rst new file mode 100644 index 0000000..907e51e --- /dev/null +++ b/docs/source/motor/DCMotor/angular_speed.rst @@ -0,0 +1,7 @@ +angular_speed +============= + + +.. currentmodule:: gearpy.motor.dc_motor + +.. autoproperty:: DCMotor.angular_speed diff --git a/docs/source/motor/DCMotor/compute_torque.rst b/docs/source/motor/DCMotor/compute_torque.rst new file mode 100644 index 0000000..066ec0c --- /dev/null +++ b/docs/source/motor/DCMotor/compute_torque.rst @@ -0,0 +1,7 @@ +compute_torque +============== + + +.. currentmodule:: gearpy.motor.dc_motor + +.. automethod:: DCMotor.compute_torque diff --git a/docs/source/motor/DCMotor/drives.rst b/docs/source/motor/DCMotor/drives.rst new file mode 100644 index 0000000..e0b717e --- /dev/null +++ b/docs/source/motor/DCMotor/drives.rst @@ -0,0 +1,7 @@ +drives +====== + + +.. currentmodule:: gearpy.motor.dc_motor + +.. autoproperty:: DCMotor.drives diff --git a/docs/source/motor/DCMotor/driving_torque.rst b/docs/source/motor/DCMotor/driving_torque.rst new file mode 100644 index 0000000..2d949e7 --- /dev/null +++ b/docs/source/motor/DCMotor/driving_torque.rst @@ -0,0 +1,7 @@ +driving_torque +============== + + +.. currentmodule:: gearpy.motor.dc_motor + +.. autoproperty:: DCMotor.driving_torque diff --git a/docs/source/motor/DCMotor/index.rst b/docs/source/motor/DCMotor/index.rst new file mode 100644 index 0000000..88b82a9 --- /dev/null +++ b/docs/source/motor/DCMotor/index.rst @@ -0,0 +1,27 @@ +DCMotor +======= + + +.. currentmodule:: gearpy.motor +.. autoclass:: gearpy.motor.dc_motor.DCMotor + :members: + :undoc-members: + :show-inheritance: + :no-index: + + +.. toctree:: + :hidden: + + angular_acceleration + angular_position + angular_speed + compute_torque + drives + driving_torque + inertia_moment + load_torque + name + time_variables + torque + update_time_variables diff --git a/docs/source/motor/DCMotor/inertia_moment.rst b/docs/source/motor/DCMotor/inertia_moment.rst new file mode 100644 index 0000000..15b0c75 --- /dev/null +++ b/docs/source/motor/DCMotor/inertia_moment.rst @@ -0,0 +1,7 @@ +inertia_moment +============== + + +.. currentmodule:: gearpy.motor.dc_motor + +.. autoproperty:: DCMotor.inertia_moment diff --git a/docs/source/motor/DCMotor/load_torque.rst b/docs/source/motor/DCMotor/load_torque.rst new file mode 100644 index 0000000..dc6c298 --- /dev/null +++ b/docs/source/motor/DCMotor/load_torque.rst @@ -0,0 +1,7 @@ +load_torque +=========== + + +.. currentmodule:: gearpy.motor.dc_motor + +.. autoproperty:: DCMotor.load_torque diff --git a/docs/source/motor/DCMotor/name.rst b/docs/source/motor/DCMotor/name.rst new file mode 100644 index 0000000..df5faee --- /dev/null +++ b/docs/source/motor/DCMotor/name.rst @@ -0,0 +1,7 @@ +name +==== + + +.. currentmodule:: gearpy.motor.dc_motor + +.. autoproperty:: DCMotor.name diff --git a/docs/source/motor/DCMotor/time_variables.rst b/docs/source/motor/DCMotor/time_variables.rst new file mode 100644 index 0000000..00e70f0 --- /dev/null +++ b/docs/source/motor/DCMotor/time_variables.rst @@ -0,0 +1,7 @@ +time_variables +============== + + +.. currentmodule:: gearpy.motor.dc_motor + +.. autoproperty:: DCMotor.time_variables diff --git a/docs/source/motor/DCMotor/torque.rst b/docs/source/motor/DCMotor/torque.rst new file mode 100644 index 0000000..2b62fdb --- /dev/null +++ b/docs/source/motor/DCMotor/torque.rst @@ -0,0 +1,7 @@ +torque +====== + + +.. currentmodule:: gearpy.motor.dc_motor + +.. autoproperty:: DCMotor.torque diff --git a/docs/source/motor/DCMotor/update_time_variables.rst b/docs/source/motor/DCMotor/update_time_variables.rst new file mode 100644 index 0000000..1642de9 --- /dev/null +++ b/docs/source/motor/DCMotor/update_time_variables.rst @@ -0,0 +1,7 @@ +update_time_variables +===================== + + +.. currentmodule:: gearpy.motor.dc_motor + +.. automethod:: DCMotor.update_time_variables diff --git a/docs/source/motor/index.rst b/docs/source/motor/index.rst new file mode 100644 index 0000000..508de91 --- /dev/null +++ b/docs/source/motor/index.rst @@ -0,0 +1,16 @@ +motor +===== + + +.. currentmodule:: gearpy.motor +.. autoclass:: gearpy.motor.dc_motor.DCMotor + :members: + :undoc-members: + :show-inheritance: + :no-index: + + +.. toctree:: + :hidden: + + DCMotor/index diff --git a/docs/source/requirements.txt b/docs/source/requirements.txt index f3d691e..ced87c5 100644 --- a/docs/source/requirements.txt +++ b/docs/source/requirements.txt @@ -1 +1,3 @@ sphinx==7.2.6 +m2r2==0.3.3.post2 +furo==2023.9.10 diff --git a/docs/source/solver/Solver/index.rst b/docs/source/solver/Solver/index.rst new file mode 100644 index 0000000..e2b5ea5 --- /dev/null +++ b/docs/source/solver/Solver/index.rst @@ -0,0 +1,16 @@ +Solver +====== + + +.. currentmodule:: gearpy.solver +.. autoclass:: gearpy.solver.solver.Solver + :members: + :undoc-members: + :show-inheritance: + :no-index: + + +.. toctree:: + :hidden: + + run diff --git a/docs/source/solver/Solver/run.rst b/docs/source/solver/Solver/run.rst new file mode 100644 index 0000000..7e0b620 --- /dev/null +++ b/docs/source/solver/Solver/run.rst @@ -0,0 +1,7 @@ +run +=== + + +.. currentmodule:: gearpy.solver.solver + +.. automethod:: Solver.run diff --git a/docs/source/solver/index.rst b/docs/source/solver/index.rst new file mode 100644 index 0000000..e624ed4 --- /dev/null +++ b/docs/source/solver/index.rst @@ -0,0 +1,16 @@ +solver +====== + + +.. currentmodule:: gearpy.solver +.. autoclass:: gearpy.solver.solver.Solver + :members: + :undoc-members: + :show-inheritance: + :no-index: + + +.. toctree:: + :hidden: + + Solver/index diff --git a/docs/source/transmission/Transmission/chain.rst b/docs/source/transmission/Transmission/chain.rst new file mode 100644 index 0000000..9c494e7 --- /dev/null +++ b/docs/source/transmission/Transmission/chain.rst @@ -0,0 +1,7 @@ +chain +===== + + +.. currentmodule:: gearpy.transmission.transmission + +.. autoproperty:: Transmission.chain diff --git a/docs/source/transmission/Transmission/index.rst b/docs/source/transmission/Transmission/index.rst new file mode 100644 index 0000000..ab46f85 --- /dev/null +++ b/docs/source/transmission/Transmission/index.rst @@ -0,0 +1,16 @@ +Transmission +============ + + +.. currentmodule:: gearpy.transmission +.. autoclass:: gearpy.transmission.transmission.Transmission + :members: + :undoc-members: + :show-inheritance: + :no-index: + + +.. toctree:: + :hidden: + + chain diff --git a/docs/source/transmission/index.rst b/docs/source/transmission/index.rst new file mode 100644 index 0000000..4115342 --- /dev/null +++ b/docs/source/transmission/index.rst @@ -0,0 +1,16 @@ +transmission +============ + + +.. currentmodule:: gearpy.transmission +.. autoclass:: gearpy.transmission.transmission.Transmission + :members: + :undoc-members: + :show-inheritance: + :no-index: + + +.. toctree:: + :hidden: + + Transmission/index diff --git a/docs/source/units/AngularAcceleration/index.rst b/docs/source/units/AngularAcceleration/index.rst new file mode 100644 index 0000000..bc592de --- /dev/null +++ b/docs/source/units/AngularAcceleration/index.rst @@ -0,0 +1,18 @@ +AngularAcceleration +=================== + + +.. currentmodule:: gearpy.units +.. autoclass:: gearpy.units.concrete_units.AngularAcceleration + :members: + :undoc-members: + :show-inheritance: + :no-index: + + +.. toctree:: + :hidden: + + to + unit + value diff --git a/docs/source/units/AngularAcceleration/to.rst b/docs/source/units/AngularAcceleration/to.rst new file mode 100644 index 0000000..4283900 --- /dev/null +++ b/docs/source/units/AngularAcceleration/to.rst @@ -0,0 +1,7 @@ +to +== + + +.. currentmodule:: gearpy.units.concrete_units + +.. automethod:: AngularAcceleration.to diff --git a/docs/source/units/AngularAcceleration/unit.rst b/docs/source/units/AngularAcceleration/unit.rst new file mode 100644 index 0000000..5b4158e --- /dev/null +++ b/docs/source/units/AngularAcceleration/unit.rst @@ -0,0 +1,7 @@ +unit +==== + + +.. currentmodule:: gearpy.units.concrete_units + +.. autoproperty:: AngularAcceleration.unit diff --git a/docs/source/units/AngularAcceleration/value.rst b/docs/source/units/AngularAcceleration/value.rst new file mode 100644 index 0000000..0e5e0bd --- /dev/null +++ b/docs/source/units/AngularAcceleration/value.rst @@ -0,0 +1,7 @@ +value +===== + + +.. currentmodule:: gearpy.units.concrete_units + +.. autoproperty:: AngularAcceleration.value diff --git a/docs/source/units/AngularPosition/index.rst b/docs/source/units/AngularPosition/index.rst new file mode 100644 index 0000000..3a0317d --- /dev/null +++ b/docs/source/units/AngularPosition/index.rst @@ -0,0 +1,18 @@ +AngularPosition +=============== + + +.. currentmodule:: gearpy.units +.. autoclass:: gearpy.units.concrete_units.AngularPosition + :members: + :undoc-members: + :show-inheritance: + :no-index: + + +.. toctree:: + :hidden: + + to + unit + value diff --git a/docs/source/units/AngularPosition/to.rst b/docs/source/units/AngularPosition/to.rst new file mode 100644 index 0000000..418a226 --- /dev/null +++ b/docs/source/units/AngularPosition/to.rst @@ -0,0 +1,7 @@ +to +== + + +.. currentmodule:: gearpy.units.concrete_units + +.. automethod:: AngularPosition.to diff --git a/docs/source/units/AngularPosition/unit.rst b/docs/source/units/AngularPosition/unit.rst new file mode 100644 index 0000000..e94ce47 --- /dev/null +++ b/docs/source/units/AngularPosition/unit.rst @@ -0,0 +1,7 @@ +unit +==== + + +.. currentmodule:: gearpy.units.concrete_units + +.. autoproperty:: AngularPosition.unit diff --git a/docs/source/units/AngularPosition/value.rst b/docs/source/units/AngularPosition/value.rst new file mode 100644 index 0000000..88b8ae3 --- /dev/null +++ b/docs/source/units/AngularPosition/value.rst @@ -0,0 +1,7 @@ +value +===== + + +.. currentmodule:: gearpy.units.concrete_units + +.. autoproperty:: AngularPosition.value diff --git a/docs/source/units/AngularSpeed/index.rst b/docs/source/units/AngularSpeed/index.rst new file mode 100644 index 0000000..0c44ed8 --- /dev/null +++ b/docs/source/units/AngularSpeed/index.rst @@ -0,0 +1,18 @@ +AngularSpeed +============ + + +.. currentmodule:: gearpy.units +.. autoclass:: gearpy.units.concrete_units.AngularSpeed + :members: + :undoc-members: + :show-inheritance: + :no-index: + + +.. toctree:: + :hidden: + + to + unit + value diff --git a/docs/source/units/AngularSpeed/to.rst b/docs/source/units/AngularSpeed/to.rst new file mode 100644 index 0000000..2bba839 --- /dev/null +++ b/docs/source/units/AngularSpeed/to.rst @@ -0,0 +1,7 @@ +to +== + + +.. currentmodule:: gearpy.units.concrete_units + +.. automethod:: AngularSpeed.to diff --git a/docs/source/units/AngularSpeed/unit.rst b/docs/source/units/AngularSpeed/unit.rst new file mode 100644 index 0000000..ad691e6 --- /dev/null +++ b/docs/source/units/AngularSpeed/unit.rst @@ -0,0 +1,7 @@ +unit +==== + + +.. currentmodule:: gearpy.units.concrete_units + +.. autoproperty:: AngularSpeed.unit diff --git a/docs/source/units/AngularSpeed/value.rst b/docs/source/units/AngularSpeed/value.rst new file mode 100644 index 0000000..f41067c --- /dev/null +++ b/docs/source/units/AngularSpeed/value.rst @@ -0,0 +1,7 @@ +value +===== + + +.. currentmodule:: gearpy.units.concrete_units + +.. autoproperty:: AngularSpeed.value diff --git a/docs/source/units/InertiaMoment/index.rst b/docs/source/units/InertiaMoment/index.rst new file mode 100644 index 0000000..50afd70 --- /dev/null +++ b/docs/source/units/InertiaMoment/index.rst @@ -0,0 +1,18 @@ +InertiaMoment +============= + + +.. currentmodule:: gearpy.units +.. autoclass:: gearpy.units.concrete_units.InertiaMoment + :members: + :undoc-members: + :show-inheritance: + :no-index: + + +.. toctree:: + :hidden: + + to + unit + value diff --git a/docs/source/units/InertiaMoment/to.rst b/docs/source/units/InertiaMoment/to.rst new file mode 100644 index 0000000..92499bd --- /dev/null +++ b/docs/source/units/InertiaMoment/to.rst @@ -0,0 +1,7 @@ +to +== + + +.. currentmodule:: gearpy.units.concrete_units + +.. automethod:: InertiaMoment.to diff --git a/docs/source/units/InertiaMoment/unit.rst b/docs/source/units/InertiaMoment/unit.rst new file mode 100644 index 0000000..240d09c --- /dev/null +++ b/docs/source/units/InertiaMoment/unit.rst @@ -0,0 +1,7 @@ +unit +==== + + +.. currentmodule:: gearpy.units.concrete_units + +.. autoproperty:: InertiaMoment.unit diff --git a/docs/source/units/InertiaMoment/value.rst b/docs/source/units/InertiaMoment/value.rst new file mode 100644 index 0000000..c512d3d --- /dev/null +++ b/docs/source/units/InertiaMoment/value.rst @@ -0,0 +1,7 @@ +value +===== + + +.. currentmodule:: gearpy.units.concrete_units + +.. autoproperty:: InertiaMoment.value diff --git a/docs/source/units/Time/index.rst b/docs/source/units/Time/index.rst new file mode 100644 index 0000000..f5ebcfa --- /dev/null +++ b/docs/source/units/Time/index.rst @@ -0,0 +1,18 @@ +Time +==== + + +.. currentmodule:: gearpy.units +.. autoclass:: gearpy.units.concrete_units.Time + :members: + :undoc-members: + :show-inheritance: + :no-index: + + +.. toctree:: + :hidden: + + to + unit + value diff --git a/docs/source/units/Time/to.rst b/docs/source/units/Time/to.rst new file mode 100644 index 0000000..6cb331e --- /dev/null +++ b/docs/source/units/Time/to.rst @@ -0,0 +1,7 @@ +to +== + + +.. currentmodule:: gearpy.units.concrete_units + +.. automethod:: Time.to diff --git a/docs/source/units/Time/unit.rst b/docs/source/units/Time/unit.rst new file mode 100644 index 0000000..8a9db29 --- /dev/null +++ b/docs/source/units/Time/unit.rst @@ -0,0 +1,7 @@ +unit +==== + + +.. currentmodule:: gearpy.units.concrete_units + +.. autoproperty:: Time.unit diff --git a/docs/source/units/Time/value.rst b/docs/source/units/Time/value.rst new file mode 100644 index 0000000..8771b64 --- /dev/null +++ b/docs/source/units/Time/value.rst @@ -0,0 +1,7 @@ +value +===== + + +.. currentmodule:: gearpy.units.concrete_units + +.. autoproperty:: Time.value diff --git a/docs/source/units/TimeInterval/index.rst b/docs/source/units/TimeInterval/index.rst new file mode 100644 index 0000000..4c5a651 --- /dev/null +++ b/docs/source/units/TimeInterval/index.rst @@ -0,0 +1,18 @@ +TimeInterval +============ + + +.. currentmodule:: gearpy.units +.. autoclass:: gearpy.units.concrete_units.TimeInterval + :members: + :undoc-members: + :show-inheritance: + :no-index: + + +.. toctree:: + :hidden: + + to + unit + value diff --git a/docs/source/units/TimeInterval/to.rst b/docs/source/units/TimeInterval/to.rst new file mode 100644 index 0000000..2354e67 --- /dev/null +++ b/docs/source/units/TimeInterval/to.rst @@ -0,0 +1,7 @@ +to +== + + +.. currentmodule:: gearpy.units.concrete_units + +.. automethod:: TimeInterval.to diff --git a/docs/source/units/TimeInterval/unit.rst b/docs/source/units/TimeInterval/unit.rst new file mode 100644 index 0000000..fead4d8 --- /dev/null +++ b/docs/source/units/TimeInterval/unit.rst @@ -0,0 +1,7 @@ +unit +==== + + +.. currentmodule:: gearpy.units.concrete_units + +.. autoproperty:: TimeInterval.unit diff --git a/docs/source/units/TimeInterval/value.rst b/docs/source/units/TimeInterval/value.rst new file mode 100644 index 0000000..8ec820a --- /dev/null +++ b/docs/source/units/TimeInterval/value.rst @@ -0,0 +1,7 @@ +value +===== + + +.. currentmodule:: gearpy.units.concrete_units + +.. autoproperty:: TimeInterval.value diff --git a/docs/source/units/Torque/index.rst b/docs/source/units/Torque/index.rst new file mode 100644 index 0000000..cfeeb09 --- /dev/null +++ b/docs/source/units/Torque/index.rst @@ -0,0 +1,18 @@ +Torque +====== + + +.. currentmodule:: gearpy.units +.. autoclass:: gearpy.units.concrete_units.Torque + :members: + :undoc-members: + :show-inheritance: + :no-index: + + +.. toctree:: + :hidden: + + to + unit + value diff --git a/docs/source/units/Torque/to.rst b/docs/source/units/Torque/to.rst new file mode 100644 index 0000000..ab2edbf --- /dev/null +++ b/docs/source/units/Torque/to.rst @@ -0,0 +1,7 @@ +to +== + + +.. currentmodule:: gearpy.units.concrete_units + +.. automethod:: Torque.to diff --git a/docs/source/units/Torque/unit.rst b/docs/source/units/Torque/unit.rst new file mode 100644 index 0000000..348e204 --- /dev/null +++ b/docs/source/units/Torque/unit.rst @@ -0,0 +1,7 @@ +unit +==== + + +.. currentmodule:: gearpy.units.concrete_units + +.. autoproperty:: Torque.unit diff --git a/docs/source/units/Torque/value.rst b/docs/source/units/Torque/value.rst new file mode 100644 index 0000000..731aa9b --- /dev/null +++ b/docs/source/units/Torque/value.rst @@ -0,0 +1,7 @@ +value +===== + + +.. currentmodule:: gearpy.units.concrete_units + +.. autoproperty:: Torque.value diff --git a/docs/source/units/index.rst b/docs/source/units/index.rst new file mode 100644 index 0000000..41cc499 --- /dev/null +++ b/docs/source/units/index.rst @@ -0,0 +1,20 @@ +units +===== + + +.. automodule:: gearpy.units + :members: + :undoc-members: + :show-inheritance: + + +.. toctree:: + :hidden: + + AngularAcceleration/index + AngularPosition/index + AngularSpeed/index + InertiaMoment/index + Time/index + TimeInterval/index + Torque/index diff --git a/docs/source/utils/add_fixed_joint.rst b/docs/source/utils/add_fixed_joint.rst new file mode 100644 index 0000000..e623b23 --- /dev/null +++ b/docs/source/utils/add_fixed_joint.rst @@ -0,0 +1,7 @@ +add_fixed_joint +=============== + + +.. currentmodule:: gearpy.utils.relations + +.. autofunction:: add_fixed_joint diff --git a/docs/source/utils/add_gear_mating.rst b/docs/source/utils/add_gear_mating.rst new file mode 100644 index 0000000..cb7d9e3 --- /dev/null +++ b/docs/source/utils/add_gear_mating.rst @@ -0,0 +1,7 @@ +add_gear_mating +=============== + + +.. currentmodule:: gearpy.utils.relations + +.. autofunction:: add_gear_mating diff --git a/docs/source/utils/index.rst b/docs/source/utils/index.rst new file mode 100644 index 0000000..e0b9b76 --- /dev/null +++ b/docs/source/utils/index.rst @@ -0,0 +1,15 @@ +utils +===== + + +.. automodule:: gearpy.utils + :members: + :undoc-members: + :show-inheritance: + + +.. toctree:: + :hidden: + + add_fixed_joint + add_gear_mating diff --git a/gearpy/__init__.py b/gearpy/__init__.py index 9b484e4..e5c5d0d 100644 --- a/gearpy/__init__.py +++ b/gearpy/__init__.py @@ -1,5 +1,7 @@ -from .gear.spur_gear import SpurGear -from .utils import add_fixed_joint, add_gear_mating -from .motor.dc_motor import DCMotor -from .solver.solver import Solver -from .transmission.transmission import Transmission +from gearpy import gear +from gearpy import mechanical_object +from gearpy import motor +from gearpy import solver +from gearpy import transmission +from gearpy import units +from gearpy import utils diff --git a/gearpy/gear/__init__.py b/gearpy/gear/__init__.py index e69de29..b499520 100644 --- a/gearpy/gear/__init__.py +++ b/gearpy/gear/__init__.py @@ -0,0 +1,2 @@ +from .gear_base import GearBase +from .spur_gear import SpurGear diff --git a/gearpy/gear/gear.py b/gearpy/gear/gear_base.py similarity index 55% rename from gearpy/gear/gear.py rename to gearpy/gear/gear_base.py index 3f67c3a..9a87bd6 100644 --- a/gearpy/gear/gear.py +++ b/gearpy/gear/gear_base.py @@ -1,13 +1,14 @@ from abc import abstractmethod -from gearpy.mechanical_object.rotating_object import RotatingObject +from gearpy.mechanical_object import RotatingObject +from gearpy.units import AngularPosition, AngularSpeed, AngularAcceleration, InertiaMoment, Torque from typing import Callable, Union class GearBase(RotatingObject): @abstractmethod - def __init__(self, name: str, n_teeth: int, inertia: float): - super().__init__(name = name, inertia = inertia) + def __init__(self, name: str, n_teeth: int, inertia_moment: InertiaMoment): + super().__init__(name = name, inertia_moment = inertia_moment) if not isinstance(n_teeth, int): raise TypeError("Parameter 'n_teeth' must be an integer.") @@ -36,7 +37,7 @@ def driven_by(self) -> RotatingObject: @abstractmethod def driven_by(self, driven_by: RotatingObject): if not isinstance(driven_by, RotatingObject): - raise TypeError("Parameter 'drives' must be a RotatingObject") + raise TypeError(f"Parameter 'driven_by' must be a {RotatingObject.__name__!r}") self.__driven_by = driven_by @@ -49,10 +50,70 @@ def drives(self) -> RotatingObject: @abstractmethod def drives(self, drives: RotatingObject): if not isinstance(drives, RotatingObject): - raise TypeError("Parameter 'drives' must be a RotatingObject") + raise TypeError(f"Parameter 'drives' must be a {RotatingObject.__name__!r}") self.__drives = drives + @property + @abstractmethod + def angular_position(self) -> AngularPosition: + return super().angular_position + + @angular_position.setter + @abstractmethod + def angular_position(self, angular_position: AngularPosition): + super(GearBase, type(self)).angular_position.fset(self, angular_position) + + @property + @abstractmethod + def angular_speed(self) -> AngularSpeed: + return super().angular_speed + + @angular_speed.setter + @abstractmethod + def angular_speed(self, angular_speed: AngularSpeed): + super(GearBase, type(self)).angular_speed.fset(self, angular_speed) + + @property + @abstractmethod + def angular_acceleration(self) -> AngularAcceleration: + return super().angular_acceleration + + @angular_acceleration.setter + @abstractmethod + def angular_acceleration(self, angular_acceleration: AngularAcceleration): + super(GearBase, type(self)).angular_acceleration.fset(self, angular_acceleration) + + @property + @abstractmethod + def torque(self) -> Torque: + return super().torque + + @torque.setter + @abstractmethod + def torque(self, torque: Torque): + super(GearBase, type(self)).torque.fset(self, torque) + + @property + @abstractmethod + def driving_torque(self) -> Torque: + return super().driving_torque + + @driving_torque.setter + @abstractmethod + def driving_torque(self, driving_torque: Torque): + super(GearBase, type(self)).driving_torque.fset(self, driving_torque) + + @property + @abstractmethod + def load_torque(self) -> Torque: + return super().load_torque + + @load_torque.setter + @abstractmethod + def load_torque(self, load_torque: Torque): + super(GearBase, type(self)).load_torque.fset(self, load_torque) + @property @abstractmethod def master_gear_ratio(self) -> float: diff --git a/gearpy/gear/spur_gear.py b/gearpy/gear/spur_gear.py index 37abf1b..f987838 100644 --- a/gearpy/gear/spur_gear.py +++ b/gearpy/gear/spur_gear.py @@ -1,12 +1,13 @@ -from .gear import GearBase -from gearpy.mechanical_object.rotating_object import RotatingObject +from gearpy.gear import GearBase +from gearpy.mechanical_object import RotatingObject +from gearpy.units import AngularPosition, AngularSpeed, AngularAcceleration, InertiaMoment, Torque from typing import Callable, Union class SpurGear(GearBase): - def __init__(self, name: str, n_teeth: int, inertia: float): - super().__init__(name = name, n_teeth = n_teeth, inertia = inertia) + def __init__(self, name: str, n_teeth: int, inertia_moment: InertiaMoment): + super().__init__(name = name, n_teeth = n_teeth, inertia_moment = inertia_moment) @property def name(self) -> str: @@ -49,56 +50,56 @@ def master_gear_efficiency(self, master_gear_efficiency: Union[float, int]): super(SpurGear, type(self)).master_gear_efficiency.fset(self, master_gear_efficiency) @property - def angle(self) -> Union[float, int]: - return super().angle + def angular_position(self) -> AngularPosition: + return super().angular_position - @angle.setter - def angle(self, angle: Union[float, int]): - super(SpurGear, type(self)).angle.fset(self, angle) + @angular_position.setter + def angular_position(self, angular_position: AngularPosition): + super(SpurGear, type(self)).angular_position.fset(self, angular_position) @property - def speed(self) -> Union[float, int]: - return super().speed + def angular_speed(self) -> AngularSpeed: + return super().angular_speed - @speed.setter - def speed(self, speed: Union[float, int]): - super(SpurGear, type(self)).speed.fset(self, speed) + @angular_speed.setter + def angular_speed(self, angular_speed: AngularSpeed): + super(SpurGear, type(self)).angular_speed.fset(self, angular_speed) @property - def acceleration(self) -> Union[float, int]: - return super().acceleration + def angular_acceleration(self) -> AngularAcceleration: + return super().angular_acceleration - @acceleration.setter - def acceleration(self, acceleration: Union[float, int]): - super(SpurGear, type(self)).acceleration.fset(self, acceleration) + @angular_acceleration.setter + def angular_acceleration(self, angular_acceleration: AngularAcceleration): + super(SpurGear, type(self)).angular_acceleration.fset(self, angular_acceleration) @property - def torque(self) -> Union[float, int]: + def torque(self) -> Torque: return super().torque @torque.setter - def torque(self, torque: Union[float, int]): + def torque(self, torque: Torque): super(SpurGear, type(self)).torque.fset(self, torque) @property - def driving_torque(self) -> Union[float, int]: + def driving_torque(self) -> Torque: return super().driving_torque @driving_torque.setter - def driving_torque(self, driving_torque: Union[float, int]): + def driving_torque(self, driving_torque: Torque): super(SpurGear, type(self)).driving_torque.fset(self, driving_torque) @property - def load_torque(self) -> Union[float, int]: + def load_torque(self) -> Torque: return super().load_torque @load_torque.setter - def load_torque(self, load_torque: Union[float, int]): + def load_torque(self, load_torque: Torque): super(SpurGear, type(self)).load_torque.fset(self, load_torque) @property - def inertia(self) -> Union[float, int]: - return super().inertia + def inertia_moment(self) -> InertiaMoment: + return super().inertia_moment @property def external_torque(self) -> Callable: diff --git a/gearpy/mechanical_object/__init__.py b/gearpy/mechanical_object/__init__.py index e69de29..de526a2 100644 --- a/gearpy/mechanical_object/__init__.py +++ b/gearpy/mechanical_object/__init__.py @@ -0,0 +1,2 @@ +from .mechanical_object import MechanicalObject +from .rotating_object import RotatingObject diff --git a/gearpy/mechanical_object/rotating_object.py b/gearpy/mechanical_object/rotating_object.py index ab1c752..7734eeb 100644 --- a/gearpy/mechanical_object/rotating_object.py +++ b/gearpy/mechanical_object/rotating_object.py @@ -1,116 +1,114 @@ from abc import abstractmethod +from gearpy.units import AngularPosition, AngularSpeed, AngularAcceleration, InertiaMoment, Torque from .mechanical_object import MechanicalObject -from typing import Union class RotatingObject(MechanicalObject): @abstractmethod - def __init__(self, name: str, inertia: Union[float, int]): + def __init__(self, name: str, inertia_moment: InertiaMoment): super().__init__(name = name) - if not isinstance(inertia, float) and not isinstance(inertia, int): - raise TypeError("Parameter 'inertia' must be a float or an integer.") + if not isinstance(inertia_moment, InertiaMoment): + raise TypeError(f"Parameter 'inertia_moment' must be an instance of {InertiaMoment.__name__!r}.") - if inertia <= 0: - raise ValueError("Parameter 'inertia' must be positive.") - - self.__angle = None - self.__speed = None - self.__acceleration = None + self.__angular_position = None + self.__angular_speed = None + self.__angular_acceleration = None self.__torque = None self.__driving_torque = None self.__load_torque = None - self.__inertia = inertia - self.__time_variables = {'angle': [], - 'speed': [], - 'acceleration': [], + self.__inertia_moment = inertia_moment + self.__time_variables = {'angular position': [], + 'angular speed': [], + 'angular acceleration': [], 'torque': [], 'driving torque': [], 'load torque': []} @property @abstractmethod - def angle(self) -> Union[float, int]: - return self.__angle + def angular_position(self) -> AngularPosition: + return self.__angular_position - @angle.setter + @angular_position.setter @abstractmethod - def angle(self, angle: Union[float, int]): - if not isinstance(angle, float) and not isinstance(angle, int): - raise TypeError("Parameter 'angle' must be a float or an integer.") + def angular_position(self, angular_position: AngularPosition): + if not isinstance(angular_position, AngularPosition): + raise TypeError(f"Parameter 'angular_position' must be an instance of {AngularPosition.__name__!r}.") - self.__angle = angle + self.__angular_position = angular_position @property @abstractmethod - def speed(self) -> Union[float, int]: - return self.__speed + def angular_speed(self) -> AngularSpeed: + return self.__angular_speed - @speed.setter + @angular_speed.setter @abstractmethod - def speed(self, speed: Union[float, int]): - if not isinstance(speed, float) and not isinstance(speed, int): - raise TypeError("Parameter 'speed' must be a float or an integer.") + def angular_speed(self, angular_speed: AngularSpeed): + if not isinstance(angular_speed, AngularSpeed): + raise TypeError(f"Parameter 'angular_speed' must be an instance of {AngularSpeed.__name__!r}.") - self.__speed = speed + self.__angular_speed = angular_speed @property @abstractmethod - def acceleration(self) -> Union[float, int]: - return self.__acceleration + def angular_acceleration(self) -> AngularAcceleration: + return self.__angular_acceleration - @acceleration.setter + @angular_acceleration.setter @abstractmethod - def acceleration(self, acceleration: Union[float, int]): - if not isinstance(acceleration, float) and not isinstance(acceleration, int): - raise TypeError("Parameter 'acceleration' must be a float or an integer.") + def angular_acceleration(self, angular_acceleration: AngularAcceleration): + if not isinstance(angular_acceleration, AngularAcceleration): + raise TypeError(f"Parameter 'angular_acceleration' must be an instance of " + f"{AngularAcceleration.__name__!r}.") - self.__acceleration = acceleration + self.__angular_acceleration = angular_acceleration @property @abstractmethod - def torque(self) -> Union[float, int]: + def torque(self) -> Torque: return self.__torque @torque.setter @abstractmethod - def torque(self, torque: Union[float, int]): - if not isinstance(torque, float) and not isinstance(torque, int): - raise TypeError("Parameter 'torque' must be a float or an integer.") + def torque(self, torque: Torque): + if not isinstance(torque, Torque): + raise TypeError(f"Parameter 'torque' must be an instance of {Torque.__name__!r}.") self.__torque = torque @property @abstractmethod - def driving_torque(self) -> Union[float, int]: + def driving_torque(self) -> Torque: return self.__driving_torque @driving_torque.setter @abstractmethod - def driving_torque(self, driving_torque: Union[float, int]): - if not isinstance(driving_torque, float) and not isinstance(driving_torque, int): - raise TypeError("Parameter 'driving_torque' must be a float or an integer.") + def driving_torque(self, driving_torque: Torque): + if not isinstance(driving_torque, Torque): + raise TypeError(f"Parameter 'driving_torque' must be an instance of {Torque.__name__!r}.") self.__driving_torque = driving_torque @property @abstractmethod - def load_torque(self) -> Union[float, int]: + def load_torque(self) -> Torque: return self.__load_torque @load_torque.setter @abstractmethod - def load_torque(self, load_torque: Union[float, int]): - if not isinstance(load_torque, float) and not isinstance(load_torque, int): - raise TypeError("Parameter 'load_torque' must be a float or an integer.") + def load_torque(self, load_torque: Torque): + if not isinstance(load_torque, Torque): + raise TypeError(f"Parameter 'load_torque' must be an instance of {Torque.__name__!r}.") self.__load_torque = load_torque @property @abstractmethod - def inertia(self) -> Union[float, int]: - return self.__inertia + def inertia_moment(self) -> InertiaMoment: + return self.__inertia_moment @property @abstractmethod @@ -119,9 +117,9 @@ def time_variables(self) -> dict: @abstractmethod def update_time_variables(self): - self.__time_variables['angle'].append(self.__angle) - self.__time_variables['speed'].append(self.__speed) - self.__time_variables['acceleration'].append(self.__acceleration) + self.__time_variables['angular position'].append(self.__angular_position) + self.__time_variables['angular speed'].append(self.__angular_speed) + self.__time_variables['angular acceleration'].append(self.__angular_acceleration) self.__time_variables['torque'].append(self.__torque) self.__time_variables['driving torque'].append(self.__driving_torque) self.__time_variables['load torque'].append(self.__load_torque) diff --git a/gearpy/motor/__init__.py b/gearpy/motor/__init__.py index e69de29..0731c80 100644 --- a/gearpy/motor/__init__.py +++ b/gearpy/motor/__init__.py @@ -0,0 +1,2 @@ +from .motor_base import MotorBase +from .dc_motor import DCMotor diff --git a/gearpy/motor/dc_motor.py b/gearpy/motor/dc_motor.py index 0e9cffe..f93fc6b 100644 --- a/gearpy/motor/dc_motor.py +++ b/gearpy/motor/dc_motor.py @@ -1,24 +1,23 @@ -from gearpy.mechanical_object.rotating_object import RotatingObject -from .motor import MotorBase -from typing import Union +from gearpy.mechanical_object import RotatingObject +from gearpy.motor import MotorBase +from gearpy.units import AngularPosition, AngularSpeed, AngularAcceleration, InertiaMoment, Torque class DCMotor(MotorBase): - def __init__(self, name: str, inertia: Union[float, int], no_load_speed: Union[float, int], - maximum_torque: Union[float, int]): - super().__init__(name = name, inertia = inertia) + def __init__(self, name: str, inertia_moment: InertiaMoment, no_load_speed: AngularSpeed, maximum_torque: Torque): + super().__init__(name = name, inertia_moment = inertia_moment) - if not isinstance(no_load_speed, float) and not isinstance(no_load_speed, int): - raise TypeError("Parameter 'no_load_speed' must be a float or an integer.") + if not isinstance(no_load_speed, AngularSpeed): + raise TypeError(f"Parameter 'no_load_speed' must be an instance of {AngularSpeed.__name__!r}") - if not isinstance(maximum_torque, float) and not isinstance(maximum_torque, int): - raise TypeError("Parameter 'maximum_torque' must be a float or an integer.") + if not isinstance(maximum_torque, Torque): + raise TypeError(f"Parameter 'maximum_torque' must be an instance of {Torque.__name__!r}.") - if no_load_speed <= 0: + if no_load_speed.value <= 0: raise ValueError("Parameter 'no_load_speed' must be positive.") - if maximum_torque <= 0: + if maximum_torque.value <= 0: raise ValueError("Parameter 'maximum_torque' must be positive.") self.__no_load_speed = no_load_speed @@ -37,67 +36,69 @@ def drives(self, drives: RotatingObject): super(DCMotor, type(self)).drives.fset(self, drives) @property - def angle(self) -> Union[float, int]: - return super().angle + def angular_position(self) -> AngularPosition: + return super().angular_position - @angle.setter - def angle(self, angle: Union[float, int]): - super(DCMotor, type(self)).angle.fset(self, angle) + @angular_position.setter + def angular_position(self, angular_position: AngularPosition): + super(DCMotor, type(self)).angular_position.fset(self, angular_position) @property - def speed(self) -> Union[float, int]: - return super().speed + def angular_speed(self) -> AngularSpeed: + return super().angular_speed - @speed.setter - def speed(self, speed: Union[float, int]): - super(DCMotor, type(self)).speed.fset(self, speed) + @angular_speed.setter + def angular_speed(self, angular_speed: AngularSpeed): + super(DCMotor, type(self)).angular_speed.fset(self, angular_speed) @property - def acceleration(self) -> Union[float, int]: - return super().acceleration + def angular_acceleration(self) -> AngularAcceleration: + return super().angular_acceleration - @acceleration.setter - def acceleration(self, acceleration: Union[float, int]): - super(DCMotor, type(self)).acceleration.fset(self, acceleration) + @angular_acceleration.setter + def angular_acceleration(self, angular_acceleration: AngularAcceleration): + super(DCMotor, type(self)).angular_acceleration.fset(self, angular_acceleration) @property - def no_load_speed(self) -> Union[float, int]: + def no_load_speed(self) -> AngularSpeed: return self.__no_load_speed @property - def maximum_torque(self) -> Union[float, int]: + def maximum_torque(self) -> Torque: return self.__maximum_torque @property - def torque(self) -> Union[float, int]: + def torque(self) -> Torque: return super().torque @torque.setter - def torque(self, torque: Union[float, int]): + def torque(self, torque: Torque): super(DCMotor, type(self)).torque.fset(self, torque) @property - def driving_torque(self) -> Union[float, int]: + def driving_torque(self) -> Torque: return super().driving_torque @driving_torque.setter - def driving_torque(self, driving_torque: Union[float, int]): + def driving_torque(self, driving_torque: Torque): super(DCMotor, type(self)).driving_torque.fset(self, driving_torque) @property - def load_torque(self) -> Union[float, int]: + def load_torque(self) -> Torque: return super().load_torque @load_torque.setter - def load_torque(self, load_torque: Union[float, int]): + def load_torque(self, load_torque: Torque): super(DCMotor, type(self)).load_torque.fset(self, load_torque) @property - def inertia(self) -> Union[float, int]: - return super().inertia + def inertia_moment(self) -> InertiaMoment: + return super().inertia_moment - def compute_torque(self) -> Union[float, int]: - return (1 - self.speed/self.__no_load_speed)*self.__maximum_torque + def compute_torque(self) -> Torque: + return Torque(value = (1 - self.angular_speed.to('rad/s').value/self.__no_load_speed.to('rad/s').value)* + self.__maximum_torque.value, + unit = self.__maximum_torque.unit) @property def time_variables(self) -> dict: diff --git a/gearpy/motor/motor.py b/gearpy/motor/motor.py deleted file mode 100644 index 6ee7907..0000000 --- a/gearpy/motor/motor.py +++ /dev/null @@ -1,27 +0,0 @@ -from abc import abstractmethod -from gearpy.mechanical_object.rotating_object import RotatingObject -from typing import Union - - -class MotorBase(RotatingObject): - - @abstractmethod - def __init__(self, name: str, inertia: Union[float, int]): - super().__init__(name = name, inertia = inertia) - self.__drives = None - - @property - @abstractmethod - def drives(self) -> RotatingObject: - return self.__drives - - @drives.setter - @abstractmethod - def drives(self, drives: RotatingObject): - if not isinstance(drives, RotatingObject): - raise TypeError("Parameter 'drives' must be a RotatingObject") - - self.__drives = drives - - @abstractmethod - def compute_torque(self): ... diff --git a/gearpy/motor/motor_base.py b/gearpy/motor/motor_base.py new file mode 100644 index 0000000..8295ce5 --- /dev/null +++ b/gearpy/motor/motor_base.py @@ -0,0 +1,87 @@ +from abc import abstractmethod +from gearpy.mechanical_object import RotatingObject +from gearpy.units import AngularPosition, AngularSpeed, AngularAcceleration, InertiaMoment, Torque + + +class MotorBase(RotatingObject): + + @abstractmethod + def __init__(self, name: str, inertia_moment: InertiaMoment): + super().__init__(name = name, inertia_moment = inertia_moment) + self.__drives = None + + @property + @abstractmethod + def drives(self) -> RotatingObject: + return self.__drives + + @drives.setter + @abstractmethod + def drives(self, drives: RotatingObject): + if not isinstance(drives, RotatingObject): + raise TypeError(f"Parameter 'drives' must be a {RotatingObject.__name__!r}") + + self.__drives = drives + + @property + @abstractmethod + def angular_position(self) -> AngularPosition: + return super().angular_position + + @angular_position.setter + @abstractmethod + def angular_position(self, angular_position: AngularPosition): + super(MotorBase, type(self)).angular_position.fset(self, angular_position) + + @property + @abstractmethod + def angular_speed(self) -> AngularSpeed: + return super().angular_speed + + @angular_speed.setter + @abstractmethod + def angular_speed(self, angular_speed: AngularSpeed): + super(MotorBase, type(self)).angular_speed.fset(self, angular_speed) + + @property + @abstractmethod + def angular_acceleration(self) -> AngularAcceleration: + return super().angular_acceleration + + @angular_acceleration.setter + @abstractmethod + def angular_acceleration(self, angular_acceleration: AngularAcceleration): + super(MotorBase, type(self)).angular_acceleration.fset(self, angular_acceleration) + + @property + @abstractmethod + def torque(self) -> Torque: + return super().torque + + @torque.setter + @abstractmethod + def torque(self, torque: Torque): + super(MotorBase, type(self)).torque.fset(self, torque) + + @property + @abstractmethod + def driving_torque(self) -> Torque: + return super().driving_torque + + @driving_torque.setter + @abstractmethod + def driving_torque(self, driving_torque: Torque): + super(MotorBase, type(self)).driving_torque.fset(self, driving_torque) + + @property + @abstractmethod + def load_torque(self) -> Torque: + return super().load_torque + + @load_torque.setter + @abstractmethod + def load_torque(self, load_torque: Torque): + super(MotorBase, type(self)).load_torque.fset(self, load_torque) + + @abstractmethod + def compute_torque(self): ... diff --git a/gearpy/solver/__init__.py b/gearpy/solver/__init__.py index e69de29..70089f3 100644 --- a/gearpy/solver/__init__.py +++ b/gearpy/solver/__init__.py @@ -0,0 +1 @@ +from .solver import Solver diff --git a/gearpy/solver/solver.py b/gearpy/solver/solver.py index ad1b56e..b697ce5 100644 --- a/gearpy/solver/solver.py +++ b/gearpy/solver/solver.py @@ -1,29 +1,21 @@ -from gearpy.mechanical_object.rotating_object import RotatingObject -from gearpy.motor.motor import MotorBase -from gearpy.transmission.transmission import Transmission +from gearpy.mechanical_object import RotatingObject +from gearpy.motor import MotorBase +from gearpy.transmission import Transmission +from gearpy.units import Time, TimeInterval import numpy as np -from typing import Union class Solver: - def __init__(self, time_discretization: Union[float, int], - simulation_time: Union[float, int], - transmission: Transmission): - if not isinstance(time_discretization, float) and not isinstance(time_discretization, int): - raise TypeError("Parameter 'time_discretization' must be a float or an integer.") + def __init__(self, time_discretization: TimeInterval, simulation_time: TimeInterval, transmission: Transmission): + if not isinstance(time_discretization, TimeInterval): + raise TypeError(f"Parameter 'time_discretization' must be an instance of {TimeInterval.__name__!r}.") - if not isinstance(simulation_time, float) and not isinstance(simulation_time, int): - raise TypeError("Parameter 'simulation_time' must be a float or an integer.") + if not isinstance(simulation_time, TimeInterval): + raise TypeError(f"Parameter 'simulation_time' must be an instance of {TimeInterval.__name__!r}.") if not isinstance(transmission, Transmission): - raise TypeError("Parameter 'transmission' must be an instance of Transmission.") - - if time_discretization <= 0: - raise ValueError("Parameter 'time_discretization' must be positive.") - - if simulation_time <= 0: - raise ValueError("Parameter 'time_simulation' must be positive.") + raise TypeError(f"Parameter 'transmission' must be an instance of {Transmission.__name__!r}.") if time_discretization >= simulation_time: raise ValueError("Parameter 'time_discretization' cannot be greater or equal to 'simulation_time'.") @@ -32,15 +24,15 @@ def __init__(self, time_discretization: Union[float, int], raise ValueError("Parameter 'transmission.chain' cannot be an empty list.") if not isinstance(transmission.chain[0], MotorBase): - raise TypeError("First element in 'transmission' must be an instance of MotorBase.") + raise TypeError(f"First element in 'transmission' must be an instance of {MotorBase.__name__!r}.") if not all([isinstance(item, RotatingObject) for item in transmission.chain]): - raise TypeError("All elements of 'transmission' must be instances of RotatingObject.") + raise TypeError(f"All elements of 'transmission' must be instances of {RotatingObject.__name__!r}.") self.time_discretization = time_discretization self.simulation_time = simulation_time self.transmission_chain = transmission.chain - self.time = [0] + self.time = [Time(value = 0, unit = time_discretization.unit)] def run(self): @@ -48,9 +40,9 @@ def run(self): self._compute_transmission_initial_state() self._update_time_variables() - for k in np.arange(self.time_discretization, self.simulation_time, self.time_discretization): + for k in np.arange(self.time_discretization.value, self.simulation_time.value, self.time_discretization.value): - self.time.append(k) + self.time.append(Time(value = float(k), unit = self.time_discretization.unit)) self._compute_kinematic_variables() self._compute_driving_torque() @@ -61,27 +53,28 @@ def run(self): def _compute_transmission_inertia(self): - self.transmission_inertia = self.transmission_chain[0].inertia + self.transmission_inertia_moment = self.transmission_chain[0].inertia_moment for item in self.transmission_chain[1:]: - self.transmission_inertia *= item.master_gear_ratio - self.transmission_inertia += item.inertia + self.transmission_inertia_moment *= item.master_gear_ratio + self.transmission_inertia_moment += item.inertia_moment def _compute_transmission_initial_state(self): for i in range(len(self.transmission_chain) - 2, -1, -1): gear_ratio = self.transmission_chain[i + 1].master_gear_ratio - self._compute_angle(gear_ratio = gear_ratio, i = i) - self._compute_speed(gear_ratio = gear_ratio, i = i) + self._compute_angular_position(gear_ratio = gear_ratio, i = i) + self._compute_angular_speed(gear_ratio = gear_ratio, i = i) self._compute_driving_torque() self._compute_load_torque() self._compute_torque() - self.transmission_chain[-1].acceleration = self.transmission_chain[-1].torque/self.transmission_inertia + self.transmission_chain[-1].angular_acceleration = self.transmission_chain[-1].torque/\ + self.transmission_inertia_moment for i in range(len(self.transmission_chain) - 2, -1, -1): gear_ratio = self.transmission_chain[i + 1].master_gear_ratio - self._compute_acceleration(gear_ratio = gear_ratio, i = i) + self._compute_angular_acceleration(gear_ratio = gear_ratio, i = i) def _update_time_variables(self): @@ -92,21 +85,21 @@ def _compute_kinematic_variables(self): for i in range(len(self.transmission_chain) - 2, -1, -1): gear_ratio = self.transmission_chain[i + 1].master_gear_ratio - self._compute_angle(gear_ratio = gear_ratio, i = i) - self._compute_speed(gear_ratio = gear_ratio, i = i) - self._compute_acceleration(gear_ratio = gear_ratio, i = i) + self._compute_angular_position(gear_ratio = gear_ratio, i = i) + self._compute_angular_speed(gear_ratio = gear_ratio, i = i) + self._compute_angular_acceleration(gear_ratio = gear_ratio, i = i) - def _compute_angle(self, gear_ratio, i): + def _compute_angular_position(self, gear_ratio, i): - self.transmission_chain[i].angle = gear_ratio*self.transmission_chain[i + 1].angle + self.transmission_chain[i].angular_position = gear_ratio*self.transmission_chain[i + 1].angular_position - def _compute_speed(self, gear_ratio, i): + def _compute_angular_speed(self, gear_ratio, i): - self.transmission_chain[i].speed = gear_ratio*self.transmission_chain[i + 1].speed + self.transmission_chain[i].angular_speed = gear_ratio*self.transmission_chain[i + 1].angular_speed - def _compute_acceleration(self, gear_ratio, i): + def _compute_angular_acceleration(self, gear_ratio, i): - self.transmission_chain[i].acceleration = gear_ratio*self.transmission_chain[i + 1].acceleration + self.transmission_chain[i].angular_acceleration = gear_ratio*self.transmission_chain[i + 1].angular_acceleration def _compute_driving_torque(self): @@ -122,9 +115,10 @@ def _compute_load_torque(self): for i in range(len(self.transmission_chain) - 1, 0, -1): if self.transmission_chain[i].external_torque is not None: self.transmission_chain[i].load_torque = \ - self.transmission_chain[i].external_torque(time = self.time[-1], - angle = self.transmission_chain[i].angle, - speed = self.transmission_chain[i].speed) + self.transmission_chain[i]. \ + external_torque(time = self.time[-1], + angular_position = self.transmission_chain[i].angular_position, + angular_speed = self.transmission_chain[i].angular_speed) gear_ratio = self.transmission_chain[i].master_gear_ratio self.transmission_chain[i - 1].load_torque = self.transmission_chain[i].load_torque/gear_ratio @@ -135,6 +129,9 @@ def _compute_torque(self): def _time_integration(self): - self.transmission_chain[-1].acceleration = self.transmission_chain[-1].torque/self.transmission_inertia - self.transmission_chain[-1].speed += self.transmission_chain[-1].acceleration*self.time_discretization - self.transmission_chain[-1].angle += self.transmission_chain[-1].speed*self.time_discretization + self.transmission_chain[-1].angular_acceleration = self.transmission_chain[-1].torque/\ + self.transmission_inertia_moment + self.transmission_chain[-1].angular_speed += self.transmission_chain[-1].angular_acceleration*\ + self.time_discretization + self.transmission_chain[-1].angular_position += self.transmission_chain[-1].angular_speed*\ + self.time_discretization diff --git a/gearpy/transmission/__init__.py b/gearpy/transmission/__init__.py index e69de29..80f9f9e 100644 --- a/gearpy/transmission/__init__.py +++ b/gearpy/transmission/__init__.py @@ -0,0 +1 @@ +from .transmission import Transmission diff --git a/gearpy/transmission/transmission.py b/gearpy/transmission/transmission.py index 3e2eccc..bfc2ed4 100644 --- a/gearpy/transmission/transmission.py +++ b/gearpy/transmission/transmission.py @@ -1,11 +1,11 @@ -from gearpy.motor.motor import MotorBase +from gearpy.motor import MotorBase class Transmission: def __init__(self, motor: MotorBase): if not isinstance(motor, MotorBase): - raise TypeError("Parameter 'motor' must be an instance of MotorBase.") + raise TypeError(f"Parameter 'motor' must be an instance of {MotorBase.__name__!r}.") if motor.drives is None: raise ValueError("Parameter 'motor' is not connected to any other element. Call 'add_fixed_joint' " diff --git a/gearpy/units/__init__.py b/gearpy/units/__init__.py new file mode 100644 index 0000000..0b93ab6 --- /dev/null +++ b/gearpy/units/__init__.py @@ -0,0 +1,7 @@ +from .concrete_units import AngularPosition, \ + AngularSpeed, \ + AngularAcceleration, \ + InertiaMoment, \ + Torque, \ + Time, \ + TimeInterval diff --git a/gearpy/units/concrete_units.py b/gearpy/units/concrete_units.py new file mode 100644 index 0000000..70bfb17 --- /dev/null +++ b/gearpy/units/concrete_units.py @@ -0,0 +1,925 @@ +from math import pi, fabs +from typing import Union +from .unit_base import UnitBase + + +COMPARISON_TOLERANCE = 1e-12 + + +class AngularPosition(UnitBase): + + __UNITS = {'rad': 1, + 'deg': pi/180, + 'arcmin': pi/180/60, + 'arcsec': pi/180/60/60} + + def __init__(self, value: Union[float, int], unit: str): + super().__init__(value = value, unit = unit) + + if unit not in self.__UNITS.keys(): + raise KeyError(f"{self.__class__.__name__} unit '{unit}' not available. " + f"Available units are: {list(self.__UNITS.keys())}") + + self.__value = value + self.__unit = unit + + def __repr__(self) -> str: + return f'{self.__value} {self.__unit}' + + def __add__(self, other: 'AngularPosition') -> 'AngularPosition': + super().__add__(other = other) + + return AngularPosition(value = self.__value + other.to(self.__unit).value, unit = self.__unit) + + def __sub__(self, other: 'AngularPosition') -> 'AngularPosition': + super().__sub__(other = other) + + return AngularPosition(value = self.__value - other.to(self.__unit).value, unit = self.__unit) + + def __mul__(self, other: Union[float, int]) -> 'AngularPosition': + super().__mul__(other = other) + + if not isinstance(other, float) and not isinstance(other, int): + raise TypeError(f'It is not allowed to multiply an {self.__class__.__name__} by a ' + f'{other.__class__.__name__}.') + + return AngularPosition(value = self.__value*other, unit = self.__unit) + + def __rmul__(self, other: Union[float, int]) -> 'AngularPosition': + super().__rmul__(other = other) + + if not isinstance(other, float) and not isinstance(other, int): + raise TypeError(f'It is not allowed to multiply a {other.__class__.__name__} by an ' + f'{self.__class__.__name__}.') + + return AngularPosition(value = self.__value*other, unit = self.__unit) + + def __truediv__(self, other: Union['AngularPosition', float, int]) -> Union['AngularPosition', float]: + super().__truediv__(other = other) + + if not isinstance(other, AngularPosition) and not isinstance(other, float) and not isinstance(other, int): + raise TypeError(f'It is not allowed to divide an {self.__class__.__name__} by a ' + f'{other.__class__.__name__}.') + + if isinstance(other, AngularPosition): + return self.__value/other.to(self.__unit).value + else: + return AngularPosition(value = self.__value/other, unit = self.__unit) + + def __eq__(self, other: 'AngularPosition') -> bool: + super().__eq__(other = other) + + if self.__unit == other.unit: + return self.__value == other.value + else: + return fabs(self.__value - other.to(self.__unit).value) < COMPARISON_TOLERANCE + + def __ne__(self, other: 'AngularPosition') -> bool: + super().__ne__(other = other) + + if self.__unit == other.unit: + return self.__value != other.value + else: + return fabs(self.__value - other.to(self.__unit).value) > COMPARISON_TOLERANCE + + def __gt__(self, other: 'AngularPosition') -> bool: + super().__gt__(other = other) + + if self.__unit == other.unit: + return self.__value > other.value + else: + return self.__value - other.to(self.__unit).value > COMPARISON_TOLERANCE + + def __ge__(self, other: 'AngularPosition') -> bool: + super().__ge__(other = other) + + if self.__unit == other.unit: + return self.__value >= other.value + else: + return self.__value - other.to(self.__unit).value >= -COMPARISON_TOLERANCE + + def __lt__(self, other: 'AngularPosition') -> bool: + super().__lt__(other = other) + + if self.__unit == other.unit: + return self.__value < other.value + else: + return self.__value - other.to(self.__unit).value < -COMPARISON_TOLERANCE + + def __le__(self, other: 'AngularPosition') -> bool: + super().__le__(other = other) + + if self.__unit == other.unit: + return self.__value <= other.value + else: + return self.__value - other.to(self.__unit).value <= COMPARISON_TOLERANCE + + @property + def value(self) -> Union[float, int]: + return self.__value + + @property + def unit(self) -> str: + return self.__unit + + def to(self, target_unit: str, inplace: bool = False) -> 'AngularPosition': + super().to(target_unit = target_unit, inplace = inplace) + + if target_unit not in self.__UNITS.keys(): + raise KeyError(f"{self.__class__.__name__} unit '{target_unit}' not available. " + f"Available units are: {list(self.__UNITS.keys())}") + + if target_unit != self.__unit: + target_value = self.__value*self.__UNITS[self.__unit]/self.__UNITS[target_unit] + else: + target_value = self.__value + + if inplace: + self.__value = target_value + self.__unit = target_unit + return self + else: + return AngularPosition(value = target_value, unit = target_unit) + + +class AngularSpeed(UnitBase): + + __UNITS = {'rad/s': 1, + 'rad/min': 1/60, + 'rad/h': 1/60/60, + 'deg/s': pi/180, + 'deg/min': pi/180/60, + 'deg/h': pi/180/60/60, + 'rps': 2*pi, + 'rpm': 2*pi/60, + 'rph': 2*pi/60/60} + + def __init__(self, value: Union[float, int], unit: str): + super().__init__(value = value, unit = unit) + + if unit not in self.__UNITS.keys(): + raise KeyError(f"{self.__class__.__name__} unit '{unit}' not available. " + f"Available units are: {list(self.__UNITS.keys())}") + + self.__value = value + self.__unit = unit + + def __repr__(self) -> str: + return f'{self.__value} {self.__unit}' + + def __add__(self, other: 'AngularSpeed') -> 'AngularSpeed': + super().__add__(other = other) + + return AngularSpeed(value = self.__value + other.to(self.__unit).value, unit = self.__unit) + + def __sub__(self, other: 'AngularSpeed') -> 'AngularSpeed': + super().__sub__(other = other) + + return AngularSpeed(value = self.__value - other.to(self.__unit).value, unit = self.__unit) + + def __mul__(self, other: Union['Time', float, int]) -> Union['AngularPosition', 'AngularSpeed']: + super().__mul__(other = other) + + if not isinstance(other, Time) and not isinstance(other, float) and not isinstance(other, int): + raise TypeError(f'It is not allowed to multiply a {self.__class__.__name__} by a ' + f'{other.__class__.__name__}.') + + if isinstance(other, Time): + return AngularPosition(value = self.to('rad/s').value*other.to('sec').value, unit = 'rad') + else: + return AngularSpeed(value = self.__value*other, unit = self.__unit) + + def __rmul__(self, other: Union['Time', float, int]) -> Union['AngularPosition', 'AngularSpeed']: + super().__rmul__(other = other) + + if not isinstance(other, Time) and not isinstance(other, float) and not isinstance(other, int): + raise TypeError(f'It is not allowed to multiply a {other.__class__.__name__} by a ' + f'{self.__class__.__name__}.') + + if isinstance(other, Time): + return AngularPosition(value = self.to('rad/s').value*other.to('sec').value, unit = 'rad') + else: + return AngularSpeed(value = self.__value*other, unit = self.__unit) + + def __truediv__(self, other: Union['AngularSpeed', float, int]) -> Union['AngularSpeed', float]: + super().__truediv__(other = other) + + if not isinstance(other, AngularSpeed) and not isinstance(other, float) and not isinstance(other, int): + raise TypeError(f'It is not allowed to divide a {self.__class__.__name__} by a {other.__class__.__name__}.') + + if isinstance(other, AngularSpeed): + return self.__value/other.to(self.__unit).value + else: + return AngularSpeed(value = self.__value/other, unit = self.__unit) + + def __eq__(self, other: 'AngularSpeed') -> bool: + super().__eq__(other = other) + + if self.__unit == other.unit: + return self.__value == other.value + else: + return fabs(self.__value - other.to(self.__unit).value) < COMPARISON_TOLERANCE + + def __ne__(self, other: 'AngularSpeed') -> bool: + super().__ne__(other = other) + + if self.__unit == other.unit: + return self.__value != other.value + else: + return fabs(self.__value - other.to(self.__unit).value) > COMPARISON_TOLERANCE + + def __gt__(self, other: 'AngularSpeed') -> bool: + super().__gt__(other = other) + + if self.__unit == other.unit: + return self.__value > other.value + else: + return self.__value - other.to(self.__unit).value > COMPARISON_TOLERANCE + + def __ge__(self, other: 'AngularSpeed') -> bool: + super().__ge__(other = other) + + if self.__unit == other.unit: + return self.__value >= other.value + else: + return self.__value - other.to(self.__unit).value >= -COMPARISON_TOLERANCE + + def __lt__(self, other: 'AngularSpeed') -> bool: + super().__lt__(other = other) + + if self.__unit == other.unit: + return self.__value < other.value + else: + return self.__value - other.to(self.__unit).value < -COMPARISON_TOLERANCE + + def __le__(self, other: 'AngularSpeed') -> bool: + super().__le__(other = other) + + if self.__unit == other.unit: + return self.__value <= other.value + else: + return self.__value - other.to(self.__unit).value <= COMPARISON_TOLERANCE + + @property + def value(self) -> Union[float, int]: + return self.__value + + @property + def unit(self) -> str: + return self.__unit + + def to(self, target_unit: str, inplace: bool = False) -> 'AngularSpeed': + super().to(target_unit = target_unit, inplace = inplace) + + if target_unit not in self.__UNITS.keys(): + raise KeyError(f"{self.__class__.__name__} unit '{target_unit}' not available. " + f"Available units are: {list(self.__UNITS.keys())}") + + if target_unit != self.__unit: + target_value = self.__value*self.__UNITS[self.__unit]/self.__UNITS[target_unit] + else: + target_value = self.__value + + if inplace: + self.__value = target_value + self.__unit = target_unit + return self + else: + return AngularSpeed(value = target_value, unit = target_unit) + + +class AngularAcceleration(UnitBase): + + __UNITS = {'rad/s^2': 1, + 'deg/s^2': pi/180} + + def __init__(self, value: Union[float, int], unit: str): + super().__init__(value = value, unit = unit) + + if unit not in self.__UNITS.keys(): + raise KeyError(f"{self.__class__.__name__} unit '{unit}' not available. " + f"Available units are: {list(self.__UNITS.keys())}") + + self.__value = value + self.__unit = unit + + def __repr__(self) -> str: + return f'{self.__value} {self.__unit}' + + def __add__(self, other: 'AngularAcceleration') -> 'AngularAcceleration': + super().__add__(other = other) + + return AngularAcceleration(value = self.__value + other.to(self.__unit).value, unit = self.__unit) + + def __sub__(self, other: 'AngularAcceleration') -> 'AngularAcceleration': + super().__sub__(other = other) + + return AngularAcceleration(value = self.__value - other.to(self.__unit).value, unit = self.__unit) + + def __mul__(self, other: Union['Time', float, int]) -> Union['AngularAcceleration', 'AngularSpeed']: + super().__mul__(other = other) + + if not isinstance(other, Time) and not isinstance(other, float) and not isinstance(other, int): + raise TypeError(f'It is not allowed to multiply an {self.__class__.__name__} by a ' + f'{other.__class__.__name__}.') + + if isinstance(other, Time): + return AngularSpeed(value = self.to('rad/s^2').value*other.to('sec').value, unit = 'rad/s') + else: + return AngularAcceleration(value = self.__value*other, unit = self.__unit) + + def __rmul__(self, other: Union['Time', float, int]) -> Union['AngularAcceleration', 'AngularSpeed']: + super().__rmul__(other = other) + + if not isinstance(other, Time) and not isinstance(other, float) and not isinstance(other, int): + raise TypeError(f'It is not allowed to multiply a {other.__class__.__name__} by an ' + f'{self.__class__.__name__}.') + + if isinstance(other, Time): + return AngularSpeed(value = self.to('rad/s^2').value*other.to('sec').value, unit = 'rad/s') + else: + return AngularAcceleration(value = self.__value*other, unit = self.__unit) + + def __truediv__(self, other: Union['AngularAcceleration', float, int]) -> Union['AngularAcceleration', float]: + super().__truediv__(other = other) + + if not isinstance(other, AngularAcceleration) and not isinstance(other, float) and not isinstance(other, int): + raise TypeError(f'It is not allowed to divide an {self.__class__.__name__} by a ' + f'{other.__class__.__name__}.') + + if isinstance(other, AngularAcceleration): + return self.__value/other.to(self.__unit).value + else: + return AngularAcceleration(value = self.__value/other, unit = self.__unit) + + def __eq__(self, other: 'AngularAcceleration') -> bool: + super().__eq__(other = other) + + if self.__unit == other.unit: + return self.__value == other.value + else: + return fabs(self.__value - other.to(self.__unit).value) < COMPARISON_TOLERANCE + + def __ne__(self, other: 'AngularAcceleration') -> bool: + super().__ne__(other = other) + + if self.__unit == other.unit: + return self.__value != other.value + else: + return fabs(self.__value - other.to(self.__unit).value) > COMPARISON_TOLERANCE + + def __gt__(self, other: 'AngularAcceleration') -> bool: + super().__gt__(other = other) + + if self.__unit == other.unit: + return self.__value > other.value + else: + return self.__value - other.to(self.__unit).value > COMPARISON_TOLERANCE + + def __ge__(self, other: 'AngularAcceleration') -> bool: + super().__ge__(other = other) + + if self.__unit == other.unit: + return self.__value >= other.value + else: + return self.__value - other.to(self.__unit).value >= -COMPARISON_TOLERANCE + + def __lt__(self, other: 'AngularAcceleration') -> bool: + super().__lt__(other = other) + + if self.__unit == other.unit: + return self.__value < other.value + else: + return self.__value - other.to(self.__unit).value < -COMPARISON_TOLERANCE + + def __le__(self, other: 'AngularAcceleration') -> bool: + super().__le__(other = other) + + if self.__unit == other.unit: + return self.__value <= other.value + else: + return self.__value - other.to(self.__unit).value <= COMPARISON_TOLERANCE + + @property + def value(self) -> Union[float, int]: + return self.__value + + @property + def unit(self) -> str: + return self.__unit + + def to(self, target_unit: str, inplace: bool = False) -> 'AngularAcceleration': + super().to(target_unit = target_unit, inplace = inplace) + + if target_unit not in self.__UNITS.keys(): + raise KeyError(f"{self.__class__.__name__} unit '{target_unit}' not available. " + f"Available units are: {list(self.__UNITS.keys())}") + + if target_unit != self.__unit: + target_value = self.__value*self.__UNITS[self.__unit]/self.__UNITS[target_unit] + else: + target_value = self.__value + + if inplace: + self.__value = target_value + self.__unit = target_unit + return self + else: + return AngularAcceleration(value = target_value, unit = target_unit) + + +class InertiaMoment(UnitBase): + + __UNITS = {'kgm^2': 1, + 'gm^2': 1e-3, + 'gcm^2': 1e-7} + + def __init__(self, value: Union[float, int], unit: str): + super().__init__(value = value, unit = unit) + + if unit not in self.__UNITS.keys(): + raise KeyError(f"{self.__class__.__name__} unit '{unit}' not available. " + f"Available units are: {list(self.__UNITS.keys())}") + + if value <= 0: + raise ValueError("Parameter 'value' must be positive.") + + self.__value = value + self.__unit = unit + + def __repr__(self) -> str: + return f'{self.__value} {self.__unit}' + + def __add__(self, other: 'InertiaMoment') -> 'InertiaMoment': + super().__add__(other = other) + + return InertiaMoment(value = self.__value + other.to(self.__unit).value, unit = self.__unit) + + def __sub__(self, other: 'InertiaMoment') -> 'InertiaMoment': + super().__sub__(other = other) + + if self.__value - other.to(self.__unit).value <= 0: + raise ValueError('Cannot perform the subtraction because the result is not positive.') + + return InertiaMoment(value = self.__value - other.to(self.__unit).value, unit = self.__unit) + + def __mul__(self, other: Union[float, int]) -> 'InertiaMoment': + super().__mul__(other = other) + + if not isinstance(other, float) and not isinstance(other, int): + raise TypeError(f'It is not allowed to multiply an {self.__class__.__name__} by a ' + f'{other.__class__.__name__}.') + + if other <= 0: + raise ValueError('Cannot perform a multiplication by a non-positive number.') + + return InertiaMoment(value = self.__value*other, unit = self.__unit) + + def __rmul__(self, other: Union[float, int]) -> 'InertiaMoment': + super().__rmul__(other = other) + + if not isinstance(other, float) and not isinstance(other, int): + raise TypeError(f'It is not allowed to multiply a {other.__class__.__name__} by an ' + f'{self.__class__.__name__}.') + + if other <= 0: + raise ValueError('Cannot perform a multiplication by a non-positive number.') + + return InertiaMoment(value = self.__value*other, unit = self.__unit) + + def __truediv__(self, other: Union['InertiaMoment', float, int]) -> Union['InertiaMoment', float]: + super().__truediv__(other = other) + + if not isinstance(other, InertiaMoment) and not isinstance(other, float) and not isinstance(other, int): + raise TypeError(f'It is not allowed to divide an {self.__class__.__name__} by a ' + f'{other.__class__.__name__}.') + + if isinstance(other, InertiaMoment): + return self.__value/other.to(self.__unit).value + else: + return InertiaMoment(value = self.__value/other, unit = self.__unit) + + def __eq__(self, other: 'InertiaMoment') -> bool: + super().__eq__(other = other) + + if self.__unit == other.unit: + return self.__value == other.value + else: + return fabs(self.__value - other.to(self.__unit).value) < COMPARISON_TOLERANCE + + def __ne__(self, other: 'InertiaMoment') -> bool: + super().__ne__(other = other) + + if self.__unit == other.unit: + return self.__value != other.value + else: + return fabs(self.__value - other.to(self.__unit).value) > COMPARISON_TOLERANCE + + def __gt__(self, other: 'InertiaMoment') -> bool: + super().__gt__(other = other) + + if self.__unit == other.unit: + return self.__value > other.value + else: + return self.__value - other.to(self.__unit).value > COMPARISON_TOLERANCE + + def __ge__(self, other: 'InertiaMoment') -> bool: + super().__ge__(other = other) + + if self.__unit == other.unit: + return self.__value >= other.value + else: + return self.__value - other.to(self.__unit).value >= -COMPARISON_TOLERANCE + + def __lt__(self, other: 'InertiaMoment') -> bool: + super().__lt__(other = other) + + if self.__unit == other.unit: + return self.__value < other.value + else: + return self.__value - other.to(self.__unit).value < -COMPARISON_TOLERANCE + + def __le__(self, other: 'InertiaMoment') -> bool: + super().__le__(other = other) + + if self.__unit == other.unit: + return self.__value <= other.value + else: + return self.__value - other.to(self.__unit).value <= COMPARISON_TOLERANCE + + @property + def value(self) -> Union[float, int]: + return self.__value + + @property + def unit(self) -> str: + return self.__unit + + def to(self, target_unit: str, inplace: bool = False) -> 'InertiaMoment': + super().to(target_unit = target_unit, inplace = inplace) + + if target_unit not in self.__UNITS.keys(): + raise KeyError(f"{self.__class__.__name__} unit '{target_unit}' not available. " + f"Available units are: {list(self.__UNITS.keys())}") + + if target_unit != self.__unit: + target_value = self.__value*self.__UNITS[self.__unit]/self.__UNITS[target_unit] + else: + target_value = self.__value + + if inplace: + self.__value = target_value + self.__unit = target_unit + return self + else: + return InertiaMoment(value = target_value, unit = target_unit) + + +class Torque(UnitBase): + + __UNITS = {'Nm': 1, + 'mNm': 1e-3, + 'kNm': 1e3, + 'kgfm': 9.80665, + 'kgfcm': 9.80665e-2} + + def __init__(self, value: Union[float, int], unit: str): + super().__init__(value = value, unit = unit) + + if unit not in self.__UNITS.keys(): + raise KeyError(f"{self.__class__.__name__} unit '{unit}' not available. " + f"Available units are: {list(self.__UNITS.keys())}") + + self.__value = value + self.__unit = unit + + def __repr__(self) -> str: + return f'{self.__value} {self.__unit}' + + def __add__(self, other: 'Torque') -> 'Torque': + super().__add__(other = other) + + return Torque(value = self.__value + other.to(self.__unit).value, unit = self.__unit) + + def __sub__(self, other: 'Torque') -> 'Torque': + super().__sub__(other = other) + + return Torque(value = self.__value - other.to(self.__unit).value, unit = self.__unit) + + def __mul__(self, other: Union[float, int]) -> 'Torque': + super().__mul__(other = other) + + if not isinstance(other, float) and not isinstance(other, int): + raise TypeError(f'It is not allowed to multiply a {self.__class__.__name__} by a ' + f'{other.__class__.__name__}.') + + return Torque(value = self.__value*other, unit = self.__unit) + + def __rmul__(self, other: Union[float, int]) -> 'Torque': + super().__rmul__(other = other) + + if not isinstance(other, float) and not isinstance(other, int): + raise TypeError(f'It is not allowed to multiply a {other.__class__.__name__} by a ' + f'{self.__class__.__name__}.') + + return Torque(value = self.__value*other, unit = self.__unit) + + def __truediv__(self, other: Union['InertiaMoment', 'Torque', float, int]) -> Union['AngularAcceleration', 'Torque', float]: + super().__truediv__(other = other) + + if not isinstance(other, Torque) and not isinstance(other, float) and not isinstance(other, int) \ + and not isinstance(other, InertiaMoment): + raise TypeError(f'It is not allowed to divide a {self.__class__.__name__} by a {other.__class__.__name__}.') + + if isinstance(other, Torque): + return self.__value/other.to(self.__unit).value + elif isinstance(other, InertiaMoment): + return AngularAcceleration(value = self.to('Nm').value/other.to('kgm^2').value, unit = 'rad/s^2') + else: + return Torque(value = self.__value/other, unit = self.__unit) + + def __eq__(self, other: 'Torque') -> bool: + super().__eq__(other = other) + + if self.__unit == other.unit: + return self.__value == other.value + else: + return fabs(self.__value - other.to(self.__unit).value) < COMPARISON_TOLERANCE + + def __ne__(self, other: 'Torque') -> bool: + super().__ne__(other = other) + + if self.__unit == other.unit: + return self.__value != other.value + else: + return fabs(self.__value - other.to(self.__unit).value) > COMPARISON_TOLERANCE + + def __gt__(self, other: 'Torque') -> bool: + super().__gt__(other = other) + + if self.__unit == other.unit: + return self.__value > other.value + else: + return self.__value - other.to(self.__unit).value > COMPARISON_TOLERANCE + + def __ge__(self, other: 'Torque') -> bool: + super().__ge__(other = other) + + if self.__unit == other.unit: + return self.__value >= other.value + else: + return self.__value - other.to(self.__unit).value >= -COMPARISON_TOLERANCE + + def __lt__(self, other: 'Torque') -> bool: + super().__lt__(other = other) + + if self.__unit == other.unit: + return self.__value < other.value + else: + return self.__value - other.to(self.__unit).value < -COMPARISON_TOLERANCE + + def __le__(self, other: 'Torque') -> bool: + super().__le__(other = other) + + if self.__unit == other.unit: + return self.__value <= other.value + else: + return self.__value - other.to(self.__unit).value <= COMPARISON_TOLERANCE + + @property + def value(self) -> Union[float, int]: + return self.__value + + @property + def unit(self) -> str: + return self.__unit + + def to(self, target_unit: str, inplace: bool = False) -> 'Torque': + super().to(target_unit = target_unit, inplace = inplace) + + if target_unit not in self.__UNITS.keys(): + raise KeyError(f"{self.__class__.__name__} unit '{target_unit}' not available. " + f"Available units are: {list(self.__UNITS.keys())}") + + if target_unit != self.__unit: + target_value = self.__value*self.__UNITS[self.__unit]/self.__UNITS[target_unit] + else: + target_value = self.__value + + if inplace: + self.__value = target_value + self.__unit = target_unit + return self + else: + return Torque(value = target_value, unit = target_unit) + + +class Time(UnitBase): + + __UNITS = {'sec': 1, + 'min': 60, + 'hour': 60*60} + + def __init__(self, value: Union[float, int], unit: str): + super().__init__(value = value, unit = unit) + + if unit not in self.__UNITS.keys(): + raise KeyError(f"{self.__class__.__name__} unit '{unit}' not available. " + f"Available units are: {list(self.__UNITS.keys())}") + + self.__value = value + self.__unit = unit + + def __repr__(self) -> str: + return f'{self.__value} {self.__unit}' + + def __add__(self, other: 'Time') -> 'Time': + super().__add__(other = other) + + return Time(value = self.__value + other.to(self.__unit).value, unit = self.__unit) + + def __sub__(self, other: 'Time') -> 'Time': + super().__sub__(other = other) + + return Time(value = self.__value - other.to(self.__unit).value, unit = self.__unit) + + def __mul__(self, other: Union['AngularAcceleration', 'AngularSpeed', float, int]) -> Union['AngularPosition', 'AngularSpeed', 'Time']: + super().__mul__(other = other) + + if not isinstance(other, AngularAcceleration) and not isinstance(other, AngularSpeed) \ + and not isinstance(other, float) and not isinstance(other, int): + raise TypeError(f'It is not allowed to multiply a {self.__class__.__name__} by a ' + f'{other.__class__.__name__}.') + + if isinstance(other, AngularAcceleration): + return AngularSpeed(value = self.to('sec').value*other.to('rad/s^2').value, unit = 'rad/s') + elif isinstance(other, AngularSpeed): + return AngularPosition(value = self.to('sec').value*other.to('rad/s').value, unit = 'rad') + else: + return Time(value = self.__value*other, unit = self.__unit) + + def __rmul__(self, other: Union['AngularAcceleration', 'AngularSpeed', float, int]) -> Union['AngularPosition', 'AngularSpeed', 'Time']: + super().__rmul__(other = other) + + if not isinstance(other, AngularAcceleration) and not isinstance(other, AngularSpeed) \ + and not isinstance(other, float) and not isinstance(other, int): + raise TypeError(f'It is not allowed to multiply a {other.__class__.__name__} by a ' + f'{self.__class__.__name__}.') + + if isinstance(other, AngularAcceleration): + return AngularSpeed(value = self.to('sec').value*other.to('rad/s^2').value, unit = 'rad/s') + elif isinstance(other, AngularSpeed): + return AngularPosition(value = self.to('sec').value*other.to('rad/s').value, unit = 'rad') + else: + return Time(value = self.__value*other, unit = self.__unit) + + def __truediv__(self, other: Union['Time', float, int]) -> Union['Time', float]: + super().__truediv__(other = other) + + if not isinstance(other, Time) and not isinstance(other, float) and not isinstance(other, int): + raise TypeError(f'It is not allowed to divide a {self.__class__.__name__} by a {other.__class__.__name__}.') + + if isinstance(other, Time): + return self.__value/other.to(self.__unit).value + else: + return Time(value = self.__value/other, unit = self.__unit) + + def __eq__(self, other: 'Time') -> bool: + super().__eq__(other = other) + + if self.__unit == other.unit: + return self.__value == other.value + else: + return fabs(self.__value - other.to(self.__unit).value) < COMPARISON_TOLERANCE + + def __ne__(self, other: 'Time') -> bool: + super().__ne__(other = other) + + if self.__unit == other.unit: + return self.__value != other.value + else: + return fabs(self.__value - other.to(self.__unit).value) > COMPARISON_TOLERANCE + + def __gt__(self, other: 'Time') -> bool: + super().__gt__(other = other) + + if self.__unit == other.unit: + return self.__value > other.value + else: + return self.__value - other.to(self.__unit).value > COMPARISON_TOLERANCE + + def __ge__(self, other: 'Time') -> bool: + super().__ge__(other = other) + + if self.__unit == other.unit: + return self.__value >= other.value + else: + return self.__value - other.to(self.__unit).value >= -COMPARISON_TOLERANCE + + def __lt__(self, other: 'Time') -> bool: + super().__lt__(other = other) + + if self.__unit == other.unit: + return self.__value < other.value + else: + return self.__value - other.to(self.__unit).value < -COMPARISON_TOLERANCE + + def __le__(self, other: 'Time') -> bool: + super().__le__(other = other) + + if self.__unit == other.unit: + return self.__value <= other.value + else: + return self.__value - other.to(self.__unit).value <= COMPARISON_TOLERANCE + + @property + def value(self) -> Union[float, int]: + return self.__value + + @property + def unit(self) -> str: + return self.__unit + + def to(self, target_unit: str, inplace: bool = False) -> 'Time': + super().to(target_unit = target_unit, inplace = inplace) + + if target_unit not in self.__UNITS.keys(): + raise KeyError(f"{self.__class__.__name__} unit '{target_unit}' not available. " + f"Available units are: {list(self.__UNITS.keys())}") + + if target_unit != self.__unit: + target_value = self.__value*self.__UNITS[self.__unit]/self.__UNITS[target_unit] + else: + target_value = self.__value + + if inplace: + self.__value = target_value + self.__unit = target_unit + return self + else: + return Time(value = target_value, unit = target_unit) + + +class TimeInterval(Time): + + def __init__(self, value: Union[float, int], unit: str): + super().__init__(value = value, unit = unit) + + if value <= 0: + raise ValueError("Parameter 'value' must be positive.") + + self.__value = value + self.__unit = unit + + def __add__(self, other: Union['Time', 'TimeInterval']) -> Union['Time', 'TimeInterval']: + super().__add__(other = other) + + if isinstance(other, TimeInterval): + return TimeInterval(value = self.__value + other.to(self.__unit).value, unit = self.__unit) + else: + return Time(value = self.__value + other.to(self.__unit).value, unit = self.__unit) + + def __sub__(self, other: Union['Time', 'TimeInterval']) -> Union['Time', 'TimeInterval']: + super().__sub__(other = other) + + if self.__value - other.to(self.__unit).value <= 0: + raise ValueError('Cannot perform the subtraction because the result is not positive.') + + if isinstance(other, TimeInterval): + return TimeInterval(value = self.__value - other.to(self.__unit).value, unit = self.__unit) + else: + return Time(value = self.__value + other.to(self.__unit).value, unit = self.__unit) + + def __mul__(self, other: Union[float, int]) -> 'TimeInterval': + super().__mul__(other = other) + + if other <= 0: + raise ValueError('Cannot perform a multiplication by a non-positive number.') + + return TimeInterval(value = self.__value*other, unit = self.__unit) + + def __rmul__(self, other: Union[float, int]) -> 'TimeInterval': + super().__rmul__(other = other) + + if other <= 0: + raise ValueError('Cannot perform a multiplication by a non-positive number.') + + return TimeInterval(value = self.__value*other, unit = self.__unit) + + def __truediv__(self, other: Union['Time', float, int]) -> Union['Time', float]: + super().__truediv__(other = other) + + if isinstance(other, Time): + return self.__value/other.to(self.__unit).value + else: + return TimeInterval(value = self.__value/other, unit = self.__unit) + + def to(self, target_unit: str, inplace: bool = False) -> 'TimeInterval': + converted = super().to(target_unit = target_unit, inplace = inplace) + + if inplace: + self.__value = converted.value + self.__unit = converted.unit + return self + else: + return TimeInterval(value = converted.value, unit = converted.unit) diff --git a/gearpy/units/unit_base.py b/gearpy/units/unit_base.py new file mode 100644 index 0000000..f2169ec --- /dev/null +++ b/gearpy/units/unit_base.py @@ -0,0 +1,93 @@ +from abc import ABC, abstractmethod +from typing import Union + + +class UnitBase(ABC): + + __UNITS = {} + + @abstractmethod + def __init__(self, value: Union[float, int], unit: str): + if not isinstance(value, float) and not isinstance(value, int): + raise TypeError("Parameter 'value' must be a float or an integer.") + + if not isinstance(unit, str): + raise TypeError("Parameter 'unit' must be a string.") + + @abstractmethod + def __repr__(self): ... + + @abstractmethod + def __add__(self, other: 'UnitBase') -> None: + if not isinstance(other, self.__class__) and not issubclass(self.__class__, other.__class__): + raise TypeError(f'It is not allowed to sum a {self.__class__.__name__} and a {other.__class__.__name__}.') + + @abstractmethod + def __sub__(self, other: 'UnitBase') -> None: + if not isinstance(other, self.__class__) and not issubclass(self.__class__, other.__class__): + raise TypeError(f'It is not allowed to subtract a {other.__class__.__name__} ' + f'from a {self.__class__.__name__}.') + + @abstractmethod + def __mul__(self, other: Union[float, int]) -> None: ... + + @abstractmethod + def __rmul__(self, other: Union[float, int]) -> None: ... + + @abstractmethod + def __truediv__(self, other: Union['UnitBase', float, int]) -> None: + if not isinstance(other, UnitBase) and not isinstance(other, float) and not isinstance(other, int): + raise TypeError(f'It is not allowed to divide a Unit by a {other.__class__.__name__}.') + + if isinstance(other, UnitBase): + if other.value == 0: + raise ZeroDivisionError('It is not allowed to divide a Unit by zero.') + else: + if other == 0: + raise ZeroDivisionError('It is not allowed to divide a Unit by zero.') + + @abstractmethod + def __eq__(self, other: 'UnitBase') -> None: + if not isinstance(other, self.__class__) and not issubclass(self.__class__, other.__class__): + raise TypeError(f'Cannot compare {self.__class__.__name__} and {other.__class__.__name__}') + + @abstractmethod + def __ne__(self, other: 'UnitBase') -> None: + if not isinstance(other, self.__class__) and not issubclass(self.__class__, other.__class__): + raise TypeError(f'Cannot compare {self.__class__.__name__} and {other.__class__.__name__}') + + @abstractmethod + def __gt__(self, other: 'UnitBase') -> None: + if not isinstance(other, self.__class__) and not issubclass(self.__class__, other.__class__): + raise TypeError(f'Cannot compare {self.__class__.__name__} and {other.__class__.__name__}') + + @abstractmethod + def __ge__(self, other: 'UnitBase') -> None: + if not isinstance(other, self.__class__) and not issubclass(self.__class__, other.__class__): + raise TypeError(f'Cannot compare {self.__class__.__name__} and {other.__class__.__name__}') + + @abstractmethod + def __lt__(self, other: 'UnitBase') -> None: + if not isinstance(other, self.__class__) and not issubclass(self.__class__, other.__class__): + raise TypeError(f'Cannot compare {self.__class__.__name__} and {other.__class__.__name__}') + + @abstractmethod + def __le__(self, other: 'UnitBase') -> None: + if not isinstance(other, self.__class__) and not issubclass(self.__class__, other.__class__): + raise TypeError(f'Cannot compare {self.__class__.__name__} and {other.__class__.__name__}') + + @property + @abstractmethod + def value(self) -> None: ... + + @property + @abstractmethod + def unit(self) -> None: ... + + @abstractmethod + def to(self, target_unit: str, inplace: bool) -> None: + if not isinstance(target_unit, str): + raise TypeError("Parameter 'target_unit' must be a string.") + + if not isinstance(inplace, bool): + raise TypeError("Parameter 'inplace' must be a bool.") diff --git a/gearpy/utils/__init__.py b/gearpy/utils/__init__.py new file mode 100644 index 0000000..e1b5455 --- /dev/null +++ b/gearpy/utils/__init__.py @@ -0,0 +1,2 @@ +from .relations import add_fixed_joint +from .relations import add_gear_mating diff --git a/gearpy/utils.py b/gearpy/utils/relations.py similarity index 94% rename from gearpy/utils.py rename to gearpy/utils/relations.py index 682566b..f33d821 100644 --- a/gearpy/utils.py +++ b/gearpy/utils/relations.py @@ -1,5 +1,5 @@ -from gearpy.gear.gear import GearBase -from gearpy.motor.motor import MotorBase +from gearpy.gear import GearBase +from gearpy.motor import MotorBase from typing import Union diff --git a/setup.py b/setup.py index 7f2a5c3..e6cde99 100644 --- a/setup.py +++ b/setup.py @@ -30,6 +30,8 @@ 'Topic :: Scientific/Engineering'], install_requires = ['numpy >= 1.26.0'], extras_require = {'dev': ['sphinx >= 7.2.6', + 'm2r2 >= 0.3.3.post2', + 'furo >= 2023.9.10', 'tox >= 4.11.3', 'hypothesis >= 6.87.1', 'twine >= 4.0.2']}, diff --git a/tests/conftest.py b/tests/conftest.py index 5e4c8c5..cf842ab 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,38 +1,62 @@ -from gearpy import DCMotor, SpurGear, Transmission, add_fixed_joint, add_gear_mating -from gearpy.gear.gear import GearBase -from gearpy.motor.motor import MotorBase -from gearpy.mechanical_object.rotating_object import RotatingObject -from hypothesis.strategies import composite, text, integers, floats, lists +from gearpy.gear import GearBase, SpurGear +from gearpy.mechanical_object import RotatingObject +from gearpy.motor import MotorBase, DCMotor +from gearpy.transmission import Transmission +from gearpy.units import AngularAcceleration, AngularPosition, AngularSpeed, InertiaMoment, Torque, Time, TimeInterval +from gearpy.utils import add_fixed_joint, add_gear_mating +from hypothesis.strategies import composite, text, integers, floats, lists, sampled_from import numpy as np from pytest import fixture -from typing import Callable -types_to_check = ['string', 2, 2.2, True, (0, 1), [0, 1], {0, 1}, {0: 1}, None, np.array([0])] +types_to_check = ['string', 2, 2.2, True, (0, 1), [0, 1], {0, 1}, {0: 1}, None, np.array([0]), + AngularPosition(1, 'rad'), AngularSpeed(1, 'rad/s'), AngularAcceleration(1, 'rad/s^2'), + InertiaMoment(1, 'kgm^2'), Torque(1, 'Nm'), Time(1, 'sec'), TimeInterval(1, 'sec')] -basic_dc_motor = DCMotor(name = 'name', inertia = 1, no_load_speed = 1, maximum_torque = 1) -basic_spur_gear = SpurGear(name = 'gear', n_teeth = 10, inertia = 1) -basic_rotating_objects = [basic_dc_motor, basic_spur_gear] +basic_spur_gear = SpurGear(name = 'gear', n_teeth = 10, inertia_moment = InertiaMoment(1, 'kgm^2')) + +basic_dc_motor = DCMotor(name = 'name', + inertia_moment = InertiaMoment(1, 'kgm^2'), + no_load_speed = AngularSpeed(1000, 'rpm'), + maximum_torque = Torque(1, 'Nm')) @composite -def dc_motors(draw): +def spur_gears(draw): name = draw(text(min_size = 1)) - inertia = draw(floats(min_value = 0, exclude_min = True, allow_nan = False, allow_infinity = False)) - no_load_speed = draw(floats(min_value = 0, exclude_min = True, allow_nan = False, allow_infinity = False)) - maximum_torque = draw(floats(min_value = 0, exclude_min = True, allow_nan = False, allow_infinity = False)) - return DCMotor(name = name, inertia = inertia, no_load_speed = no_load_speed, maximum_torque = maximum_torque) + n_teeth = draw(integers(min_value = 1)) + + inertia_moment_units_list = list(InertiaMoment._InertiaMoment__UNITS.keys()) + inertia_moment_value = draw(floats(allow_nan = False, allow_infinity = False, min_value = 1e-10, exclude_min = True, max_value = 1000)) + inertia_moment_unit = draw(sampled_from(elements = inertia_moment_units_list)) + + return SpurGear(name = name, + n_teeth = n_teeth, + inertia_moment = InertiaMoment(inertia_moment_value, inertia_moment_unit)) @composite -def spur_gears(draw): +def dc_motors(draw): name = draw(text(min_size = 1)) - n_teeth = draw(integers(min_value = 1)) - inertia = draw(floats(min_value = 0, exclude_min = True, allow_nan = False, allow_infinity = False)) - return SpurGear(name = name, n_teeth = n_teeth, inertia = inertia) + inertia_moment_units_list = list(InertiaMoment._InertiaMoment__UNITS.keys()) + inertia_moment_value = draw(floats(allow_nan = False, allow_infinity = False, min_value = 1e-10, exclude_min = True, max_value = 1000)) + inertia_moment_unit = draw(sampled_from(elements = inertia_moment_units_list)) + + no_load_speed_units_list = list(AngularSpeed._AngularSpeed__UNITS.keys()) + no_load_speed_value = draw(floats(allow_nan = False, allow_infinity = False, min_value = 1e-10, exclude_min = True, max_value = 1000)) + no_load_speed_unit = draw(sampled_from(elements = no_load_speed_units_list)) + + maximum_torque_units_list = list(Torque._Torque__UNITS.keys()) + maximum_torque_value = draw(floats(allow_nan = False, allow_infinity = False, min_value = 1e-10, exclude_min = True, max_value = 1000)) + maximum_torque_unit = draw(sampled_from(elements = maximum_torque_units_list)) + + return DCMotor(name = name, + inertia_moment = InertiaMoment(inertia_moment_value, inertia_moment_unit), + no_load_speed = AngularSpeed(no_load_speed_value, no_load_speed_unit), + maximum_torque = Torque(maximum_torque_value, maximum_torque_unit)) @composite @@ -51,129 +75,21 @@ def transmissions(draw): return Transmission(motor = motor) -@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, float) - and not isinstance(type_to_check, int) and not isinstance(type_to_check, bool)]) -def rotating_object_angle_type_error(request): - return request.param - - -@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, float) - and not isinstance(type_to_check, int) and not isinstance(type_to_check, bool)]) -def rotating_object_speed_type_error(request): - return request.param - - -@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, float) - and not isinstance(type_to_check, int) and not isinstance(type_to_check, bool)]) -def rotating_object_acceleration_type_error(request): - return request.param - - -@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, float) - and not isinstance(type_to_check, int) and not isinstance(type_to_check, bool)]) -def rotating_object_torque_type_error(request): - return request.param - - -@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, float) - and not isinstance(type_to_check, int) and not isinstance(type_to_check, bool)]) -def rotating_object_driving_torque_type_error(request): - return request.param - - -@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, float) - and not isinstance(type_to_check, int) and not isinstance(type_to_check, bool)]) -def rotating_object_load_torque_type_error(request): - return request.param - - -dc_motor_init_type_error_1 = [{'name': type_to_check, 'inertia': 1, 'no_load_speed': 1, 'maximum_torque': 1} - for type_to_check in types_to_check if not isinstance(type_to_check, str)] - -dc_motor_init_type_error_2 = [{'name': 'motor', 'inertia': type_to_check, 'no_load_speed': 1, 'maximum_torque': 1} - for type_to_check in types_to_check if not isinstance(type_to_check, float) - and not isinstance(type_to_check, int) and not isinstance(type_to_check, bool)] - -dc_motor_init_type_error_3 = [{'name': 'motor', 'inertia': 1, 'no_load_speed': type_to_check, 'maximum_torque': 1} - for type_to_check in types_to_check if not isinstance(type_to_check, float) - and not isinstance(type_to_check, int) and not isinstance(type_to_check, bool)] - -dc_motor_init_type_error_4 = [{'name': 'motor', 'inertia': 1, 'no_load_speed': 1, 'maximum_torque': type_to_check} - for type_to_check in types_to_check if not isinstance(type_to_check, float) - and not isinstance(type_to_check, int) and not isinstance(type_to_check, bool)] - -@fixture(params = [*dc_motor_init_type_error_1, - *dc_motor_init_type_error_2, - *dc_motor_init_type_error_3, - *dc_motor_init_type_error_4]) -def dc_motor_init_type_error(request): - return request.param - - -@fixture(params = [{'name': '', 'inertia': 1, 'no_load_speed': 1, 'maximum_torque': 1}, - {'name': 'motor', 'inertia': -1, 'no_load_speed': 1, 'maximum_torque': 1}, - {'name': 'motor', 'inertia': 1, 'no_load_speed': -1, 'maximum_torque': 1}, - {'name': 'motor', 'inertia': 1, 'no_load_speed': 1, 'maximum_torque': -1}]) -def dc_motor_init_value_error(request): - return request.param - - -@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, RotatingObject)]) -def dc_motor_drives_type_error(request): - return request.param - - -@fixture(params = [{'name': 'gear', 'n_teeth': type_to_check, 'inertia': 1} for type_to_check in types_to_check - if not isinstance(type_to_check, int) and not isinstance(type_to_check, bool)]) -def spur_gear_init_type_error(request): - return request.param - - -@fixture(params = [{'name': '', 'n_teeth': 1, 'inertia': 1}, - {'name': 'gear', 'n_teeth': -1, 'inertia': 1}, - {'name': 'gear', 'n_teeth': 1, 'inertia': -1}]) -def spur_gear_init_value_error(request): - return request.param - - -@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, RotatingObject)]) -def spur_gear_driven_by_type_error(request): - return request.param - - -@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, RotatingObject)]) -def spur_gear_drives_type_error(request): - return request.param - - -@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, float)]) -def spur_gear_master_gear_ratio_type_error(request): - return request.param - - -@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, float) - and not isinstance(type_to_check, int) and not isinstance(type_to_check, bool)]) -def spur_gear_master_gear_efficiency_type_error(request): - return request.param - - -@fixture(params = [-1, 2]) -def spur_gear_master_gear_efficiency_value_error(request): - return request.param - +@composite +def time_intervals(draw): + value = draw(floats(allow_nan = False, allow_infinity = False, min_value = 1e-10, exclude_min = True, max_value = 1000)) + unit = draw(sampled_from(elements = list(TimeInterval._Time__UNITS.keys()))) -@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, Callable)]) -def spur_gear_external_torque_type_error(request): - return request.param + return TimeInterval(value = value, unit = unit) -add_gear_mating_type_error_1 = [{'gear 1': type_to_check, 'gear 2': basic_spur_gear, 'efficiency': 0.5} +add_gear_mating_type_error_1 = [{'master': type_to_check, 'slave': basic_spur_gear, 'efficiency': 0.5} for type_to_check in types_to_check if not isinstance(type_to_check, GearBase)] -add_gear_mating_type_error_2 = [{'gear 1': basic_spur_gear, 'gear 2': type_to_check, 'efficiency': 0.5} +add_gear_mating_type_error_2 = [{'master': basic_spur_gear, 'slave': type_to_check, 'efficiency': 0.5} for type_to_check in types_to_check if not isinstance(type_to_check, GearBase)] -add_gear_mating_type_error_3 = [{'gear 1': basic_spur_gear, 'gear 2': basic_spur_gear, 'efficiency': type_to_check} +add_gear_mating_type_error_3 = [{'master': basic_spur_gear, 'slave': basic_spur_gear, 'efficiency': type_to_check} for type_to_check in types_to_check if not isinstance(type_to_check, float) and not isinstance(type_to_check, int) and not isinstance(type_to_check, bool)] @@ -206,8 +122,9 @@ def transmission_init_type_error(request): return request.param -motor_transmission_solver_init_type_error = DCMotor(name = 'name', inertia = 1, no_load_speed = 1, maximum_torque = 1) -gear_transmission_solver_init_type_error = SpurGear(name = 'gear', n_teeth = 10, inertia = 1) +motor_transmission_solver_init_type_error = DCMotor(name = 'name', inertia_moment = InertiaMoment(1, 'kgm^2'), + no_load_speed = AngularSpeed(1000, 'rpm'), maximum_torque = Torque(1, 'Nm')) +gear_transmission_solver_init_type_error = SpurGear(name = 'gear', n_teeth = 10, inertia_moment = InertiaMoment(1, 'kgm^2')) add_fixed_joint(master = motor_transmission_solver_init_type_error, slave = gear_transmission_solver_init_type_error) transmission_solver_init_type_error = Transmission(motor = motor_transmission_solver_init_type_error) @@ -224,25 +141,23 @@ def chain(self): def chain(self, chain): self.__chain = chain -solver_init_type_error_1 = [{'time_discretization': type_to_check, 'simulation_time': 1, +solver_init_type_error_1 = [{'time_discretization': type_to_check, 'simulation_time': TimeInterval(5, 'sec'), 'transmission': transmission_solver_init_type_error} for type_to_check in types_to_check - if not isinstance(type_to_check, float) and not isinstance(type_to_check, int) - and not isinstance(type_to_check, bool)] + if not isinstance(type_to_check, TimeInterval)] -solver_init_type_error_2 = [{'time_discretization': 1, 'simulation_time': type_to_check, +solver_init_type_error_2 = [{'time_discretization': TimeInterval(1, 'sec'), 'simulation_time': type_to_check, 'transmission': transmission_solver_init_type_error} for type_to_check in types_to_check - if not isinstance(type_to_check, float) and not isinstance(type_to_check, int) - and not isinstance(type_to_check, bool)] + if not isinstance(type_to_check, TimeInterval)] -solver_init_type_error_3 = [{'time_discretization': 1, 'simulation_time': 5, +solver_init_type_error_3 = [{'time_discretization': TimeInterval(1, 'sec'), 'simulation_time': TimeInterval(5, 'sec'), 'transmission': type_to_check} for type_to_check in types_to_check if not isinstance(type_to_check, Transmission)] -solver_init_type_error_4 = [{'time_discretization': 1, 'simulation_time': 5, +solver_init_type_error_4 = [{'time_discretization': TimeInterval(1, 'sec'), 'simulation_time': TimeInterval(5, 'sec'), 'transmission': TransmissionFake([type_to_check, basic_spur_gear])} for type_to_check in types_to_check if not isinstance(type_to_check, MotorBase)] -solver_init_type_error_5 = [{'time_discretization': 1, 'simulation_time': 5, +solver_init_type_error_5 = [{'time_discretization': TimeInterval(1, 'sec'), 'simulation_time': TimeInterval(5, 'sec'), 'transmission': TransmissionFake([basic_dc_motor, type_to_check])} for type_to_check in types_to_check if not isinstance(type_to_check, RotatingObject)] @@ -254,15 +169,13 @@ def chain(self, chain): def solver_init_type_error(request): return request.param -motor_transmission_solver_init_value_error = DCMotor(name = 'name', inertia = 1, - no_load_speed = 1, maximum_torque = 1) -gear_transmission_solver_init_value_error = SpurGear(name = 'gear', n_teeth = 10, inertia = 1) +motor_transmission_solver_init_value_error = DCMotor(name = 'name', inertia_moment = InertiaMoment(1, 'kgm^2'), + no_load_speed = AngularSpeed(1000, 'rpm'), maximum_torque = Torque(1, 'Nm')) +gear_transmission_solver_init_value_error = SpurGear(name = 'gear', n_teeth = 10, inertia_moment = InertiaMoment(1, 'kgm^2')) add_fixed_joint(master = motor_transmission_solver_init_value_error, slave = gear_transmission_solver_init_value_error) transmission_solver_init_value_error = Transmission(motor = motor_transmission_solver_init_value_error) -@fixture(params = [{'time_discretization': -1, 'simulation_time': 5, 'transmission': transmission_solver_init_value_error}, - {'time_discretization': 1, 'simulation_time': -1, 'transmission': transmission_solver_init_value_error}, - {'time_discretization': 6, 'simulation_time': 5, 'transmission': transmission_solver_init_value_error}, - {'time_discretization': 1, 'simulation_time': 5, 'transmission': TransmissionFake([])}]) +@fixture(params = [{'time_discretization': TimeInterval(5, 'sec'), 'simulation_time': TimeInterval(1, 'sec'), 'transmission': transmission_solver_init_value_error}, + {'time_discretization': TimeInterval(1, 'sec'), 'simulation_time': TimeInterval(5, 'sec'), 'transmission': TransmissionFake([])}]) def solver_init_value_error(request): return request.param diff --git a/tests/test_dc_motor.py b/tests/test_dc_motor.py deleted file mode 100644 index 9fbdb20..0000000 --- a/tests/test_dc_motor.py +++ /dev/null @@ -1,79 +0,0 @@ -from gearpy import DCMotor, SpurGear -from hypothesis import given, settings -from hypothesis.strategies import text, floats -from pytest import mark, raises -from tests.conftest import basic_dc_motor - - -@mark.dc_motor -class TestDCMotorInit: - - - @mark.genuine - @given(name = text(min_size = 1), - inertia = floats(min_value = 0, exclude_min = True), - no_load_speed = floats(min_value = 0, exclude_min = True), - maximum_torque = floats(min_value = 0, exclude_min = True)) - @settings(max_examples = 100) - def test_method(self, name, inertia, no_load_speed, maximum_torque): - motor = DCMotor(name = name, inertia = inertia, no_load_speed = no_load_speed, maximum_torque = maximum_torque) - - assert motor.name == name - assert motor.inertia == inertia - assert motor.no_load_speed == no_load_speed - assert motor.maximum_torque == maximum_torque - - - @mark.error - def test_raises_type_error(self, dc_motor_init_type_error): - with raises(TypeError): - DCMotor(name = dc_motor_init_type_error['name'], - inertia = dc_motor_init_type_error['inertia'], - no_load_speed = dc_motor_init_type_error['no_load_speed'], - maximum_torque = dc_motor_init_type_error['maximum_torque']) - - - @mark.error - def test_raises_value_error(self, dc_motor_init_value_error): - with raises(ValueError): - DCMotor(name = dc_motor_init_value_error['name'], - inertia = dc_motor_init_value_error['inertia'], - no_load_speed = dc_motor_init_value_error['no_load_speed'], - maximum_torque = dc_motor_init_value_error['maximum_torque']) - - -@mark.dc_motor -class TestDCMotorDrives: - - - @mark.genuine - def test_property(self): - gear = SpurGear(name = 'gear', n_teeth = 10, inertia = 1) - basic_dc_motor.drives = gear - - assert basic_dc_motor.drives == gear - - - @mark.error - def test_raises_type_error(self, dc_motor_drives_type_error): - with raises(TypeError): - basic_dc_motor.drives = dc_motor_drives_type_error - - -@mark.dc_motor -class TestDCMotorComputeTorque: - - - @mark.genuine - @given(name = text(min_size = 1), - inertia = floats(min_value = 0, exclude_min = True), - no_load_speed = floats(min_value = 0, exclude_min = True), - maximum_torque = floats(min_value = 0, exclude_min = True), - speed = floats(allow_nan = False, allow_infinity = False)) - @settings(max_examples = 100) - def test_method(self, name, inertia, no_load_speed, maximum_torque, speed): - motor = DCMotor(name = name, inertia = inertia, no_load_speed = no_load_speed, maximum_torque = maximum_torque) - motor.speed = speed - torque = motor.compute_torque() - - assert isinstance(torque, float) or isinstance(torque, int) diff --git a/tests/test_gear/conftest.py b/tests/test_gear/conftest.py new file mode 100644 index 0000000..b71a2c1 --- /dev/null +++ b/tests/test_gear/conftest.py @@ -0,0 +1,59 @@ +from gearpy.mechanical_object import RotatingObject +from gearpy.units import InertiaMoment +from pytest import fixture +from tests.conftest import types_to_check +from typing import Callable + + +spur_gear_init_type_error_1 = [{'name': type_to_check, 'n_teeth': 10, 'inertia_moment': InertiaMoment(1, 'kgm^2')} + for type_to_check in types_to_check if not isinstance(type_to_check, str)] + +spur_gear_init_type_error_2 = [{'name': 'gear', 'n_teeth': type_to_check, 'inertia_moment': InertiaMoment(1, 'kgm^2')} + for type_to_check in types_to_check if not isinstance(type_to_check, int) + and not isinstance(type_to_check, bool)] + +spur_gear_init_type_error_3 = [{'name': 'gear', 'n_teeth': 10, 'inertia_moment': type_to_check} + for type_to_check in types_to_check if not isinstance(type_to_check, InertiaMoment)] + +@fixture(params = [*spur_gear_init_type_error_1, + *spur_gear_init_type_error_2, + *spur_gear_init_type_error_3]) +def spur_gear_init_type_error(request): + return request.param + + +@fixture(params = [{'name': '', 'n_teeth': 1, 'inertia_moment': InertiaMoment(1, 'kgm^2')}, + {'name': 'gear', 'n_teeth': -1, 'inertia_moment': InertiaMoment(1, 'kgm^2')}]) +def spur_gear_init_value_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, RotatingObject)]) +def spur_gear_driven_by_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, RotatingObject)]) +def spur_gear_drives_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, float)]) +def spur_gear_master_gear_ratio_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, float) + and not isinstance(type_to_check, int) and not isinstance(type_to_check, bool)]) +def spur_gear_master_gear_efficiency_type_error(request): + return request.param + + +@fixture(params = [-1, 2]) +def spur_gear_master_gear_efficiency_value_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, Callable)]) +def spur_gear_external_torque_type_error(request): + return request.param diff --git a/tests/test_spur_gear.py b/tests/test_gear/test_spur_gear.py similarity index 79% rename from tests/test_spur_gear.py rename to tests/test_gear/test_spur_gear.py index e8ab3f9..455f330 100644 --- a/tests/test_spur_gear.py +++ b/tests/test_gear/test_spur_gear.py @@ -1,8 +1,11 @@ -from gearpy import DCMotor, SpurGear +from gearpy.gear import SpurGear +from gearpy.motor import DCMotor +from gearpy.units import AngularSpeed, InertiaMoment, Torque from hypothesis import given, settings from hypothesis.strategies import text, floats, integers, functions from pytest import mark, raises from tests.conftest import basic_spur_gear +from tests.test_units.test_inertia_moment.conftest import inertia_moments @mark.spur_gear @@ -12,30 +15,26 @@ class TestSpurGearInit: @mark.genuine @given(name = text(min_size = 1), n_teeth = integers(min_value = 1), - inertia = floats(min_value = 0, exclude_min = True)) + inertia_moment = inertia_moments()) @settings(max_examples = 100) - def test_method(self, name, n_teeth, inertia): - gear = SpurGear(name = name, n_teeth = n_teeth, inertia = inertia) + def test_method(self, name, n_teeth, inertia_moment): + gear = SpurGear(name = name, n_teeth = n_teeth, inertia_moment = inertia_moment) assert gear.name == name assert gear.n_teeth == n_teeth - assert gear.inertia == inertia + assert gear.inertia_moment == inertia_moment @mark.error def test_raises_type_error(self, spur_gear_init_type_error): with raises(TypeError): - SpurGear(name = spur_gear_init_type_error['name'], - n_teeth = spur_gear_init_type_error['n_teeth'], - inertia = spur_gear_init_type_error['inertia']) + SpurGear(**spur_gear_init_type_error) @mark.error def test_raises_value_error(self, spur_gear_init_value_error): with raises(ValueError): - SpurGear(name = spur_gear_init_value_error['name'], - n_teeth = spur_gear_init_value_error['n_teeth'], - inertia = spur_gear_init_value_error['inertia']) + SpurGear(**spur_gear_init_value_error) @mark.spur_gear @@ -44,8 +43,9 @@ class TestSpurGearDrivenBy: @mark.genuine def test_property(self): - motor = DCMotor(name = 'motor', inertia = 1, no_load_speed = 1, maximum_torque = 1) - gear = SpurGear(name = 'gear', n_teeth = 10, inertia = 1) + motor = DCMotor(name = 'motor', inertia_moment = InertiaMoment(1, 'kgm^2'), + no_load_speed = AngularSpeed(1000, 'rpm'), maximum_torque = Torque(1, 'Nm')) + gear = SpurGear(name = 'gear', n_teeth = 10, inertia_moment = InertiaMoment(1, 'kgm^2')) for master in [motor, gear]: basic_spur_gear.driven_by = master @@ -65,7 +65,7 @@ class TestSpurGearDrives: @mark.genuine def test_property(self): - gear = SpurGear(name = 'gear', n_teeth = 10, inertia = 1) + gear = SpurGear(name = 'gear', n_teeth = 10, inertia_moment = InertiaMoment(1, 'kgm^2')) basic_spur_gear.drives = gear assert basic_spur_gear.drives == gear @@ -105,6 +105,7 @@ def test_raises_value_error(self): @mark.spur_gear class TestSpurGearMasterGearEfficiency: + @mark.genuine @given(master_gear_efficiency = floats(allow_nan = False, allow_infinity = False, min_value = 0, exclude_min = False, max_value = 1, exclude_max = False)) @@ -114,6 +115,7 @@ def test_property(self, master_gear_efficiency): assert basic_spur_gear.master_gear_efficiency == master_gear_efficiency + @mark.error def test_raises_type_error(self, spur_gear_master_gear_efficiency_type_error): with raises(TypeError): @@ -129,6 +131,7 @@ def test_raises_value_error(self, spur_gear_master_gear_efficiency_value_error): @mark.spur_gear class TestSpurGearExternalTorque: + @mark.genuine @given(external_torque = functions()) @settings(max_examples = 100) @@ -137,6 +140,7 @@ def test_property(self, external_torque): assert basic_spur_gear.external_torque == external_torque + @mark.error def test_raises_type_error(self, spur_gear_external_torque_type_error): with raises(TypeError): diff --git a/tests/test_motor/conftest.py b/tests/test_motor/conftest.py new file mode 100644 index 0000000..80773c2 --- /dev/null +++ b/tests/test_motor/conftest.py @@ -0,0 +1,40 @@ +from gearpy.mechanical_object import RotatingObject +from gearpy.units import AngularSpeed, InertiaMoment, Torque +from tests.conftest import types_to_check +from pytest import fixture + + +dc_motor_init_type_error_1 = [{'name': type_to_check, 'inertia_moment': InertiaMoment(1, 'kgm^2'), + 'no_load_speed': AngularSpeed(1000, 'rpm'), 'maximum_torque': Torque(1, 'Nm')} + for type_to_check in types_to_check if not isinstance(type_to_check, str)] + +dc_motor_init_type_error_2 = [{'name': 'motor', 'inertia_moment': type_to_check, + 'no_load_speed': AngularSpeed(1000, 'rpm'), 'maximum_torque': Torque(1, 'Nm')} + for type_to_check in types_to_check if not isinstance(type_to_check, InertiaMoment)] + +dc_motor_init_type_error_3 = [{'name': 'motor', 'inertia_moment': InertiaMoment(1, 'kgm^2'), + 'no_load_speed': type_to_check, 'maximum_torque': Torque(1, 'Nm')} + for type_to_check in types_to_check if not isinstance(type_to_check, AngularSpeed)] + +dc_motor_init_type_error_4 = [{'name': 'motor', 'inertia_moment': InertiaMoment(1, 'kgm^2'), + 'no_load_speed': AngularSpeed(1000, 'rpm'), 'maximum_torque': type_to_check} + for type_to_check in types_to_check if not isinstance(type_to_check, Torque)] + +@fixture(params = [*dc_motor_init_type_error_1, + *dc_motor_init_type_error_2, + *dc_motor_init_type_error_3, + *dc_motor_init_type_error_4]) +def dc_motor_init_type_error(request): + return request.param + + +@fixture(params = [{'name': '', 'inertia_moment': InertiaMoment(1, 'kgm^2'), 'no_load_speed': AngularSpeed(1000, 'rpm'), 'maximum_torque': Torque(1, 'Nm')}, + {'name': 'motor', 'inertia_moment': InertiaMoment(1, 'kgm^2'), 'no_load_speed': AngularSpeed(-1000, 'rpm'), 'maximum_torque': Torque(1, 'Nm')}, + {'name': 'motor', 'inertia_moment': InertiaMoment(1, 'kgm^2'), 'no_load_speed': AngularSpeed(1000, 'rpm'), 'maximum_torque': Torque(-1, 'Nm')}]) +def dc_motor_init_value_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, RotatingObject)]) +def dc_motor_drives_type_error(request): + return request.param diff --git a/tests/test_motor/test_dc_motor.py b/tests/test_motor/test_dc_motor.py new file mode 100644 index 0000000..439b46d --- /dev/null +++ b/tests/test_motor/test_dc_motor.py @@ -0,0 +1,80 @@ +from gearpy.gear import SpurGear +from gearpy.motor import DCMotor +from gearpy.units import Torque +from hypothesis import given, settings +from hypothesis.strategies import text +from pytest import mark, raises +from tests.conftest import basic_dc_motor +from tests.test_units.test_angular_speed.conftest import angular_speeds +from tests.test_units.test_inertia_moment.conftest import inertia_moments, basic_inertia_moment +from tests.test_units.test_torque.conftest import torques + + +@mark.dc_motor +class TestDCMotorInit: + + + @mark.genuine + @given(name = text(min_size = 1), + inertia_moment = inertia_moments(), + no_load_speed = angular_speeds(), + maximum_torque = torques()) + @settings(max_examples = 100) + def test_method(self, name, inertia_moment, no_load_speed, maximum_torque): + if no_load_speed.value > 0 and maximum_torque.value > 0: + motor = DCMotor(name = name, inertia_moment = inertia_moment, no_load_speed = no_load_speed, maximum_torque = maximum_torque) + + assert motor.name == name + assert motor.inertia_moment == inertia_moment + assert motor.no_load_speed == no_load_speed + assert motor.maximum_torque == maximum_torque + + + @mark.error + def test_raises_type_error(self, dc_motor_init_type_error): + with raises(TypeError): + DCMotor(**dc_motor_init_type_error) + + + @mark.error + def test_raises_value_error(self, dc_motor_init_value_error): + with raises(ValueError): + DCMotor(**dc_motor_init_value_error) + + +@mark.dc_motor +class TestDCMotorDrives: + + + @mark.genuine + def test_property(self): + gear = SpurGear(name = 'gear', n_teeth = 10, inertia_moment = basic_inertia_moment) + basic_dc_motor.drives = gear + + assert basic_dc_motor.drives == gear + + + @mark.error + def test_raises_type_error(self, dc_motor_drives_type_error): + with raises(TypeError): + basic_dc_motor.drives = dc_motor_drives_type_error + + +@mark.dc_motor +class TestDCMotorComputeTorque: + + + @mark.genuine + @given(name = text(min_size = 1), + inertia_moment = inertia_moments(), + no_load_speed = angular_speeds(), + maximum_torque = torques(), + speed = angular_speeds()) + @settings(max_examples = 100) + def test_method(self, name, inertia_moment, no_load_speed, maximum_torque, speed): + if no_load_speed.value > 1e-10 and maximum_torque.value > 1e-10: + motor = DCMotor(name = name, inertia_moment = inertia_moment, no_load_speed = no_load_speed, maximum_torque = maximum_torque) + motor.angular_speed = speed + torque = motor.compute_torque() + + assert isinstance(torque, Torque) diff --git a/tests/test_rotating_object/conftest.py b/tests/test_rotating_object/conftest.py new file mode 100644 index 0000000..85dcf87 --- /dev/null +++ b/tests/test_rotating_object/conftest.py @@ -0,0 +1,37 @@ +from gearpy.units import AngularAcceleration, AngularPosition, AngularSpeed, Torque +from tests.conftest import types_to_check +from tests.conftest import basic_spur_gear, basic_dc_motor +from pytest import fixture + + +basic_rotating_objects = [basic_dc_motor, basic_spur_gear] + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, AngularPosition)]) +def rotating_object_angular_position_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, AngularSpeed)]) +def rotating_object_angular_speed_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, AngularAcceleration)]) +def rotating_object_angular_acceleration_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, Torque)]) +def rotating_object_torque_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, Torque)]) +def rotating_object_driving_torque_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, Torque)]) +def rotating_object_load_torque_type_error(request): + return request.param diff --git a/tests/test_rotating_object.py b/tests/test_rotating_object/test_rotating_object.py similarity index 57% rename from tests/test_rotating_object.py rename to tests/test_rotating_object/test_rotating_object.py index 5d18ca7..948538d 100644 --- a/tests/test_rotating_object.py +++ b/tests/test_rotating_object/test_rotating_object.py @@ -1,70 +1,73 @@ from hypothesis import given, settings -from hypothesis.strategies import floats from pytest import mark, raises -from tests.conftest import basic_rotating_objects +from tests.test_rotating_object.conftest import basic_rotating_objects +from tests.test_units.test_angular_acceleration.conftest import angular_accelerations +from tests.test_units.test_angular_position.conftest import angular_positions +from tests.test_units.test_angular_speed.conftest import angular_speeds +from tests.test_units.test_torque.conftest import torques @mark.rotating_object -class TestRotatingObjectAngle: +class TestRotatingObjectAngularPosition: @mark.genuine - @given(angle = floats(allow_nan = False, allow_infinity = False)) + @given(angular_position = angular_positions()) @settings(max_examples = 100) - def test_property(self, angle): + def test_property(self, angular_position): for rotating_object in basic_rotating_objects: - rotating_object.angle = angle + rotating_object.angular_position = angular_position - assert rotating_object.angle == angle + assert rotating_object.angular_position == angular_position @mark.error - def test_raises_type_error(self, rotating_object_angle_type_error): + def test_raises_type_error(self, rotating_object_angular_position_type_error): for rotating_object in basic_rotating_objects: with raises(TypeError): - rotating_object.angle = rotating_object_angle_type_error + rotating_object.angular_position = rotating_object_angular_position_type_error @mark.rotating_object -class TestRotatingObjectSpeed: +class TestRotatingObjectAngularSpeed: @mark.genuine - @given(speed = floats(allow_nan = False, allow_infinity = False)) + @given(angular_speed = angular_speeds()) @settings(max_examples = 100) - def test_property(self, speed): + def test_property(self, angular_speed): for rotating_object in basic_rotating_objects: - rotating_object.speed = speed + rotating_object.angular_speed = angular_speed - assert rotating_object.speed == speed + assert rotating_object.angular_speed == angular_speed @mark.error - def test_raises_type_error(self, rotating_object_speed_type_error): + def test_raises_type_error(self, rotating_object_angular_speed_type_error): for rotating_object in basic_rotating_objects: with raises(TypeError): - rotating_object.speed = rotating_object_speed_type_error + rotating_object.angular_speed = rotating_object_angular_speed_type_error @mark.rotating_object -class TestRotatingObjectAcceleration: +class TestRotatingObjectAngularAcceleration: @mark.genuine - @given(acceleration = floats(allow_nan = False, allow_infinity = False)) + @given(angular_acceleration = angular_accelerations()) @settings(max_examples = 100) - def test_property(self, acceleration): + def test_property(self, angular_acceleration): for rotating_object in basic_rotating_objects: - rotating_object.acceleration = acceleration + rotating_object.angular_acceleration = angular_acceleration - assert rotating_object.acceleration == acceleration + assert rotating_object.angular_acceleration == angular_acceleration @mark.error - def test_raises_type_error(self, rotating_object_acceleration_type_error): + def test_raises_type_error(self, rotating_object_angular_acceleration_type_error): for rotating_object in basic_rotating_objects: with raises(TypeError): - rotating_object.acceleration = rotating_object_acceleration_type_error + rotating_object.angular_acceleration = rotating_object_angular_acceleration_type_error @mark.rotating_object @@ -72,7 +75,7 @@ class TestRotatingObjectTorque: @mark.genuine - @given(torque = floats(allow_nan = False, allow_infinity = False)) + @given(torque = torques()) @settings(max_examples = 100) def test_property(self, torque): for rotating_object in basic_rotating_objects: @@ -93,7 +96,7 @@ class TestRotatingObjectDrivingTorque: @mark.genuine - @given(driving_torque = floats(allow_nan = False, allow_infinity = False)) + @given(driving_torque = torques()) @settings(max_examples = 100) def test_property(self, driving_torque): for rotating_object in basic_rotating_objects: @@ -114,7 +117,7 @@ class TestRotatingObjectLoadTorque: @mark.genuine - @given(load_torque = floats(allow_nan = False, allow_infinity = False)) + @given(load_torque = torques()) @settings(max_examples = 100) def test_property(self, load_torque): for rotating_object in basic_rotating_objects: @@ -140,7 +143,7 @@ def test_property(self): time_variables = rotating_object.time_variables assert isinstance(time_variables, dict) - assert ['angle', 'speed', 'acceleration', + assert ['angular position', 'angular speed', 'angular acceleration', 'torque', 'driving torque', 'load torque'] == list(time_variables.keys()) assert all([value == [] for value in time_variables.values()]) @@ -150,17 +153,17 @@ class TestRotatingObjectUpdateTimeVariables: @mark.genuine - @given(angle = floats(allow_nan = False, allow_infinity = False), - speed = floats(allow_nan = False, allow_infinity = False), - acceleration = floats(allow_nan = False, allow_infinity = False), - torque = floats(allow_nan = False, allow_infinity = False), - driving_torque = floats(allow_nan = False, allow_infinity = False), - load_torque = floats(allow_nan = False, allow_infinity = False)) - def test_method(self, angle, speed, acceleration, torque, driving_torque, load_torque): + @given(angular_position = angular_positions(), + angular_speed = angular_speeds(), + angular_acceleration = angular_accelerations(), + torque = torques(), + driving_torque = torques(), + load_torque = torques()) + def test_method(self, angular_position, angular_speed, angular_acceleration, torque, driving_torque, load_torque): for rotating_object in basic_rotating_objects: - rotating_object.angle = angle - rotating_object.speed = speed - rotating_object.acceleration = acceleration + rotating_object.angular_position = angular_position + rotating_object.angular_speed = angular_speed + rotating_object.angular_acceleration = angular_acceleration rotating_object.torque = torque rotating_object.driving_torque = driving_torque rotating_object.load_torque = load_torque @@ -169,11 +172,11 @@ def test_method(self, angle, speed, acceleration, torque, driving_torque, load_t time_variables = rotating_object.time_variables assert isinstance(time_variables, dict) - assert ['angle', 'speed', 'acceleration', + assert ['angular position', 'angular speed', 'angular acceleration', 'torque', 'driving torque', 'load torque'] == list(time_variables.keys()) - assert time_variables['angle'][-1] == angle - assert time_variables['speed'][-1] == speed - assert time_variables['acceleration'][-1] == acceleration + assert time_variables['angular position'][-1] == angular_position + assert time_variables['angular speed'][-1] == angular_speed + assert time_variables['angular acceleration'][-1] == angular_acceleration assert time_variables['torque'][-1] == torque assert time_variables['driving torque'][-1] == driving_torque assert time_variables['load torque'][-1] == load_torque diff --git a/tests/test_solver.py b/tests/test_solver.py index 57c9026..9dae17d 100644 --- a/tests/test_solver.py +++ b/tests/test_solver.py @@ -1,9 +1,12 @@ -from gearpy import Solver +from gearpy.solver import Solver +from gearpy.units import Torque, Time from hypothesis import given, settings from hypothesis.strategies import floats, integers import numpy as np from pytest import mark, raises -from tests.conftest import transmissions +from tests.conftest import transmissions, time_intervals +from tests.test_units.test_angular_position.conftest import angular_positions +from tests.test_units.test_angular_speed.conftest import angular_speeds @mark.solver @@ -11,8 +14,7 @@ class TestSolverInit: @mark.genuine - @given(time_discretization = floats(min_value = 0, max_value = 1, exclude_min = True, - allow_nan = False, allow_infinity = False), + @given(time_discretization = time_intervals(), simulation_steps = integers(min_value = 2, max_value = 1000), transmission = transmissions()) @settings(max_examples = 100) @@ -25,23 +27,19 @@ def test_method(self, time_discretization, simulation_steps, transmission): assert solver.time_discretization == time_discretization assert solver.simulation_time == simulation_time assert solver.transmission_chain == transmission.chain - assert solver.time == [0] + assert solver.time == [Time(value = 0, unit = time_discretization.unit)] @mark.error def test_raises_type_error(self, solver_init_type_error): with raises(TypeError): - Solver(time_discretization = solver_init_type_error['time_discretization'], - simulation_time = solver_init_type_error['simulation_time'], - transmission = solver_init_type_error['transmission']) + Solver(**solver_init_type_error) @mark.error def test_raises_value_error(self, solver_init_value_error): with raises(ValueError): - Solver(time_discretization = solver_init_value_error['time_discretization'], - simulation_time = solver_init_value_error['simulation_time'], - transmission = solver_init_value_error['transmission']) + Solver(**solver_init_value_error) @mark.solver @@ -49,20 +47,20 @@ class TestSolverRun: @mark.genuine - @given(time_discretization = floats(min_value = 1e-10, max_value = 10, allow_nan = False, allow_infinity = False), + @given(time_discretization = time_intervals(), simulation_steps = floats(min_value = 5, max_value = 1000, allow_nan = False, allow_infinity = False), transmission = transmissions(), - initial_angle = floats(allow_nan = False, allow_infinity = False), - initial_speed = floats(allow_nan = False, allow_infinity = False)) + initial_angular_position = angular_positions(), + initial_angular_speed = angular_speeds()) @settings(max_examples = 100, deadline = None) - def test_method(self, time_discretization, simulation_steps, transmission, initial_angle, initial_speed): - transmission.chain[-1].angle = initial_angle - transmission.chain[-1].speed = initial_speed - transmission.chain[-1].external_torque = lambda time, angle, speed: 0.001 + def test_method(self, time_discretization, simulation_steps, transmission, initial_angular_position, initial_angular_speed): + transmission.chain[-1].angular_position = initial_angular_position + transmission.chain[-1].angular_speed = initial_angular_speed + transmission.chain[-1].external_torque = lambda time, angular_position, angular_speed: Torque(0.001, 'Nm') simulation_time = time_discretization*simulation_steps solver = Solver(time_discretization = time_discretization, simulation_time = simulation_time, transmission = transmission) solver.run() - assert len(solver.time) == len(np.arange(time_discretization, simulation_time, time_discretization)) + 1 + assert len(solver.time) == len(np.arange(time_discretization.value, simulation_time.value, time_discretization.value)) + 1 diff --git a/tests/test_transmission.py b/tests/test_transmission.py index 2c9ed88..30ff854 100644 --- a/tests/test_transmission.py +++ b/tests/test_transmission.py @@ -1,4 +1,7 @@ -from gearpy import DCMotor, Transmission, add_gear_mating, add_fixed_joint +from gearpy.motor import DCMotor +from gearpy.transmission import Transmission +from gearpy.units import AngularSpeed, InertiaMoment, Torque +from gearpy.utils import add_gear_mating, add_fixed_joint from hypothesis import given, settings from hypothesis.strategies import lists from pytest import mark, raises @@ -40,6 +43,7 @@ def test_raises_type_error(self, transmission_init_type_error): @mark.error def test_raises_value_error(self): - motor = DCMotor(name = 'motor', inertia = 1, no_load_speed = 1, maximum_torque = 1) + motor = DCMotor(name = 'motor', inertia_moment = InertiaMoment(1, 'kgm^2'), + no_load_speed = AngularSpeed(1000, 'rpm'), maximum_torque = Torque(1, 'Nm')) with raises(ValueError): Transmission(motor = motor) diff --git a/tests/test_units/test_angular_acceleration/conftest.py b/tests/test_units/test_angular_acceleration/conftest.py new file mode 100644 index 0000000..3b96625 --- /dev/null +++ b/tests/test_units/test_angular_acceleration/conftest.py @@ -0,0 +1,113 @@ +from gearpy.units import AngularAcceleration, Time +from hypothesis.strategies import composite, floats, sampled_from +import numpy as np +from tests.conftest import types_to_check +from pytest import fixture + + +basic_angular_acceleration = AngularAcceleration(1, 'rad/s^2') + + +@composite +def angular_accelerations(draw): + value = draw(floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000)) + unit = draw(sampled_from(elements = list(AngularAcceleration._AngularAcceleration__UNITS.keys()))) + + return AngularAcceleration(value = value, unit = unit) + +@composite +def times(draw): + value = draw(floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000)) + unit = draw(sampled_from(elements = list(Time._Time__UNITS.keys()))) + + return Time(value = value, unit = unit) + + +angular_acceleration_init_type_error_1 = [{'value': type_to_check, 'unit': 'unit'} for type_to_check in types_to_check + if not isinstance(type_to_check, float) + and not isinstance(type_to_check, int)] + +angular_acceleration_init_type_error_2 = [{'value': 1, 'unit': types_to_check} for type_to_check in types_to_check + if not isinstance(type_to_check, str)] + +@fixture(params = [*angular_acceleration_init_type_error_1, + *angular_acceleration_init_type_error_2]) +def angular_acceleration_init_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, AngularAcceleration)]) +def angular_acceleration_add_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, AngularAcceleration)]) +def angular_acceleration_sub_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, float) + and not isinstance(type_to_check, int) and not isinstance(type_to_check, Time)]) +def angular_acceleration_mul_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, float) + and not isinstance(type_to_check, int) and not isinstance(type_to_check, np.ndarray) + and not isinstance(type_to_check, Time)]) +def angular_acceleration_rmul_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, float) + and not isinstance(type_to_check, int) and not isinstance(type_to_check, AngularAcceleration)]) +def angular_acceleration_truediv_type_error(request): + return request.param + + +@fixture(params = [0, 0.0, AngularAcceleration(0, 'rad/s^2')]) +def angular_acceleration_truediv_zero_division_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, AngularAcceleration)]) +def angular_acceleration_eq_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, AngularAcceleration)]) +def angular_acceleration_ne_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, AngularAcceleration)]) +def angular_acceleration_gt_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, AngularAcceleration)]) +def angular_acceleration_ge_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, AngularAcceleration)]) +def angular_acceleration_lt_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, AngularAcceleration)]) +def angular_acceleration_le_type_error(request): + return request.param + + +angular_acceleration_to_type_error_1 = [{'target_unit': type_to_check, 'inplace': True} + for type_to_check in types_to_check if not isinstance(type_to_check, str)] + +angular_acceleration_to_type_error_2 = [{'target_unit': 'target_unit', 'inplace': type_to_check} + for type_to_check in types_to_check if not isinstance(type_to_check, bool) + and not isinstance(type_to_check, int)] + +@fixture(params = [*angular_acceleration_to_type_error_1, + *angular_acceleration_to_type_error_2]) +def angular_acceleration_to_type_error(request): + return request.param \ No newline at end of file diff --git a/tests/test_units/test_angular_acceleration/test_angular_acceleration.py b/tests/test_units/test_angular_acceleration/test_angular_acceleration.py new file mode 100644 index 0000000..8dee305 --- /dev/null +++ b/tests/test_units/test_angular_acceleration/test_angular_acceleration.py @@ -0,0 +1,405 @@ +from gearpy.units import AngularAcceleration, AngularSpeed, Time +from hypothesis.strategies import floats, sampled_from, one_of, booleans +from hypothesis import given, settings +from tests.test_units.test_angular_acceleration.conftest import basic_angular_acceleration, angular_accelerations, times +from pytest import mark, raises + + +units_list = list(AngularAcceleration._AngularAcceleration__UNITS.keys()) + + +@mark.units +class TestAngularAccelerationInit: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000), + unit = sampled_from(elements = units_list)) + @settings(max_examples = 100) + def test_method(self, value, unit): + angular_acceleration = AngularAcceleration(value = value, unit = unit) + + assert angular_acceleration.value == value + assert angular_acceleration.unit == unit + + + @mark.error + def test_raises_type_error(self, angular_acceleration_init_type_error): + with raises(TypeError): + AngularAcceleration(**angular_acceleration_init_type_error) + + + @mark.error + def test_raises_key_error(self): + fake_units = [f'fake {unit}' for unit in units_list] + for fake_unit in fake_units: + with raises(KeyError): + AngularAcceleration(value = 1, unit = fake_unit) + + +@mark.units +class TestAngularAccelerationRepr: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000), + unit = sampled_from(elements = units_list)) + @settings(max_examples = 100) + def test_method(self, value, unit): + angular_acceleration = AngularAcceleration(value = value, unit = unit) + + assert str(angular_acceleration) == f'{value} {unit}' + + + +@mark.units +class TestAngularAccelerationAdd: + + + @mark.genuine + @given(value_1 = floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000), + unit_1 = sampled_from(elements = units_list), + value_2 = floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000), + unit_2 = sampled_from(elements = units_list)) + @settings(max_examples = 100) + def test_method(self, value_1, unit_1, value_2, unit_2): + angular_acceleration_1 = AngularAcceleration(value = value_1, unit = unit_1) + angular_acceleration_2 = AngularAcceleration(value = value_2, unit = unit_2) + result = angular_acceleration_1 + angular_acceleration_2 + + assert isinstance(result, AngularAcceleration) + assert result.value == angular_acceleration_1.value + angular_acceleration_2.to(unit_1).value + assert result.unit == angular_acceleration_1.unit + + + @mark.error + def test_raises_type_error(self, angular_acceleration_add_type_error): + with raises(TypeError): + assert basic_angular_acceleration + angular_acceleration_add_type_error + + +@mark.units +class TestAngularAccelerationSub: + + + @mark.genuine + @given(value_1 = floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000), + unit_1 = sampled_from(elements = units_list), + value_2 = floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000), + unit_2 = sampled_from(elements = units_list)) + @settings(max_examples = 100) + def test_method(self, value_1, unit_1, value_2, unit_2): + angular_acceleration_1 = AngularAcceleration(value = value_1, unit = unit_1) + angular_acceleration_2 = AngularAcceleration(value = value_2, unit = unit_2) + result = angular_acceleration_1 - angular_acceleration_2 + + assert isinstance(result, AngularAcceleration) + assert result.value == angular_acceleration_1.value - angular_acceleration_2.to(unit_1).value + assert result.unit == angular_acceleration_1.unit + + + @mark.error + def test_raises_type_error(self, angular_acceleration_sub_type_error): + with raises(TypeError): + assert basic_angular_acceleration - angular_acceleration_sub_type_error + + +@mark.units +class TestAngularAccelerationMul: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000), + unit = sampled_from(elements = units_list), + multiplier = one_of(floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000), + times())) + @settings(max_examples = 100) + def test_method(self, value, unit, multiplier): + angular_acceleration = AngularAcceleration(value = value, unit = unit) + result = angular_acceleration*multiplier + + if isinstance(multiplier, Time): + assert isinstance(result, AngularSpeed) + assert result.value == angular_acceleration.to('rad/s^2').value*multiplier.to('sec').value + assert result.unit == 'rad/s' + else: + assert isinstance(result, AngularAcceleration) + assert result.value == angular_acceleration.value*multiplier + assert result.unit == angular_acceleration.unit + + + @mark.error + def test_raises_type_error(self, angular_acceleration_mul_type_error): + with raises(TypeError): + assert basic_angular_acceleration*angular_acceleration_mul_type_error + + +@mark.units +class TestAngularAccelerationRmul: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000), + unit = sampled_from(elements = units_list), + multiplier = one_of(floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000), + times())) + @settings(max_examples = 100) + def test_method(self, value, unit, multiplier): + angular_acceleration = AngularAcceleration(value = value, unit = unit) + result = multiplier*angular_acceleration + + if isinstance(multiplier, Time): + assert isinstance(result, AngularSpeed) + assert result.value == angular_acceleration.to('rad/s^2').value*multiplier.to('sec').value + assert result.unit == 'rad/s' + else: + assert isinstance(result, AngularAcceleration) + assert result.value == angular_acceleration.value*multiplier + assert result.unit == angular_acceleration.unit + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000), + unit = sampled_from(elements = units_list)) + @settings(max_examples = 100) + def test_method_patch(self, value, unit): + angular_acceleration = AngularAcceleration(value = value, unit = unit) + + class FakeTime(Time): + + def __mul__(self, other: AngularAcceleration): return NotImplemented + + fake_multiplier = FakeTime(1, 'sec') + result = fake_multiplier*angular_acceleration + + assert isinstance(result, AngularSpeed) + assert result.value == angular_acceleration.to('rad/s^2').value*fake_multiplier.to('sec').value + assert result.unit == 'rad/s' + + + @mark.error + def test_raises_type_error(self, angular_acceleration_rmul_type_error): + with raises(TypeError): + assert angular_acceleration_rmul_type_error*basic_angular_acceleration + + +@mark.units +class TestAngularAccelerationTruediv: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000), + unit = sampled_from(elements = units_list), + divider = one_of(floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000), + angular_accelerations())) + @settings(max_examples = 100) + def test_method(self, value, unit, divider): + angular_acceleration = AngularAcceleration(value = value, unit = unit) + + if isinstance(divider, AngularAcceleration): + if abs(divider.value) >= 1e-300: + result = angular_acceleration/divider + assert isinstance(result, float) + assert result == angular_acceleration.value/divider.to(unit).value + else: + if divider != 0: + result = angular_acceleration/divider + assert isinstance(result, AngularAcceleration) + assert result.value == angular_acceleration.value/divider + assert result.unit == angular_acceleration.unit + + + @mark.error + def test_raises_type_error(self, angular_acceleration_truediv_type_error): + with raises(TypeError): + assert basic_angular_acceleration/angular_acceleration_truediv_type_error + + + @mark.error + def test_raises_zero_division_error(self, angular_acceleration_truediv_zero_division_error): + with raises(ZeroDivisionError): + assert basic_angular_acceleration/angular_acceleration_truediv_zero_division_error + + +@mark.units +class TestAngularAccelerationEq: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, max_value = 1000, min_value = -1000), + unit = sampled_from(elements = units_list)) + @settings(max_examples = 100) + def test_method(self, value, unit): + angular_acceleration_1 = AngularAcceleration(value = value, unit = unit) + angular_acceleration_2 = AngularAcceleration(value = value, unit = unit) + + for target_unit in units_list: + assert angular_acceleration_1 == angular_acceleration_2.to(target_unit) + + + @mark.error + def test_raises_type_error(self, angular_acceleration_eq_type_error): + with raises(TypeError): + assert basic_angular_acceleration == angular_acceleration_eq_type_error + + +@mark.units +class TestAngularAccelerationNe: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, max_value = 1000, min_value = -1000), + unit = sampled_from(elements = units_list), + gap = floats(allow_nan = False, allow_infinity = False, min_value = 1e-10, exclude_min = True)) + @settings(max_examples = 100) + def test_method(self, value, unit, gap): + angular_acceleration_1 = AngularAcceleration(value = value, unit = unit) + angular_acceleration_2 = AngularAcceleration(value = value + gap, unit = unit) + + for target_unit in units_list: + assert angular_acceleration_1 != angular_acceleration_2.to(target_unit) + + + @mark.error + def test_raises_type_error(self, angular_acceleration_ne_type_error): + with raises(TypeError): + assert basic_angular_acceleration != angular_acceleration_ne_type_error + + +@mark.units +class TestAngularAccelerationGt: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, max_value = 1000, min_value = -1000), + unit = sampled_from(elements = units_list), + gap = floats(allow_nan = False, allow_infinity = False, min_value = 1e-10, exclude_min = True)) + @settings(max_examples = 100) + def test_method(self, value, unit, gap): + angular_acceleration_1 = AngularAcceleration(value = value + gap, unit = unit) + angular_acceleration_2 = AngularAcceleration(value = value, unit = unit) + + for target_unit in units_list: + assert angular_acceleration_1 > angular_acceleration_2.to(target_unit) + + + @mark.error + def test_raises_type_error(self, angular_acceleration_gt_type_error): + with raises(TypeError): + assert basic_angular_acceleration > angular_acceleration_gt_type_error + + +@mark.units +class TestAngularAccelerationGe: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, max_value = 1000, min_value = -1000), + unit = sampled_from(elements = units_list), + gap = floats(allow_nan = False, allow_infinity = False, min_value = 0, exclude_min = False)) + @settings(max_examples = 100) + def test_method(self, value, unit, gap): + angular_acceleration_1 = AngularAcceleration(value = value + gap, unit = unit) + angular_acceleration_2 = AngularAcceleration(value = value, unit = unit) + + for target_unit in units_list: + assert angular_acceleration_1 >= angular_acceleration_2.to(target_unit) + + + @mark.error + def test_raises_type_error(self, angular_acceleration_ge_type_error): + with raises(TypeError): + assert basic_angular_acceleration >= angular_acceleration_ge_type_error + + +@mark.units +class TestAngularAccelerationLt: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, max_value = 1000, min_value = -1000), + unit = sampled_from(elements = units_list), + gap = floats(allow_nan = False, allow_infinity = False, min_value = 1e-10, exclude_min = True)) + @settings(max_examples = 100) + def test_method(self, value, unit, gap): + if value != 0: + angular_acceleration_1 = AngularAcceleration(value = value, unit = unit) + angular_acceleration_2 = AngularAcceleration(value = value + gap, unit = unit) + + for target_unit in units_list: + assert angular_acceleration_1 < angular_acceleration_2.to(target_unit) + + + @mark.error + def test_raises_type_error(self, angular_acceleration_lt_type_error): + with raises(TypeError): + assert basic_angular_acceleration < angular_acceleration_lt_type_error + + +@mark.units +class TestAngularAccelerationLe: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, max_value = 1000, min_value = -1000), + unit = sampled_from(elements = units_list), + gap = floats(allow_nan = False, allow_infinity = False, min_value = 0, exclude_min = False)) + @settings(max_examples = 100) + def test_method(self, value, unit, gap): + if value != 0: + angular_acceleration_1 = AngularAcceleration(value = value, unit = unit) + angular_acceleration_2 = AngularAcceleration(value = value + gap, unit = unit) + + for target_unit in units_list: + assert angular_acceleration_1 <= angular_acceleration_2.to(target_unit) + + + @mark.error + def test_raises_type_error(self, angular_acceleration_le_type_error): + with raises(TypeError): + assert basic_angular_acceleration <= angular_acceleration_le_type_error + + +@mark.units +class TestAngularAccelerationTo: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, max_value = 1000, min_value = -1000), + unit = sampled_from(elements = units_list), + inplace = booleans()) + @settings(max_examples = 100) + def test_method(self, value, unit, inplace): + if value != 0: + angular_acceleration = AngularAcceleration(value = value, unit = unit) + + for target_unit in units_list: + converted_acceleration = angular_acceleration.to(target_unit = target_unit, inplace = inplace) + + assert converted_acceleration.unit == target_unit + if target_unit != unit: + assert converted_acceleration.value != value + assert converted_acceleration.unit != unit + + if inplace: + assert converted_acceleration.value == angular_acceleration.value + assert converted_acceleration.unit == angular_acceleration.unit + else: + assert converted_acceleration.value != angular_acceleration.value + assert converted_acceleration.unit != angular_acceleration.unit + else: + assert converted_acceleration == angular_acceleration + + + @mark.error + def test_raises_type_error(self, angular_acceleration_to_type_error): + with raises(TypeError): + basic_angular_acceleration.to(**angular_acceleration_to_type_error) + + + @mark.error + def test_raises_key_error(self): + fake_units = [f'fake {unit}' for unit in units_list] + for fake_unit in fake_units: + with raises(KeyError): + basic_angular_acceleration.to(fake_unit) diff --git a/tests/test_units/test_angular_position/conftest.py b/tests/test_units/test_angular_position/conftest.py new file mode 100644 index 0000000..7756dba --- /dev/null +++ b/tests/test_units/test_angular_position/conftest.py @@ -0,0 +1,103 @@ +from gearpy.units import AngularPosition +from hypothesis.strategies import composite, floats, sampled_from +import numpy as np +from tests.conftest import types_to_check +from pytest import fixture + + +basic_angular_position = AngularPosition(1, 'rad') + + +@composite +def angular_positions(draw): + value = draw(floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000)) + unit = draw(sampled_from(elements = list(AngularPosition._AngularPosition__UNITS.keys()))) + + return AngularPosition(value = value, unit = unit) + + +angular_position_init_type_error_1 = [{'value': type_to_check, 'unit': 'unit'} for type_to_check in types_to_check + if not isinstance(type_to_check, float) and not isinstance(type_to_check, int)] + +angular_position_init_type_error_2 = [{'value': 1, 'unit': types_to_check} for type_to_check in types_to_check + if not isinstance(type_to_check, str)] + +@fixture(params = [*angular_position_init_type_error_1, + *angular_position_init_type_error_2]) +def angular_position_init_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, AngularPosition)]) +def angular_position_add_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, AngularPosition)]) +def angular_position_sub_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check + if not isinstance(type_to_check, float) and not isinstance(type_to_check, int)]) +def angular_position_mul_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, float) + and not isinstance(type_to_check, int) and not isinstance(type_to_check, np.ndarray)]) +def angular_position_rmul_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, float) + and not isinstance(type_to_check, int) and not isinstance(type_to_check, AngularPosition)]) +def angular_position_truediv_type_error(request): + return request.param + + +@fixture(params = [0, 0.0, AngularPosition(0, 'rad')]) +def angular_position_truediv_zero_division_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, AngularPosition)]) +def angular_position_eq_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, AngularPosition)]) +def angular_position_ne_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, AngularPosition)]) +def angular_position_gt_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, AngularPosition)]) +def angular_position_ge_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, AngularPosition)]) +def angular_position_lt_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, AngularPosition)]) +def angular_position_le_type_error(request): + return request.param + + +angular_position_to_type_error_1 = [{'target_unit': type_to_check, 'inplace': True} for type_to_check in types_to_check + if not isinstance(type_to_check, str)] + +angular_position_to_type_error_2 = [{'target_unit': 'target_unit', 'inplace': type_to_check} for type_to_check in types_to_check + if not isinstance(type_to_check, bool) and not isinstance(type_to_check, int)] + +@fixture(params = [*angular_position_to_type_error_1, + *angular_position_to_type_error_2]) +def angular_position_to_type_error(request): + return request.param diff --git a/tests/test_units/test_angular_position/test_angular_position.py b/tests/test_units/test_angular_position/test_angular_position.py new file mode 100644 index 0000000..e24ef44 --- /dev/null +++ b/tests/test_units/test_angular_position/test_angular_position.py @@ -0,0 +1,374 @@ +from gearpy.units import AngularPosition +from hypothesis.strategies import floats, sampled_from, one_of, booleans +from hypothesis import given, settings +from tests.test_units.test_angular_position.conftest import basic_angular_position, angular_positions +from pytest import mark, raises + + +units_list = list(AngularPosition._AngularPosition__UNITS.keys()) + + +@mark.units +class TestAngularPositionInit: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000), + unit = sampled_from(elements = units_list)) + @settings(max_examples = 100) + def test_method(self, value, unit): + angular_position = AngularPosition(value = value, unit = unit) + + assert angular_position.value == value + assert angular_position.unit == unit + + + @mark.error + def test_raises_type_error(self, angular_position_init_type_error): + with raises(TypeError): + AngularPosition(**angular_position_init_type_error) + + + @mark.error + def test_raises_key_error(self): + fake_units = [f'fake {unit}' for unit in units_list] + for fake_unit in fake_units: + with raises(KeyError): + AngularPosition(value = 1, unit = fake_unit) + + +@mark.units +class TestAngularPositionRepr: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000), + unit = sampled_from(elements = units_list)) + @settings(max_examples = 100) + def test_method(self, value, unit): + angular_position = AngularPosition(value = value, unit = unit) + + assert str(angular_position) == f'{value} {unit}' + + + +@mark.units +class TestAngularPositionAdd: + + + @mark.genuine + @given(value_1 = floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000), + unit_1 = sampled_from(elements = units_list), + value_2 = floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000), + unit_2 = sampled_from(elements = units_list)) + @settings(max_examples = 100) + def test_method(self, value_1, unit_1, value_2, unit_2): + angular_position_1 = AngularPosition(value = value_1, unit = unit_1) + angular_position_2 = AngularPosition(value = value_2, unit = unit_2) + result = angular_position_1 + angular_position_2 + + assert isinstance(result, AngularPosition) + assert result.value == angular_position_1.value + angular_position_2.to(unit_1).value + assert result.unit == angular_position_1.unit + + + @mark.error + def test_raises_type_error(self, angular_position_add_type_error): + with raises(TypeError): + assert basic_angular_position + angular_position_add_type_error + + +@mark.units +class TestAngularPositionSub: + + + @mark.genuine + @given(value_1 = floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000), + unit_1 = sampled_from(elements = units_list), + value_2 = floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000), + unit_2 = sampled_from(elements = units_list)) + @settings(max_examples = 100) + def test_method(self, value_1, unit_1, value_2, unit_2): + angular_position_1 = AngularPosition(value = value_1, unit = unit_1) + angular_position_2 = AngularPosition(value = value_2, unit = unit_2) + result = angular_position_1 - angular_position_2 + + assert isinstance(result, AngularPosition) + assert result.value == angular_position_1.value - angular_position_2.to(unit_1).value + assert result.unit == angular_position_1.unit + + + @mark.error + def test_raises_type_error(self, angular_position_sub_type_error): + with raises(TypeError): + assert basic_angular_position - angular_position_sub_type_error + + +@mark.units +class TestAngularPositionMul: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000), + unit = sampled_from(elements = units_list), + multiplier = floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000)) + @settings(max_examples = 100) + def test_method(self, value, unit, multiplier): + angular_position = AngularPosition(value = value, unit = unit) + result = angular_position*multiplier + + assert isinstance(result, AngularPosition) + assert result.value == angular_position.value*multiplier + assert result.unit == angular_position.unit + + + @mark.error + def test_raises_type_error(self, angular_position_mul_type_error): + with raises(TypeError): + assert basic_angular_position*angular_position_mul_type_error + + +@mark.units +class TestAngularPositionRmul: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000), + unit = sampled_from(elements = units_list), + multiplier = floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000)) + @settings(max_examples = 100) + def test_method(self, value, unit, multiplier): + angular_position = AngularPosition(value = value, unit = unit) + result = multiplier*angular_position + + assert isinstance(result, AngularPosition) + assert result.value == angular_position.value*multiplier + assert result.unit == angular_position.unit + + + @mark.error + def test_raises_type_error(self, angular_position_rmul_type_error): + with raises(TypeError): + assert angular_position_rmul_type_error*basic_angular_position + + +@mark.units +class TestAngularPositionTruediv: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000), + unit = sampled_from(elements = units_list), + divider = one_of(floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000), + angular_positions())) + @settings(max_examples = 100) + def test_method(self, value, unit, divider): + angular_position = AngularPosition(value = value, unit = unit) + + if isinstance(divider, AngularPosition): + if abs(divider.value) >= 1e-300: + result = angular_position/divider + assert isinstance(result, float) + assert result == angular_position.value/divider.to(unit).value + else: + if divider != 0: + result = angular_position/divider + assert isinstance(result, AngularPosition) + assert result.value == angular_position.value/divider + assert result.unit == angular_position.unit + + + @mark.error + def test_raises_type_error(self, angular_position_truediv_type_error): + with raises(TypeError): + assert basic_angular_position/angular_position_truediv_type_error + + + @mark.error + def test_raises_zero_division_error(self, angular_position_truediv_zero_division_error): + with raises(ZeroDivisionError): + assert basic_angular_position/angular_position_truediv_zero_division_error + + +@mark.units +class TestAngularPositionEq: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, max_value = 1000, min_value = -1000), + unit = sampled_from(elements = units_list)) + @settings(max_examples = 100) + def test_method(self, value, unit): + angular_position_1 = AngularPosition(value = value, unit = unit) + angular_position_2 = AngularPosition(value = value, unit = unit) + + for target_unit in units_list: + assert angular_position_1 == angular_position_2.to(target_unit) + + + @mark.error + def test_raises_type_error(self, angular_position_eq_type_error): + with raises(TypeError): + assert basic_angular_position == angular_position_eq_type_error + + +@mark.units +class TestAngularPositionNe: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, max_value = 1000, min_value = -1000), + unit = sampled_from(elements = units_list), + gap = floats(allow_nan = False, allow_infinity = False, min_value = 1e-10, exclude_min = True)) + @settings(max_examples = 100) + def test_method(self, value, unit, gap): + angular_position_1 = AngularPosition(value = value, unit = unit) + angular_position_2 = AngularPosition(value = value + gap, unit = unit) + + for target_unit in units_list: + assert angular_position_1 != angular_position_2.to(target_unit) + + + @mark.error + def test_raises_type_error(self, angular_position_ne_type_error): + with raises(TypeError): + assert basic_angular_position != angular_position_ne_type_error + + +@mark.units +class TestAngularPositionGt: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, max_value = 1000, min_value = -1000), + unit = sampled_from(elements = units_list), + gap = floats(allow_nan = False, allow_infinity = False, min_value = 1e-10, exclude_min = True)) + @settings(max_examples = 100) + def test_method(self, value, unit, gap): + angular_position_1 = AngularPosition(value = value + gap, unit = unit) + angular_position_2 = AngularPosition(value = value, unit = unit) + + for target_unit in units_list: + assert angular_position_1 > angular_position_2.to(target_unit) + + + @mark.error + def test_raises_type_error(self, angular_position_gt_type_error): + with raises(TypeError): + assert basic_angular_position > angular_position_gt_type_error + + +@mark.units +class TestAngularPositionGe: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, max_value = 1000, min_value = -1000), + unit = sampled_from(elements = units_list), + gap = floats(allow_nan = False, allow_infinity = False, min_value = 0, exclude_min = False)) + @settings(max_examples = 100) + def test_method(self, value, unit, gap): + angular_position_1 = AngularPosition(value = value + gap, unit = unit) + angular_position_2 = AngularPosition(value = value, unit = unit) + + for target_unit in units_list: + assert angular_position_1 >= angular_position_2.to(target_unit) + + + @mark.error + def test_raises_type_error(self, angular_position_ge_type_error): + with raises(TypeError): + assert basic_angular_position >= angular_position_ge_type_error + + +@mark.units +class TestAngularPositionLt: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, max_value = 1000, min_value = -1000), + unit = sampled_from(elements = units_list), + gap = floats(allow_nan = False, allow_infinity = False, min_value = 1e-10, exclude_min = True)) + @settings(max_examples = 100) + def test_method(self, value, unit, gap): + if value != 0: + angular_position_1 = AngularPosition(value = value, unit = unit) + angular_position_2 = AngularPosition(value = value + gap, unit = unit) + + for target_unit in units_list: + assert angular_position_1 < angular_position_2.to(target_unit) + + + @mark.error + def test_raises_type_error(self, angular_position_lt_type_error): + with raises(TypeError): + assert basic_angular_position < angular_position_lt_type_error + + +@mark.units +class TestAngularPositionLe: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, max_value = 1000, min_value = -1000), + unit = sampled_from(elements = units_list), + gap = floats(allow_nan = False, allow_infinity = False, min_value = 0, exclude_min = False)) + @settings(max_examples = 100) + def test_method(self, value, unit, gap): + if value != 0: + angular_position_1 = AngularPosition(value = value, unit = unit) + angular_position_2 = AngularPosition(value = value + gap, unit = unit) + + for target_unit in units_list: + assert angular_position_1 <= angular_position_2.to(target_unit) + + + @mark.error + def test_raises_type_error(self, angular_position_le_type_error): + with raises(TypeError): + assert basic_angular_position <= angular_position_le_type_error + + +@mark.units +class TestAngularPositionTo: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, max_value = 1000, min_value = -1000), + unit = sampled_from(elements = units_list), + inplace = booleans()) + @settings(max_examples = 100) + def test_method(self, value, unit, inplace): + if value != 0: + angular_position = AngularPosition(value = value, unit = unit) + + for target_unit in units_list: + converted_position = angular_position.to(target_unit = target_unit, inplace = inplace) + + assert converted_position.unit == target_unit + if target_unit != unit: + assert converted_position.value != value + assert converted_position.unit != unit + + if inplace: + assert converted_position.value == angular_position.value + assert converted_position.unit == angular_position.unit + else: + assert converted_position.value != angular_position.value + assert converted_position.unit != angular_position.unit + else: + assert converted_position == angular_position + + + @mark.error + def test_raises_type_error(self, angular_position_to_type_error): + with raises(TypeError): + basic_angular_position.to(**angular_position_to_type_error) + + + @mark.error + def test_raises_key_error(self): + fake_units = [f'fake {unit}' for unit in units_list] + for fake_unit in fake_units: + with raises(KeyError): + basic_angular_position.to(fake_unit) diff --git a/tests/test_units/test_angular_speed/conftest.py b/tests/test_units/test_angular_speed/conftest.py new file mode 100644 index 0000000..19c9b75 --- /dev/null +++ b/tests/test_units/test_angular_speed/conftest.py @@ -0,0 +1,112 @@ +from gearpy.units import AngularSpeed, Time +from hypothesis.strategies import composite, floats, sampled_from +import numpy as np +from tests.conftest import types_to_check +from pytest import fixture + + +basic_angular_speed = AngularSpeed(1, 'rad/s') + + +@composite +def angular_speeds(draw): + value = draw(floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000)) + unit = draw(sampled_from(elements = list(AngularSpeed._AngularSpeed__UNITS.keys()))) + + return AngularSpeed(value = value, unit = unit) + + +@composite +def times(draw): + value = draw(floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000)) + unit = draw(sampled_from(elements = list(Time._Time__UNITS.keys()))) + + return Time(value = value, unit = unit) + + +angular_speed_init_type_error_1 = [{'value': type_to_check, 'unit': 'unit'} for type_to_check in types_to_check + if not isinstance(type_to_check, float) and not isinstance(type_to_check, int)] + +angular_speed_init_type_error_2 = [{'value': 1, 'unit': types_to_check} for type_to_check in types_to_check + if not isinstance(type_to_check, str)] + +@fixture(params = [*angular_speed_init_type_error_1, + *angular_speed_init_type_error_2]) +def angular_speed_init_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, AngularSpeed)]) +def angular_speed_add_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, AngularSpeed)]) +def angular_speed_sub_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, float) + and not isinstance(type_to_check, int) and not isinstance(type_to_check, Time)]) +def angular_speed_mul_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, float) + and not isinstance(type_to_check, int) and not isinstance(type_to_check, np.ndarray) + and not isinstance(type_to_check, Time)]) +def angular_speed_rmul_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, float) + and not isinstance(type_to_check, int) and not isinstance(type_to_check, AngularSpeed)]) +def angular_speed_truediv_type_error(request): + return request.param + + +@fixture(params = [0, 0.0, AngularSpeed(0, 'rad/s')]) +def angular_speed_truediv_zero_division_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, AngularSpeed)]) +def angular_speed_eq_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, AngularSpeed)]) +def angular_speed_ne_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, AngularSpeed)]) +def angular_speed_gt_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, AngularSpeed)]) +def angular_speed_ge_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, AngularSpeed)]) +def angular_speed_lt_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, AngularSpeed)]) +def angular_speed_le_type_error(request): + return request.param + + +angular_speed_to_type_error_1 = [{'target_unit': type_to_check, 'inplace': True} for type_to_check in types_to_check + if not isinstance(type_to_check, str)] + +angular_speed_to_type_error_2 = [{'target_unit': 'target_unit', 'inplace': type_to_check} for type_to_check in types_to_check + if not isinstance(type_to_check, bool) and not isinstance(type_to_check, int)] + +@fixture(params = [*angular_speed_to_type_error_1, + *angular_speed_to_type_error_2]) +def angular_speed_to_type_error(request): + return request.param diff --git a/tests/test_units/test_angular_speed/test_angular_speed.py b/tests/test_units/test_angular_speed/test_angular_speed.py new file mode 100644 index 0000000..eba12f3 --- /dev/null +++ b/tests/test_units/test_angular_speed/test_angular_speed.py @@ -0,0 +1,405 @@ +from gearpy.units import AngularPosition, AngularSpeed, Time +from hypothesis.strategies import floats, sampled_from, one_of, booleans +from hypothesis import given, settings +from tests.test_units.test_angular_speed.conftest import basic_angular_speed, angular_speeds, times +from pytest import mark, raises + + +units_list = list(AngularSpeed._AngularSpeed__UNITS.keys()) + + +@mark.units +class TestAngularSpeedInit: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000), + unit = sampled_from(elements = units_list)) + @settings(max_examples = 100) + def test_method(self, value, unit): + angular_speed = AngularSpeed(value = value, unit = unit) + + assert angular_speed.value == value + assert angular_speed.unit == unit + + + @mark.error + def test_raises_type_error(self, angular_speed_init_type_error): + with raises(TypeError): + AngularSpeed(**angular_speed_init_type_error) + + + @mark.error + def test_raises_key_error(self): + fake_units = [f'fake {unit}' for unit in units_list] + for fake_unit in fake_units: + with raises(KeyError): + AngularSpeed(value = 1, unit = fake_unit) + + +@mark.units +class TestAngularSpeedRepr: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000), + unit = sampled_from(elements = units_list)) + @settings(max_examples = 100) + def test_method(self, value, unit): + angular_speed = AngularSpeed(value = value, unit = unit) + + assert str(angular_speed) == f'{value} {unit}' + + + +@mark.units +class TestAngularSpeedAdd: + + + @mark.genuine + @given(value_1 = floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000), + unit_1 = sampled_from(elements = units_list), + value_2 = floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000), + unit_2 = sampled_from(elements = units_list)) + @settings(max_examples = 100) + def test_method(self, value_1, unit_1, value_2, unit_2): + angular_speed_1 = AngularSpeed(value = value_1, unit = unit_1) + angular_speed_2 = AngularSpeed(value = value_2, unit = unit_2) + result = angular_speed_1 + angular_speed_2 + + assert isinstance(result, AngularSpeed) + assert result.value == angular_speed_1.value + angular_speed_2.to(unit_1).value + assert result.unit == angular_speed_1.unit + + + @mark.error + def test_raises_type_error(self, angular_speed_add_type_error): + with raises(TypeError): + assert basic_angular_speed + angular_speed_add_type_error + + +@mark.units +class TestAngularSpeedSub: + + + @mark.genuine + @given(value_1 = floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000), + unit_1 = sampled_from(elements = units_list), + value_2 = floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000), + unit_2 = sampled_from(elements = units_list)) + @settings(max_examples = 100) + def test_method(self, value_1, unit_1, value_2, unit_2): + angular_speed_1 = AngularSpeed(value = value_1, unit = unit_1) + angular_speed_2 = AngularSpeed(value = value_2, unit = unit_2) + result = angular_speed_1 - angular_speed_2 + + assert isinstance(result, AngularSpeed) + assert result.value == angular_speed_1.value - angular_speed_2.to(unit_1).value + assert result.unit == angular_speed_1.unit + + + @mark.error + def test_raises_type_error(self, angular_speed_sub_type_error): + with raises(TypeError): + assert basic_angular_speed - angular_speed_sub_type_error + + +@mark.units +class TestAngularSpeedMul: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000), + unit = sampled_from(elements = units_list), + multiplier = one_of(floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000), + times())) + @settings(max_examples = 100) + def test_method(self, value, unit, multiplier): + angular_speed = AngularSpeed(value = value, unit = unit) + result = angular_speed*multiplier + + if isinstance(multiplier, Time): + assert isinstance(result, AngularPosition) + assert result.value == angular_speed.to('rad/s').value*multiplier.to('sec').value + assert result.unit == 'rad' + else: + assert isinstance(result, AngularSpeed) + assert result.value == angular_speed.value*multiplier + assert result.unit == angular_speed.unit + + + @mark.error + def test_raises_type_error(self, angular_speed_mul_type_error): + with raises(TypeError): + assert basic_angular_speed*angular_speed_mul_type_error + + +@mark.units +class TestAngularSpeedRmul: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000), + unit = sampled_from(elements = units_list), + multiplier = one_of(floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000), + times())) + @settings(max_examples = 100) + def test_method(self, value, unit, multiplier): + angular_speed = AngularSpeed(value = value, unit = unit) + result = multiplier*angular_speed + + if isinstance(multiplier, Time): + assert isinstance(result, AngularPosition) + assert result.value == angular_speed.to('rad/s').value*multiplier.to('sec').value + assert result.unit == 'rad' + else: + assert isinstance(result, AngularSpeed) + assert result.value == angular_speed.value*multiplier + assert result.unit == angular_speed.unit + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000), + unit = sampled_from(elements = units_list)) + @settings(max_examples = 100) + def test_method_patch(self, value, unit): + angular_speed = AngularSpeed(value = value, unit = unit) + + class FakeTime(Time): + + def __mul__(self, other: AngularSpeed): return NotImplemented + + fake_multiplier = FakeTime(1, 'sec') + result = fake_multiplier*angular_speed + + assert isinstance(result, AngularPosition) + assert result.value == angular_speed.to('rad/s').value*fake_multiplier.to('sec').value + assert result.unit == 'rad' + + + @mark.error + def test_raises_type_error(self, angular_speed_rmul_type_error): + with raises(TypeError): + assert angular_speed_rmul_type_error*basic_angular_speed + + +@mark.units +class TestAngularSpeedTruediv: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000), + unit = sampled_from(elements = units_list), + divider = one_of(floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000), + angular_speeds())) + @settings(max_examples = 100) + def test_method(self, value, unit, divider): + angular_speed = AngularSpeed(value = value, unit = unit) + + if isinstance(divider, AngularSpeed): + if abs(divider.value) >= 1e-300: + result = angular_speed/divider + assert isinstance(result, float) + assert result == angular_speed.value/divider.to(unit).value + else: + if divider != 0: + result = angular_speed/divider + assert isinstance(result, AngularSpeed) + assert result.value == angular_speed.value/divider + assert result.unit == angular_speed.unit + + + @mark.error + def test_raises_type_error(self, angular_speed_truediv_type_error): + with raises(TypeError): + assert basic_angular_speed/angular_speed_truediv_type_error + + + @mark.error + def test_raises_zero_division_error(self, angular_speed_truediv_zero_division_error): + with raises(ZeroDivisionError): + assert basic_angular_speed/angular_speed_truediv_zero_division_error + + +@mark.units +class TestAngularSpeedEq: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, max_value = 1000, min_value = -1000), + unit = sampled_from(elements = units_list)) + @settings(max_examples = 100) + def test_method(self, value, unit): + angular_speed_1 = AngularSpeed(value = value, unit = unit) + angular_speed_2 = AngularSpeed(value = value, unit = unit) + + for target_unit in units_list: + assert angular_speed_1 == angular_speed_2.to(target_unit) + + + @mark.error + def test_raises_type_error(self, angular_speed_eq_type_error): + with raises(TypeError): + assert basic_angular_speed == angular_speed_eq_type_error + + +@mark.units +class TestAngularSpeedNe: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, max_value = 1000, min_value = -1000), + unit = sampled_from(elements = units_list), + gap = floats(allow_nan = False, allow_infinity = False, min_value = 1e-10, exclude_min = True)) + @settings(max_examples = 100) + def test_method(self, value, unit, gap): + angular_speed_1 = AngularSpeed(value = value, unit = unit) + angular_speed_2 = AngularSpeed(value = value + gap, unit = unit) + + for target_unit in units_list: + assert angular_speed_1 != angular_speed_2.to(target_unit) + + + @mark.error + def test_raises_type_error(self, angular_speed_ne_type_error): + with raises(TypeError): + assert basic_angular_speed != angular_speed_ne_type_error + + +@mark.units +class TestAngularSpeedGt: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, max_value = 1000, min_value = -1000), + unit = sampled_from(elements = units_list), + gap = floats(allow_nan = False, allow_infinity = False, min_value = 1e-10, exclude_min = True)) + @settings(max_examples = 100) + def test_method(self, value, unit, gap): + angular_speed_1 = AngularSpeed(value = value + gap, unit = unit) + angular_speed_2 = AngularSpeed(value = value, unit = unit) + + for target_unit in units_list: + assert angular_speed_1 > angular_speed_2.to(target_unit) + + + @mark.error + def test_raises_type_error(self, angular_speed_gt_type_error): + with raises(TypeError): + assert basic_angular_speed > angular_speed_gt_type_error + + +@mark.units +class TestAngularSpeedGe: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, max_value = 1000, min_value = -1000), + unit = sampled_from(elements = units_list), + gap = floats(allow_nan = False, allow_infinity = False, min_value = 0, exclude_min = False)) + @settings(max_examples = 100) + def test_method(self, value, unit, gap): + angular_speed_1 = AngularSpeed(value = value + gap, unit = unit) + angular_speed_2 = AngularSpeed(value = value, unit = unit) + + for target_unit in units_list: + assert angular_speed_1 >= angular_speed_2.to(target_unit) + + + @mark.error + def test_raises_type_error(self, angular_speed_ge_type_error): + with raises(TypeError): + assert basic_angular_speed >= angular_speed_ge_type_error + + +@mark.units +class TestAngularSpeedLt: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, max_value = 1000, min_value = -1000), + unit = sampled_from(elements = units_list), + gap = floats(allow_nan = False, allow_infinity = False, min_value = 1e-10, exclude_min = True)) + @settings(max_examples = 100) + def test_method(self, value, unit, gap): + if value != 0: + angular_speed_1 = AngularSpeed(value = value, unit = unit) + angular_speed_2 = AngularSpeed(value = value + gap, unit = unit) + + for target_unit in units_list: + assert angular_speed_1 < angular_speed_2.to(target_unit) + + + @mark.error + def test_raises_type_error(self, angular_speed_lt_type_error): + with raises(TypeError): + assert basic_angular_speed < angular_speed_lt_type_error + + +@mark.units +class TestAngularSpeedLe: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, max_value = 1000, min_value = -1000), + unit = sampled_from(elements = units_list), + gap = floats(allow_nan = False, allow_infinity = False, min_value = 0, exclude_min = False)) + @settings(max_examples = 100) + def test_method(self, value, unit, gap): + if value != 0: + angular_speed_1 = AngularSpeed(value = value, unit = unit) + angular_speed_2 = AngularSpeed(value = value + gap, unit = unit) + + for target_unit in units_list: + assert angular_speed_1 <= angular_speed_2.to(target_unit) + + + @mark.error + def test_raises_type_error(self, angular_speed_le_type_error): + with raises(TypeError): + assert basic_angular_speed <= angular_speed_le_type_error + + +@mark.units +class TestAngularSpeedTo: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, max_value = 1000, min_value = -1000), + unit = sampled_from(elements = units_list), + inplace = booleans()) + @settings(max_examples = 100) + def test_method(self, value, unit, inplace): + if value != 0: + angular_speed = AngularSpeed(value = value, unit = unit) + + for target_unit in units_list: + converted_speed = angular_speed.to(target_unit = target_unit, inplace = inplace) + + assert converted_speed.unit == target_unit + if target_unit != unit: + assert converted_speed.value != value + assert converted_speed.unit != unit + + if inplace: + assert converted_speed.value == angular_speed.value + assert converted_speed.unit == angular_speed.unit + else: + assert converted_speed.value != angular_speed.value + assert converted_speed.unit != angular_speed.unit + else: + assert converted_speed == angular_speed + + + @mark.error + def test_raises_type_error(self, angular_speed_to_type_error): + with raises(TypeError): + basic_angular_speed.to(**angular_speed_to_type_error) + + + @mark.error + def test_raises_key_error(self): + fake_units = [f'fake {unit}' for unit in units_list] + for fake_unit in fake_units: + with raises(KeyError): + basic_angular_speed.to(fake_unit) diff --git a/tests/test_units/test_inertia_moment/conftest.py b/tests/test_units/test_inertia_moment/conftest.py new file mode 100644 index 0000000..9771836 --- /dev/null +++ b/tests/test_units/test_inertia_moment/conftest.py @@ -0,0 +1,104 @@ +from gearpy.units import InertiaMoment +from hypothesis.strategies import composite, floats, sampled_from +import numpy as np +from tests.conftest import types_to_check +from pytest import fixture + + +basic_inertia_moment = InertiaMoment(1, 'kgm^2') + + +@composite +def inertia_moments(draw): + value = draw(floats(allow_nan = False, allow_infinity = False, min_value = 0, exclude_min = True, max_value = 1000)) + unit = draw(sampled_from(elements = list(InertiaMoment._InertiaMoment__UNITS.keys()))) + + return InertiaMoment(value = value, unit = unit) + + +inertia_moment_init_type_error_1 = [{'value': type_to_check, 'unit': 'unit'} for type_to_check in types_to_check + if not isinstance(type_to_check, float) and not isinstance(type_to_check, int)] + +inertia_moment_init_type_error_2 = [{'value': 1, 'unit': types_to_check} for type_to_check in types_to_check + if not isinstance(type_to_check, str)] + +@fixture(params = [*inertia_moment_init_type_error_1, + *inertia_moment_init_type_error_2]) +def inertia_moment_init_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, InertiaMoment)]) +def inertia_moment_add_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, InertiaMoment)]) +def inertia_moment_sub_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check + if not isinstance(type_to_check, float) and not isinstance(type_to_check, int)]) +def inertia_moment_mul_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, float) + and not isinstance(type_to_check, int) and not isinstance(type_to_check, np.ndarray)]) +def inertia_moment_rmul_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, float) + and not isinstance(type_to_check, int) and not isinstance(type_to_check, InertiaMoment)]) +def inertia_moment_truediv_type_error(request): + return request.param + + +@fixture(params = [0, 0.0]) +def inertia_moment_truediv_zero_division_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, InertiaMoment)]) +def inertia_moment_eq_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, InertiaMoment)]) +def inertia_moment_ne_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, InertiaMoment)]) +def inertia_moment_gt_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, InertiaMoment)]) +def inertia_moment_ge_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, InertiaMoment)]) +def inertia_moment_lt_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, InertiaMoment)]) +def inertia_moment_le_type_error(request): + return request.param + + +inertia_moment_to_type_error_1 = [{'target_unit': type_to_check, 'inplace': True} + for type_to_check in types_to_check if not isinstance(type_to_check, str)] + +inertia_moment_to_type_error_2 = [{'target_unit': 'target_unit', 'inplace': type_to_check} + for type_to_check in types_to_check if not isinstance(type_to_check, bool) + and not isinstance(type_to_check, int)] + +@fixture(params = [*inertia_moment_to_type_error_1, + *inertia_moment_to_type_error_2]) +def inertia_moment_to_type_error(request): + return request.param \ No newline at end of file diff --git a/tests/test_units/test_inertia_moment/test_inertia_moment.py b/tests/test_units/test_inertia_moment/test_inertia_moment.py new file mode 100644 index 0000000..d94f183 --- /dev/null +++ b/tests/test_units/test_inertia_moment/test_inertia_moment.py @@ -0,0 +1,399 @@ +from gearpy.units import InertiaMoment +from hypothesis.strategies import floats, sampled_from, one_of, booleans +from hypothesis import given, settings +from tests.test_units.test_inertia_moment.conftest import basic_inertia_moment, inertia_moments +from pytest import mark, raises + + +units_list = list(InertiaMoment._InertiaMoment__UNITS.keys()) + + +@mark.units +class TestInertiaMomentInit: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, min_value = 0, exclude_min = True, max_value = 1000), + unit = sampled_from(elements = units_list)) + @settings(max_examples = 100) + def test_method(self, value, unit): + inertia_moment = InertiaMoment(value = value, unit = unit) + + assert inertia_moment.value == value + assert inertia_moment.unit == unit + + + @mark.error + def test_raises_type_error(self, inertia_moment_init_type_error): + with raises(TypeError): + InertiaMoment(**inertia_moment_init_type_error) + + + @mark.error + def test_raises_key_error(self): + fake_units = [f'fake {unit}' for unit in units_list] + for fake_unit in fake_units: + with raises(KeyError): + InertiaMoment(value = 1, unit = fake_unit) + + + @mark.error + def test_raises_value_error(self): + with raises(ValueError): + InertiaMoment(value = -1, unit = 'kgm^2') + + +@mark.units +class TestInertiaMomentRepr: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, min_value = 0, exclude_min = True, max_value = 1000), + unit = sampled_from(elements = units_list)) + @settings(max_examples = 100) + def test_method(self, value, unit): + inertia_moment = InertiaMoment(value = value, unit = unit) + + assert str(inertia_moment) == f'{value} {unit}' + + + +@mark.units +class TestInertiaMomentAdd: + + + @mark.genuine + @given(value_1 = floats(allow_nan = False, allow_infinity = False, min_value = 1e-10, exclude_min = True, max_value = 1000), + unit_1 = sampled_from(elements = units_list), + value_2 = floats(allow_nan = False, allow_infinity = False, min_value = 1e-10, exclude_min = True, max_value = 1000), + unit_2 = sampled_from(elements = units_list)) + @settings(max_examples = 100) + def test_method(self, value_1, unit_1, value_2, unit_2): + inertia_moment_1 = InertiaMoment(value = value_1, unit = unit_1) + inertia_moment_2 = InertiaMoment(value = value_2, unit = unit_2) + result = inertia_moment_1 + inertia_moment_2 + + assert isinstance(result, InertiaMoment) + assert result.value == inertia_moment_1.value + inertia_moment_2.to(unit_1).value + assert result.unit == inertia_moment_1.unit + + + @mark.error + def test_raises_type_error(self, inertia_moment_add_type_error): + with raises(TypeError): + assert basic_inertia_moment + inertia_moment_add_type_error + + +@mark.units +class TestInertiaMomentSub: + + + @mark.genuine + @given(value_1 = floats(allow_nan = False, allow_infinity = False, min_value = 1e-10, exclude_min = True, max_value = 1000), + unit_1 = sampled_from(elements = units_list), + value_2 = floats(allow_nan = False, allow_infinity = False, min_value = 1e-10, exclude_min = True, max_value = 1000), + unit_2 = sampled_from(elements = units_list)) + @settings(max_examples = 100) + def test_method(self, value_1, unit_1, value_2, unit_2): + inertia_moment_1 = InertiaMoment(value = value_1, unit = unit_1) + inertia_moment_2 = InertiaMoment(value = value_2, unit = unit_2) + if inertia_moment_1 > inertia_moment_2: + result = inertia_moment_1 - inertia_moment_2 + + assert isinstance(result, InertiaMoment) + assert result.value == inertia_moment_1.value - inertia_moment_2.to(unit_1).value + assert result.unit == inertia_moment_1.unit + + + @mark.error + def test_raises_type_error(self, inertia_moment_sub_type_error): + with raises(TypeError): + assert basic_inertia_moment - inertia_moment_sub_type_error + + + @mark.error + def test_raises_value_error(self): + with raises(ValueError): + assert basic_inertia_moment - InertiaMoment(basic_inertia_moment.value + 1, basic_inertia_moment.unit) + + +@mark.units +class TestInertiaMomentMul: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, min_value = 1e-10, exclude_min = True, max_value = 1000), + unit = sampled_from(elements = units_list), + multiplier = floats(allow_nan = False, allow_infinity = False, min_value = 1e-10, exclude_min = True, max_value = 1000)) + @settings(max_examples = 100) + def test_method(self, value, unit, multiplier): + inertia_moment = InertiaMoment(value = value, unit = unit) + result = inertia_moment*multiplier + + assert isinstance(result, InertiaMoment) + assert result.value == inertia_moment.value*multiplier + assert result.unit == inertia_moment.unit + + + @mark.error + def test_raises_type_error(self, inertia_moment_mul_type_error): + with raises(TypeError): + assert basic_inertia_moment*inertia_moment_mul_type_error + + + @mark.error + def test_raises_value_error(self): + with raises(ValueError): + assert basic_inertia_moment*(-1) + + +@mark.units +class TestInertiaMomentRmul: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, min_value = 1e-10, exclude_min = True, max_value = 1000), + unit = sampled_from(elements = units_list), + multiplier = floats(allow_nan = False, allow_infinity = False, min_value = 1e-10, exclude_min = True, max_value = 1000)) + @settings(max_examples = 100) + def test_method(self, value, unit, multiplier): + inertia_moment = InertiaMoment(value = value, unit = unit) + result = multiplier*inertia_moment + + assert isinstance(result, InertiaMoment) + assert result.value == inertia_moment.value*multiplier + assert result.unit == inertia_moment.unit + + + @mark.error + def test_raises_type_error(self, inertia_moment_rmul_type_error): + with raises(TypeError): + assert inertia_moment_rmul_type_error*basic_inertia_moment + + + @mark.error + def test_raises_value_error(self): + with raises(ValueError): + assert -1*basic_inertia_moment + + +@mark.units +class TestInertiaMomentTruediv: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, min_value = 1e-10, exclude_min = True, max_value = 1000), + unit = sampled_from(elements = units_list), + divider = one_of(floats(allow_nan = False, allow_infinity = False, min_value = 1e-10, exclude_min = True, max_value = 1000), + inertia_moments())) + @settings(max_examples = 100) + def test_method(self, value, unit, divider): + inertia_moment = InertiaMoment(value = value, unit = unit) + + if isinstance(divider, InertiaMoment): + if abs(divider.value) >= 1e-300: + result = inertia_moment/divider + assert isinstance(result, float) + assert result == inertia_moment.value/divider.to(unit).value + else: + if divider != 0: + result = inertia_moment/divider + assert isinstance(result, InertiaMoment) + assert result.value == inertia_moment.value/divider + assert result.unit == inertia_moment.unit + + + @mark.error + def test_raises_type_error(self, inertia_moment_truediv_type_error): + with raises(TypeError): + assert basic_inertia_moment/inertia_moment_truediv_type_error + + + @mark.error + def test_raises_zero_division_error(self, inertia_moment_truediv_zero_division_error): + with raises(ZeroDivisionError): + assert basic_inertia_moment/inertia_moment_truediv_zero_division_error + + +@mark.units +class TestInertiaMomentEq: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, min_value = 1e-10, exclude_min = True, max_value = 1000), + unit = sampled_from(elements = units_list)) + @settings(max_examples = 100) + def test_method(self, value, unit): + inertia_moment_1 = InertiaMoment(value = value, unit = unit) + inertia_moment_2 = InertiaMoment(value = value, unit = unit) + + for target_unit in units_list: + assert inertia_moment_1 == inertia_moment_2.to(target_unit) + + + @mark.error + def test_raises_type_error(self, inertia_moment_eq_type_error): + with raises(TypeError): + assert basic_inertia_moment == inertia_moment_eq_type_error + + +@mark.units +class TestInertiaMomentNe: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, min_value = 1e-10, exclude_min = True, max_value = 1000), + unit = sampled_from(elements = units_list), + gap = floats(allow_nan = False, allow_infinity = False, min_value = 1e-10, exclude_min = True, max_value = 1000)) + @settings(max_examples = 100) + def test_method(self, value, unit, gap): + inertia_moment_1 = InertiaMoment(value = value, unit = unit) + inertia_moment_2 = InertiaMoment(value = value + gap, unit = unit) + + for target_unit in units_list: + assert inertia_moment_1 != inertia_moment_2.to(target_unit) + + + @mark.error + def test_raises_type_error(self, inertia_moment_ne_type_error): + with raises(TypeError): + assert basic_inertia_moment != inertia_moment_ne_type_error + + +@mark.units +class TestInertiaMomentGt: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, min_value = 1e-10, exclude_min = True, max_value = 1000), + unit = sampled_from(elements = units_list), + gap = floats(allow_nan = False, allow_infinity = False, min_value = 1e-10, exclude_min = True, max_value = 1000)) + @settings(max_examples = 100) + def test_method(self, value, unit, gap): + inertia_moment_1 = InertiaMoment(value = value + gap, unit = unit) + inertia_moment_2 = InertiaMoment(value = value, unit = unit) + + for target_unit in units_list: + assert inertia_moment_1 > inertia_moment_2.to(target_unit) + + + @mark.error + def test_raises_type_error(self, inertia_moment_gt_type_error): + with raises(TypeError): + assert basic_inertia_moment > inertia_moment_gt_type_error + + +@mark.units +class TestInertiaMomentGe: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, min_value = 1e-10, exclude_min = True, max_value = 1000), + unit = sampled_from(elements = units_list), + gap = floats(allow_nan = False, allow_infinity = False, min_value = 0, exclude_min = False, max_value = 1000)) + @settings(max_examples = 100) + def test_method(self, value, unit, gap): + inertia_moment_1 = InertiaMoment(value = value + gap, unit = unit) + inertia_moment_2 = InertiaMoment(value = value, unit = unit) + + for target_unit in units_list: + assert inertia_moment_1 >= inertia_moment_2.to(target_unit) + + + @mark.error + def test_raises_type_error(self, inertia_moment_ge_type_error): + with raises(TypeError): + assert basic_inertia_moment >= inertia_moment_ge_type_error + + +@mark.units +class TestInertiaMomentLt: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, min_value = 1e-10, exclude_min = True, max_value = 1000), + unit = sampled_from(elements = units_list), + gap = floats(allow_nan = False, allow_infinity = False, min_value = 1e-10, exclude_min = True, max_value = 1000)) + @settings(max_examples = 100) + def test_method(self, value, unit, gap): + if value != 0: + inertia_moment_1 = InertiaMoment(value = value, unit = unit) + inertia_moment_2 = InertiaMoment(value = value + gap, unit = unit) + + for target_unit in units_list: + assert inertia_moment_1 < inertia_moment_2.to(target_unit) + + + @mark.error + def test_raises_type_error(self, inertia_moment_lt_type_error): + with raises(TypeError): + assert basic_inertia_moment < inertia_moment_lt_type_error + + +@mark.units +class TestInertiaMomentLe: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, min_value = 1e-10, exclude_min = True, max_value = 1000), + unit = sampled_from(elements = units_list), + gap = floats(allow_nan = False, allow_infinity = False, min_value = 0, exclude_min = False, max_value = 1000)) + @settings(max_examples = 100) + def test_method(self, value, unit, gap): + if value != 0: + inertia_moment_1 = InertiaMoment(value = value, unit = unit) + inertia_moment_2 = InertiaMoment(value = value + gap, unit = unit) + + for target_unit in units_list: + assert inertia_moment_1 <= inertia_moment_2.to(target_unit) + + + @mark.error + def test_raises_type_error(self, inertia_moment_le_type_error): + with raises(TypeError): + assert basic_inertia_moment <= inertia_moment_le_type_error + + +@mark.units +class TestInertiaMomentTo: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, min_value = 1e-10, exclude_min = True, max_value = 1000), + unit = sampled_from(elements = units_list), + inplace = booleans()) + @settings(max_examples = 100) + def test_method(self, value, unit, inplace): + if value != 0: + inertia_moment = InertiaMoment(value = value, unit = unit) + + for target_unit in units_list: + converted_inertia = inertia_moment.to(target_unit = target_unit, inplace = inplace) + + assert converted_inertia.unit == target_unit + if target_unit != unit: + assert converted_inertia.value != value + assert converted_inertia.unit != unit + + if inplace: + assert converted_inertia.value == inertia_moment.value + assert converted_inertia.unit == inertia_moment.unit + else: + assert converted_inertia.value != inertia_moment.value + assert converted_inertia.unit != inertia_moment.unit + else: + assert converted_inertia == inertia_moment + + + @mark.error + def test_raises_type_error(self, inertia_moment_to_type_error): + with raises(TypeError): + basic_inertia_moment.to(**inertia_moment_to_type_error) + + + @mark.error + def test_raises_key_error(self): + fake_units = [f'fake {unit}' for unit in units_list] + for fake_unit in fake_units: + with raises(KeyError): + basic_inertia_moment.to(fake_unit) diff --git a/tests/test_units/test_time/conftest.py b/tests/test_units/test_time/conftest.py new file mode 100644 index 0000000..a34af47 --- /dev/null +++ b/tests/test_units/test_time/conftest.py @@ -0,0 +1,105 @@ +from gearpy.units import AngularAcceleration, AngularSpeed, Time +from hypothesis.strategies import composite, floats, sampled_from +import numpy as np +from tests.conftest import types_to_check +from pytest import fixture + + +basic_time = Time(1, 'sec') + + +@composite +def times(draw): + value = draw(floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000)) + unit = draw(sampled_from(elements = list(Time._Time__UNITS.keys()))) + + return Time(value = value, unit = unit) + + +time_init_type_error_1 = [{'value': type_to_check, 'unit': 'unit'} for type_to_check in types_to_check + if not isinstance(type_to_check, float) and not isinstance(type_to_check, int)] + +time_init_type_error_2 = [{'value': 1, 'unit': types_to_check} for type_to_check in types_to_check + if not isinstance(type_to_check, str)] + +@fixture(params = [*time_init_type_error_1, + *time_init_type_error_2]) +def time_init_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, Time)]) +def time_add_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, Time)]) +def time_sub_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check + if not isinstance(type_to_check, float) and not isinstance(type_to_check, int) + and not isinstance(type_to_check, AngularSpeed) and not isinstance(type_to_check, AngularAcceleration)]) +def time_mul_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, float) + and not isinstance(type_to_check, int) and not isinstance(type_to_check, np.ndarray) + and not isinstance(type_to_check, AngularSpeed) and not isinstance(type_to_check, AngularAcceleration)]) +def time_rmul_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, float) + and not isinstance(type_to_check, int) and not isinstance(type_to_check, Time)]) +def time_truediv_type_error(request): + return request.param + + +@fixture(params = [0, 0.0, Time(0, 'sec')]) +def time_truediv_zero_division_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, Time)]) +def time_eq_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, Time)]) +def time_ne_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, Time)]) +def time_gt_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, Time)]) +def time_ge_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, Time)]) +def time_lt_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, Time)]) +def time_le_type_error(request): + return request.param + + +time_to_type_error_1 = [{'target_unit': type_to_check, 'inplace': True} + for type_to_check in types_to_check if not isinstance(type_to_check, str)] + +time_to_type_error_2 = [{'target_unit': 'target_unit', 'inplace': type_to_check} for type_to_check in types_to_check + if not isinstance(type_to_check, bool) and not isinstance(type_to_check, int)] + +@fixture(params = [*time_to_type_error_1, + *time_to_type_error_2]) +def time_to_type_error(request): + return request.param \ No newline at end of file diff --git a/tests/test_units/test_time/test_time.py b/tests/test_units/test_time/test_time.py new file mode 100644 index 0000000..b905af5 --- /dev/null +++ b/tests/test_units/test_time/test_time.py @@ -0,0 +1,436 @@ +from gearpy.units import AngularAcceleration, AngularPosition, AngularSpeed, Time +from hypothesis.strategies import floats, sampled_from, one_of, booleans +from hypothesis import given, settings +from tests.test_units.test_angular_speed.conftest import angular_speeds +from tests.test_units.test_angular_acceleration.conftest import angular_accelerations +from tests.test_units.test_time.conftest import basic_time, times +from pytest import mark, raises + + +units_list = list(Time._Time__UNITS.keys()) + + +@mark.units +class TestTimeInit: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False), + unit = sampled_from(elements = units_list)) + @settings(max_examples = 100) + def test_method(self, value, unit): + time = Time(value = value, unit = unit) + + assert time.value == value + assert time.unit == unit + + + @mark.error + def test_raises_type_error(self, time_init_type_error): + with raises(TypeError): + Time(**time_init_type_error) + + + @mark.error + def test_raises_key_error(self): + fake_units = [f'fake {unit}' for unit in units_list] + for fake_unit in fake_units: + with raises(KeyError): + Time(value = 1, unit = fake_unit) + + +@mark.units +class TestTimeRepr: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False), + unit = sampled_from(elements = units_list)) + @settings(max_examples = 100) + def test_method(self, value, unit): + time = Time(value = value, unit = unit) + + assert str(time) == f'{value} {unit}' + + + +@mark.units +class TestTimeAdd: + + + @mark.genuine + @given(value_1 = floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000), + unit_1 = sampled_from(elements = units_list), + value_2 = floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000), + unit_2 = sampled_from(elements = units_list)) + @settings(max_examples = 100) + def test_method(self, value_1, unit_1, value_2, unit_2): + time_1 = Time(value = value_1, unit = unit_1) + time_2 = Time(value = value_2, unit = unit_2) + result = time_1 + time_2 + + assert isinstance(result, Time) + assert result.value == time_1.value + time_2.to(unit_1).value + assert result.unit == time_1.unit + + + @mark.error + def test_raises_type_error(self, time_add_type_error): + with raises(TypeError): + assert basic_time + time_add_type_error + + +@mark.units +class TestTimeSub: + + + @mark.genuine + @given(value_1 = floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000), + unit_1 = sampled_from(elements = units_list), + value_2 = floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000), + unit_2 = sampled_from(elements = units_list)) + @settings(max_examples = 100) + def test_method(self, value_1, unit_1, value_2, unit_2): + time_1 = Time(value = value_1, unit = unit_1) + time_2 = Time(value = value_2, unit = unit_2) + result = time_1 - time_2 + + assert isinstance(result, Time) + assert result.value == time_1.value - time_2.to(unit_1).value + assert result.unit == time_1.unit + + + @mark.error + def test_raises_type_error(self, time_sub_type_error): + with raises(TypeError): + assert basic_time - time_sub_type_error + + +@mark.units +class TestTimeMul: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000), + unit = sampled_from(elements = units_list), + multiplier = one_of(floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000), + angular_speeds(), + angular_accelerations())) + @settings(max_examples = 100) + def test_method(self, value, unit, multiplier): + time = Time(value = value, unit = unit) + result = time*multiplier + + if isinstance(multiplier, AngularAcceleration): + assert isinstance(result, AngularSpeed) + assert result.value == time.to('sec').value*multiplier.to('rad/s^2').value + assert result.unit == 'rad/s' + elif isinstance(multiplier, AngularSpeed): + assert isinstance(result, AngularPosition) + assert result.value == time.to('sec').value*multiplier.to('rad/s').value + assert result.unit == 'rad' + else: + assert isinstance(result, Time) + assert result.value == time.value*multiplier + assert result.unit == time.unit + + + @mark.error + def test_raises_type_error(self, time_mul_type_error): + with raises(TypeError): + assert basic_time*time_mul_type_error + + +@mark.units +class TestTimeRmul: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000), + unit = sampled_from(elements = units_list), + multiplier = one_of(floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000), + angular_speeds(), + angular_accelerations())) + @settings(max_examples = 100) + def test_method(self, value, unit, multiplier): + time = Time(value = value, unit = unit) + result = multiplier*time + + if isinstance(multiplier, AngularAcceleration): + assert isinstance(result, AngularSpeed) + assert result.value == time.to('sec').value*multiplier.to('rad/s^2').value + assert result.unit == 'rad/s' + elif isinstance(multiplier, AngularSpeed): + assert isinstance(result, AngularPosition) + assert result.value == time.to('sec').value*multiplier.to('rad/s').value + assert result.unit == 'rad' + else: + assert isinstance(result, Time) + assert result.value == time.value*multiplier + assert result.unit == time.unit + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000), + unit = sampled_from(elements = units_list)) + @settings(max_examples = 100) + def test_method_patch_1(self, value, unit): + time = Time(value = value, unit = unit) + + class FakeAngularAcceleration(AngularAcceleration): + + def __mul__(self, other: Time): return NotImplemented + + fake_multiplier = FakeAngularAcceleration(1, 'rad/s^2') + result = fake_multiplier*time + + assert isinstance(result, AngularSpeed) + assert result.value == time.to('sec').value*fake_multiplier.to('rad/s^2').value + assert result.unit == 'rad/s' + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000), + unit = sampled_from(elements = units_list)) + @settings(max_examples = 100) + def test_method_patch_2(self, value, unit): + time = Time(value = value, unit = unit) + + class FakeAngularSpeed(AngularSpeed): + + def __mul__(self, other: Time): return NotImplemented + + fake_multiplier = FakeAngularSpeed(1, 'rad/s') + result = fake_multiplier*time + + assert isinstance(result, AngularPosition) + assert result.value == time.to('sec').value*fake_multiplier.to('rad/s').value + assert result.unit == 'rad' + + + @mark.error + def test_raises_type_error(self, time_rmul_type_error): + with raises(TypeError): + assert time_rmul_type_error*basic_time + + +@mark.units +class TestTimeTruediv: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000), + unit = sampled_from(elements = units_list), + divider = one_of(floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000), + times())) + @settings(max_examples = 100) + def test_method(self, value, unit, divider): + time = Time(value = value, unit = unit) + + if isinstance(divider, Time): + if abs(divider.value) >= 1e-300: + result = time/divider + assert isinstance(result, float) + assert result == time.value/divider.to(unit).value + else: + if divider != 0: + result = time/divider + assert isinstance(result, Time) + assert result.value == time.value/divider + assert result.unit == time.unit + + + @mark.error + def test_raises_type_error(self, time_truediv_type_error): + with raises(TypeError): + assert basic_time/time_truediv_type_error + + + @mark.error + def test_raises_zero_division_error(self, time_truediv_zero_division_error): + with raises(ZeroDivisionError): + assert basic_time/time_truediv_zero_division_error + + +@mark.units +class TestTimeEq: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, max_value = 1000, min_value = -1000), + unit = sampled_from(elements = units_list)) + @settings(max_examples = 100) + def test_method(self, value, unit): + time_1 = Time(value = value, unit = unit) + time_2 = Time(value = value, unit = unit) + + for target_unit in units_list: + assert time_1 == time_2.to(target_unit) + + + @mark.error + def test_raises_type_error(self, time_eq_type_error): + with raises(TypeError): + assert basic_time == time_eq_type_error + + +@mark.units +class TestTimeNe: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, max_value = 1000, min_value = -1000), + unit = sampled_from(elements = units_list), + gap = floats(allow_nan = False, allow_infinity = False, min_value = 1e-10, exclude_min = True, max_value = 1000)) + @settings(max_examples = 100) + def test_method(self, value, unit, gap): + time_1 = Time(value = value, unit = unit) + time_2 = Time(value = value + gap, unit = unit) + + for target_unit in units_list: + assert time_1 != time_2.to(target_unit) + + + @mark.error + def test_raises_type_error(self, time_ne_type_error): + with raises(TypeError): + assert basic_time != time_ne_type_error + + +@mark.units +class TestTimeGt: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, max_value = 1000, min_value = -1000), + unit = sampled_from(elements = units_list), + gap = floats(allow_nan = False, allow_infinity = False, min_value = 1e-10, exclude_min = True, max_value = 1000)) + @settings(max_examples = 100) + def test_method(self, value, unit, gap): + time_1 = Time(value = value + gap, unit = unit) + time_2 = Time(value = value, unit = unit) + + for target_unit in units_list: + assert time_1 > time_2.to(target_unit) + + + @mark.error + def test_raises_type_error(self, time_gt_type_error): + with raises(TypeError): + assert basic_time > time_gt_type_error + + +@mark.units +class TestTimeGe: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, max_value = 1000, min_value = -1000), + unit = sampled_from(elements = units_list), + gap = floats(allow_nan = False, allow_infinity = False, min_value = 0, exclude_min = False, max_value = 1000)) + @settings(max_examples = 100) + def test_method(self, value, unit, gap): + time_1 = Time(value = value + gap, unit = unit) + time_2 = Time(value = value, unit = unit) + + for target_unit in units_list: + assert time_1 >= time_2.to(target_unit) + + + @mark.error + def test_raises_type_error(self, time_ge_type_error): + with raises(TypeError): + assert basic_time >= time_ge_type_error + + +@mark.units +class TestTimeLt: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, max_value = 1000, min_value = -1000), + unit = sampled_from(elements = units_list), + gap = floats(allow_nan = False, allow_infinity = False, min_value = 1e-10, exclude_min = True, max_value = 1000)) + @settings(max_examples = 100) + def test_method(self, value, unit, gap): + if value != 0: + time_1 = Time(value = value, unit = unit) + time_2 = Time(value = value + gap, unit = unit) + + for target_unit in units_list: + assert time_1 < time_2.to(target_unit) + + + @mark.error + def test_raises_type_error(self, time_lt_type_error): + with raises(TypeError): + assert basic_time < time_lt_type_error + + +@mark.units +class TestTimeLe: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, max_value = 1000, min_value = -1000), + unit = sampled_from(elements = units_list), + gap = floats(allow_nan = False, allow_infinity = False, min_value = 0, exclude_min = False, max_value = 1000)) + @settings(max_examples = 100) + def test_method(self, value, unit, gap): + if value != 0: + time_1 = Time(value = value, unit = unit) + time_2 = Time(value = value + gap, unit = unit) + + for target_unit in units_list: + assert time_1 <= time_2.to(target_unit) + + + @mark.error + def test_raises_type_error(self, time_le_type_error): + with raises(TypeError): + assert basic_time <= time_le_type_error + + +@mark.units +class TestTimeTo: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, max_value = 1000, min_value = -1000), + unit = sampled_from(elements = units_list), + inplace = booleans()) + @settings(max_examples = 100) + def test_method(self, value, unit, inplace): + if value != 0: + time = Time(value = value, unit = unit) + + for target_unit in units_list: + converted_time = time.to(target_unit = target_unit, inplace = inplace) + + assert converted_time.unit == target_unit + if target_unit != unit: + assert converted_time.value != value + assert converted_time.unit != unit + + if inplace: + assert converted_time.value == time.value + assert converted_time.unit == time.unit + else: + assert converted_time.value != time.value + assert converted_time.unit != time.unit + else: + assert converted_time == time + + + @mark.error + def test_raises_type_error(self, time_to_type_error): + with raises(TypeError): + basic_time.to(**time_to_type_error) + + + @mark.error + def test_raises_key_error(self): + fake_units = [f'fake {unit}' for unit in units_list] + for fake_unit in fake_units: + with raises(KeyError): + basic_time.to(fake_unit) diff --git a/tests/test_units/test_time_interval/conftest.py b/tests/test_units/test_time_interval/conftest.py new file mode 100644 index 0000000..fda50bd --- /dev/null +++ b/tests/test_units/test_time_interval/conftest.py @@ -0,0 +1,106 @@ +from gearpy.units import AngularAcceleration, AngularSpeed, Time, TimeInterval +import numpy as np +from tests.conftest import types_to_check +from pytest import fixture + + +basic_time_interval = TimeInterval(1, 'sec') + + +time_interval_init_type_error_1 = [{'value': type_to_check, 'unit': 'unit'} for type_to_check in types_to_check + if not isinstance(type_to_check, float) and not isinstance(type_to_check, int)] + +time_interval_init_type_error_2 = [{'value': 1, 'unit': types_to_check} for type_to_check in types_to_check + if not isinstance(type_to_check, str)] + +@fixture(params = [*time_interval_init_type_error_1, + *time_interval_init_type_error_2]) +def time_interval_init_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check + if not isinstance(type_to_check, Time) and not isinstance(type_to_check, TimeInterval)]) +def time_interval_add_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check + if not isinstance(type_to_check, Time) and not isinstance(type_to_check, TimeInterval)]) +def time_interval_sub_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check + if not isinstance(type_to_check, float) and not isinstance(type_to_check, int) + and not isinstance(type_to_check, AngularSpeed) and not isinstance(type_to_check, AngularAcceleration)]) +def time_interval_mul_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, float) + and not isinstance(type_to_check, int) and not isinstance(type_to_check, np.ndarray) + and not isinstance(type_to_check, AngularSpeed) and not isinstance(type_to_check, AngularAcceleration)]) +def time_interval_rmul_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, float) + and not isinstance(type_to_check, int) and not isinstance(type_to_check, Time) + and not isinstance(type_to_check, TimeInterval)]) +def time_interval_truediv_type_error(request): + return request.param + + +@fixture(params = [0, 0.0]) +def time_interval_truediv_zero_division_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check + if not isinstance(type_to_check, Time) and not isinstance(type_to_check, TimeInterval)]) +def time_interval_eq_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check + if not isinstance(type_to_check, Time) and not isinstance(type_to_check, TimeInterval)]) +def time_interval_ne_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check + if not isinstance(type_to_check, Time) and not isinstance(type_to_check, TimeInterval)]) +def time_interval_gt_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check + if not isinstance(type_to_check, Time) and not isinstance(type_to_check, TimeInterval)]) +def time_interval_ge_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check + if not isinstance(type_to_check, Time) and not isinstance(type_to_check, TimeInterval)]) +def time_interval_lt_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check + if not isinstance(type_to_check, Time) and not isinstance(type_to_check, TimeInterval)]) +def time_interval_le_type_error(request): + return request.param + + +time_interval_to_type_error_1 = [{'target_unit': type_to_check, 'inplace': True} + for type_to_check in types_to_check if not isinstance(type_to_check, str)] + +time_interval_to_type_error_2 = [{'target_unit': 'target_unit', 'inplace': type_to_check} + for type_to_check in types_to_check if not isinstance(type_to_check, bool) + and not isinstance(type_to_check, int)] + +@fixture(params = [*time_interval_to_type_error_1, + *time_interval_to_type_error_2]) +def time_interval_to_type_error(request): + return request.param \ No newline at end of file diff --git a/tests/test_units/test_time_interval/test_time_interval.py b/tests/test_units/test_time_interval/test_time_interval.py new file mode 100644 index 0000000..e737b4c --- /dev/null +++ b/tests/test_units/test_time_interval/test_time_interval.py @@ -0,0 +1,414 @@ +from gearpy.units import Time, TimeInterval +from hypothesis.strategies import floats, sampled_from, one_of, booleans +from hypothesis import given, settings +from tests.conftest import time_intervals +from tests.test_units.test_time_interval.conftest import basic_time_interval +from pytest import mark, raises + + +units_list = list(TimeInterval._Time__UNITS.keys()) + + +@mark.units +class TestTimeIntervalInit: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, min_value = 1e-10, exclude_min = True), + unit = sampled_from(elements = units_list)) + @settings(max_examples = 100) + def test_method(self, value, unit): + time_interval = TimeInterval(value = value, unit = unit) + + assert time_interval.value == value + assert time_interval.unit == unit + + + @mark.error + def test_raises_type_error(self, time_interval_init_type_error): + with raises(TypeError): + TimeInterval(**time_interval_init_type_error) + + + @mark.error + def test_raises_key_error(self): + fake_units = [f'fake {unit}' for unit in units_list] + for fake_unit in fake_units: + with raises(KeyError): + TimeInterval(value = 1, unit = fake_unit) + + + @mark.error + def test_raises_value_error(self): + with raises(ValueError): + TimeInterval(value = -1, unit = 'sec') + + +@mark.units +class TestTimeIntervalRepr: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, min_value = 1e-10, exclude_min = True), + unit = sampled_from(elements = units_list)) + @settings(max_examples = 100) + def test_method(self, value, unit): + time_interval = TimeInterval(value = value, unit = unit) + + assert str(time_interval) == f'{value} {unit}' + + + +@mark.units +class TestTimeIntervalAdd: + + + @mark.genuine + @given(value_1 = floats(allow_nan = False, allow_infinity = False, min_value = 1e-10, exclude_min = True), + unit_1 = sampled_from(elements = units_list), + value_2 = floats(allow_nan = False, allow_infinity = False, min_value = 1e-10, exclude_min = True), + unit_2 = sampled_from(elements = units_list)) + @settings(max_examples = 100) + def test_method(self, value_1, unit_1, value_2, unit_2): + time_interval_1 = TimeInterval(value = value_1, unit = unit_1) + time_interval_2 = TimeInterval(value = value_2, unit = unit_2) + time = Time(value = value_2, unit = unit_2) + result_1 = time_interval_1 + time_interval_2 + result_2 = time_interval_1 + time + + assert isinstance(result_1, TimeInterval) + assert result_1.value == time_interval_1.value + time_interval_2.to(unit_1).value + assert result_1.unit == time_interval_1.unit + assert isinstance(result_2, Time) + assert result_2.value == time_interval_1.value + time.to(unit_1).value + assert result_2.unit == time_interval_1.unit + + + @mark.error + def test_raises_type_error(self, time_interval_add_type_error): + with raises(TypeError): + assert basic_time_interval + time_interval_add_type_error + + +@mark.units +class TestTimeIntervalSub: + + + @mark.genuine + @given(value_1 = floats(allow_nan = False, allow_infinity = False, min_value = 1e-10, exclude_min = True), + unit_1 = sampled_from(elements = units_list), + value_2 = floats(allow_nan = False, allow_infinity = False, min_value = 1e-10, exclude_min = True), + unit_2 = sampled_from(elements = units_list)) + @settings(max_examples = 100) + def test_method(self, value_1, unit_1, value_2, unit_2): + time_interval_1 = TimeInterval(value = value_1, unit = unit_1) + time_interval_2 = TimeInterval(value = value_2, unit = unit_2) + time = Time(value = value_2, unit = unit_2) + + if time_interval_1 > time_interval_2: + result_1 = time_interval_1 - time_interval_2 + + assert isinstance(result_1, TimeInterval) + assert result_1.value == time_interval_1.value - time_interval_2.to(unit_1).value + assert result_1.unit == time_interval_1.unit + + if time_interval_1 > time: + result_2 = time_interval_1 - time + + assert isinstance(result_2, Time) + assert result_2.value == time_interval_1.value + time.to(unit_1).value + assert result_2.unit == time_interval_1.unit + + + @mark.error + def test_raises_type_error(self, time_interval_sub_type_error): + with raises(TypeError): + assert basic_time_interval - time_interval_sub_type_error + + + @mark.error + def test_raises_value_error(self): + with raises(ValueError): + assert basic_time_interval - TimeInterval(basic_time_interval.value + 1, basic_time_interval.unit) + + +@mark.units +class TestTimeIntervalMul: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, min_value = 1e-10, exclude_min = True), + unit = sampled_from(elements = units_list), + multiplier = floats(allow_nan = False, allow_infinity = False, min_value = 1e-10, max_value = 1000)) + @settings(max_examples = 100) + def test_method(self, value, unit, multiplier): + time_interval = TimeInterval(value = value, unit = unit) + result = time_interval*multiplier + + assert isinstance(result, TimeInterval) + assert result.value == time_interval.value*multiplier + assert result.unit == time_interval.unit + + + @mark.error + def test_raises_type_error(self, time_interval_mul_type_error): + with raises(TypeError): + assert basic_time_interval*time_interval_mul_type_error + + + @mark.error + def test_raises_value_error(self): + with raises(ValueError): + assert basic_time_interval*(-1) + + +@mark.units +class TestTimeIntervalRmul: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, min_value = 1e-10, exclude_min = True), + unit = sampled_from(elements = units_list), + multiplier = floats(allow_nan = False, allow_infinity = False, min_value = 1e-10, max_value = 1000)) + @settings(max_examples = 100) + def test_method(self, value, unit, multiplier): + time_interval = TimeInterval(value = value, unit = unit) + result = multiplier*time_interval + + assert isinstance(result, TimeInterval) + assert result.value == time_interval.value*multiplier + assert result.unit == time_interval.unit + + + @mark.error + def test_raises_type_error(self, time_interval_rmul_type_error): + with raises(TypeError): + assert time_interval_rmul_type_error*basic_time_interval + + + @mark.error + def test_raises_value_error(self): + with raises(ValueError): + assert -1*basic_time_interval + + +@mark.units +class TestTimeIntervalTruediv: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, min_value = 1e-10, exclude_min = True, max_value = 1000), + unit = sampled_from(elements = units_list), + divider = one_of(floats(allow_nan = False, allow_infinity = False, min_value = 1e-10, exclude_min = True, max_value = 1000), + time_intervals())) + @settings(max_examples = 100) + def test_method(self, value, unit, divider): + time_interval = TimeInterval(value = value, unit = unit) + + if isinstance(divider, TimeInterval): + if abs(divider.value) >= 1e-300: + result = time_interval/divider + assert isinstance(result, float) + assert result == time_interval.value/divider.to(unit).value + else: + if divider != 0: + result = time_interval/divider + assert isinstance(result, TimeInterval) + assert result.value == time_interval.value/divider + assert result.unit == time_interval.unit + + + @mark.error + def test_raises_type_error(self, time_interval_truediv_type_error): + with raises(TypeError): + assert basic_time_interval/time_interval_truediv_type_error + + + @mark.error + def test_raises_zero_division_error(self, time_interval_truediv_zero_division_error): + with raises(ZeroDivisionError): + assert basic_time_interval/time_interval_truediv_zero_division_error + + +@mark.units +class TestTimeIntervalEq: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, min_value = 1e-10, exclude_min = True, max_value = 1000), + unit = sampled_from(elements = units_list)) + @settings(max_examples = 100) + def test_method(self, value, unit): + time_interval_1 = TimeInterval(value = value, unit = unit) + time_interval_2 = TimeInterval(value = value, unit = unit) + + for target_unit in units_list: + assert time_interval_1 == time_interval_2.to(target_unit) + + + @mark.error + def test_raises_type_error(self, time_interval_eq_type_error): + with raises(TypeError): + assert basic_time_interval == time_interval_eq_type_error + + +@mark.units +class TestTimeIntervalNe: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, min_value = 1e-10, exclude_min = True, max_value = 1000), + unit = sampled_from(elements = units_list), + gap = floats(allow_nan = False, allow_infinity = False, min_value = 1e-10, exclude_min = True, max_value = 1000)) + @settings(max_examples = 100) + def test_method(self, value, unit, gap): + time_interval_1 = TimeInterval(value = value, unit = unit) + time_interval_2 = TimeInterval(value = value + gap, unit = unit) + + for target_unit in units_list: + assert time_interval_1 != time_interval_2.to(target_unit) + + + @mark.error + def test_raises_type_error(self, time_interval_ne_type_error): + with raises(TypeError): + assert basic_time_interval != time_interval_ne_type_error + + +@mark.units +class TestTimeIntervalGt: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, min_value = 1e-10, exclude_min = True, max_value = 1000), + unit = sampled_from(elements = units_list), + gap = floats(allow_nan = False, allow_infinity = False, min_value = 1e-10, exclude_min = True, max_value = 1000)) + @settings(max_examples = 100) + def test_method(self, value, unit, gap): + time_interval_1 = TimeInterval(value = value + gap, unit = unit) + time_interval_2 = TimeInterval(value = value, unit = unit) + + for target_unit in units_list: + assert time_interval_1 > time_interval_2.to(target_unit) + + + @mark.error + def test_raises_type_error(self, time_interval_gt_type_error): + with raises(TypeError): + assert basic_time_interval > time_interval_gt_type_error + + +@mark.units +class TestTimeIntervalGe: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, min_value = 1e-10, exclude_min = True, max_value = 1000), + unit = sampled_from(elements = units_list), + gap = floats(allow_nan = False, allow_infinity = False, min_value = 0, exclude_min = False, max_value = 1000)) + @settings(max_examples = 100) + def test_method(self, value, unit, gap): + time_interval_1 = TimeInterval(value = value + gap, unit = unit) + time_interval_2 = TimeInterval(value = value, unit = unit) + + for target_unit in units_list: + assert time_interval_1 >= time_interval_2.to(target_unit) + + + @mark.error + def test_raises_type_error(self, time_interval_ge_type_error): + with raises(TypeError): + assert basic_time_interval >= time_interval_ge_type_error + + +@mark.units +class TestTimeIntervalLt: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, min_value = 1e-10, exclude_min = True, max_value = 1000), + unit = sampled_from(elements = units_list), + gap = floats(allow_nan = False, allow_infinity = False, min_value = 1e-10, exclude_min = True, max_value = 1000)) + @settings(max_examples = 100) + def test_method(self, value, unit, gap): + if value != 0: + time_interval_1 = TimeInterval(value = value, unit = unit) + time_interval_2 = TimeInterval(value = value + gap, unit = unit) + + for target_unit in units_list: + assert time_interval_1 < time_interval_2.to(target_unit) + + + @mark.error + def test_raises_type_error(self, time_interval_lt_type_error): + with raises(TypeError): + assert basic_time_interval < time_interval_lt_type_error + + +@mark.units +class TestTimeIntervalLe: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, min_value = 1e-10, exclude_min = True, max_value = 1000), + unit = sampled_from(elements = units_list), + gap = floats(allow_nan = False, allow_infinity = False, min_value = 0, exclude_min = False, max_value = 1000)) + @settings(max_examples = 100) + def test_method(self, value, unit, gap): + if value != 0: + time_interval_1 = TimeInterval(value = value, unit = unit) + time_interval_2 = TimeInterval(value = value + gap, unit = unit) + + for target_unit in units_list: + assert time_interval_1 <= time_interval_2.to(target_unit) + + + @mark.error + def test_raises_type_error(self, time_interval_le_type_error): + with raises(TypeError): + assert basic_time_interval <= time_interval_le_type_error + + +@mark.units +class TestTimeIntervalTo: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, min_value = 1e-10, exclude_min = True, max_value = 1000), + unit = sampled_from(elements = units_list), + inplace = booleans()) + @settings(max_examples = 100) + def test_method(self, value, unit, inplace): + if value != 0: + time_interval = TimeInterval(value = value, unit = unit) + + for target_unit in units_list: + converted_time_interval = time_interval.to(target_unit = target_unit, inplace = inplace) + + assert converted_time_interval.unit == target_unit + if target_unit != unit: + assert converted_time_interval.value != value + assert converted_time_interval.unit != unit + + if inplace: + assert converted_time_interval.value == time_interval.value + assert converted_time_interval.unit == time_interval.unit + else: + assert converted_time_interval.value != time_interval.value + assert converted_time_interval.unit != time_interval.unit + else: + assert converted_time_interval == time_interval + + + @mark.error + def test_raises_type_error(self, time_interval_to_type_error): + with raises(TypeError): + basic_time_interval.to(**time_interval_to_type_error) + + + @mark.error + def test_raises_key_error(self): + fake_units = [f'fake {unit}' for unit in units_list] + for fake_unit in fake_units: + with raises(KeyError): + basic_time_interval.to(fake_unit) diff --git a/tests/test_units/test_torque/conftest.py b/tests/test_units/test_torque/conftest.py new file mode 100644 index 0000000..867ecf3 --- /dev/null +++ b/tests/test_units/test_torque/conftest.py @@ -0,0 +1,104 @@ +from gearpy.units import InertiaMoment, Torque +from hypothesis.strategies import composite, floats, sampled_from +import numpy as np +from tests.conftest import types_to_check +from pytest import fixture + + +basic_torque = Torque(1, 'Nm') + + +@composite +def torques(draw): + value = draw(floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000)) + unit = draw(sampled_from(elements = list(Torque._Torque__UNITS.keys()))) + + return Torque(value = value, unit = unit) + + +torque_init_type_error_1 = [{'value': type_to_check, 'unit': 'unit'} for type_to_check in types_to_check + if not isinstance(type_to_check, float) and not isinstance(type_to_check, int)] + +torque_init_type_error_2 = [{'value': 1, 'unit': types_to_check} for type_to_check in types_to_check + if not isinstance(type_to_check, str)] + +@fixture(params = [*torque_init_type_error_1, + *torque_init_type_error_2]) +def torque_init_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, Torque)]) +def torque_add_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, Torque)]) +def torque_sub_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check + if not isinstance(type_to_check, float) and not isinstance(type_to_check, int)]) +def torque_mul_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, float) + and not isinstance(type_to_check, int) and not isinstance(type_to_check, np.ndarray)]) +def torque_rmul_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, float) + and not isinstance(type_to_check, int) and not isinstance(type_to_check, InertiaMoment) + and not isinstance(type_to_check, Torque)]) +def torque_truediv_type_error(request): + return request.param + + +@fixture(params = [0, 0.0, Torque(0, 'Nm')]) +def torque_truediv_zero_division_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, Torque)]) +def torque_eq_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, Torque)]) +def torque_ne_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, Torque)]) +def torque_gt_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, Torque)]) +def torque_ge_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, Torque)]) +def torque_lt_type_error(request): + return request.param + + +@fixture(params = [type_to_check for type_to_check in types_to_check if not isinstance(type_to_check, Torque)]) +def torque_le_type_error(request): + return request.param + + +torque_to_type_error_1 = [{'target_unit': type_to_check, 'inplace': True} + for type_to_check in types_to_check if not isinstance(type_to_check, str)] + +torque_to_type_error_2 = [{'target_unit': 'target_unit', 'inplace': type_to_check} for type_to_check in types_to_check + if not isinstance(type_to_check, bool) and not isinstance(type_to_check, int)] + +@fixture(params = [*torque_to_type_error_1, + *torque_to_type_error_2]) +def torque_to_type_error(request): + return request.param \ No newline at end of file diff --git a/tests/test_units/test_torque/test_torque.py b/tests/test_units/test_torque/test_torque.py new file mode 100644 index 0000000..df6e4cd --- /dev/null +++ b/tests/test_units/test_torque/test_torque.py @@ -0,0 +1,382 @@ +from gearpy.units import AngularAcceleration, InertiaMoment, Torque +from hypothesis.strategies import floats, sampled_from, one_of, booleans +from hypothesis import given, settings +from tests.test_units.test_inertia_moment.conftest import inertia_moments +from tests.test_units.test_torque.conftest import basic_torque, torques +from pytest import mark, raises + + +units_list = list(Torque._Torque__UNITS.keys()) + + +@mark.units +class TestTorqueInit: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000), + unit = sampled_from(elements = units_list)) + @settings(max_examples = 100) + def test_method(self, value, unit): + torque = Torque(value = value, unit = unit) + + assert torque.value == value + assert torque.unit == unit + + + @mark.error + def test_raises_type_error(self, torque_init_type_error): + with raises(TypeError): + Torque(**torque_init_type_error) + + + @mark.error + def test_raises_key_error(self): + fake_units = [f'fake {unit}' for unit in units_list] + for fake_unit in fake_units: + with raises(KeyError): + Torque(value = 1, unit = fake_unit) + + +@mark.units +class TestTorqueRepr: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000), + unit = sampled_from(elements = units_list)) + @settings(max_examples = 100) + def test_method(self, value, unit): + torque = Torque(value = value, unit = unit) + + assert str(torque) == f'{value} {unit}' + + + +@mark.units +class TestTorqueAdd: + + + @mark.genuine + @given(value_1 = floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000), + unit_1 = sampled_from(elements = units_list), + value_2 = floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000), + unit_2 = sampled_from(elements = units_list)) + @settings(max_examples = 100) + def test_method(self, value_1, unit_1, value_2, unit_2): + torque_1 = Torque(value = value_1, unit = unit_1) + torque_2 = Torque(value = value_2, unit = unit_2) + result = torque_1 + torque_2 + + assert isinstance(result, Torque) + assert result.value == torque_1.value + torque_2.to(unit_1).value + assert result.unit == torque_1.unit + + + @mark.error + def test_raises_type_error(self, torque_add_type_error): + with raises(TypeError): + assert basic_torque + torque_add_type_error + + +@mark.units +class TestTorqueSub: + + + @mark.genuine + @given(value_1 = floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000), + unit_1 = sampled_from(elements = units_list), + value_2 = floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000), + unit_2 = sampled_from(elements = units_list)) + @settings(max_examples = 100) + def test_method(self, value_1, unit_1, value_2, unit_2): + torque_1 = Torque(value = value_1, unit = unit_1) + torque_2 = Torque(value = value_2, unit = unit_2) + result = torque_1 - torque_2 + + assert isinstance(result, Torque) + assert result.value == torque_1.value - torque_2.to(unit_1).value + assert result.unit == torque_1.unit + + + @mark.error + def test_raises_type_error(self, torque_sub_type_error): + with raises(TypeError): + assert basic_torque - torque_sub_type_error + + +@mark.units +class TestTorqueMul: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000), + unit = sampled_from(elements = units_list), + multiplier = floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000)) + @settings(max_examples = 100) + def test_method(self, value, unit, multiplier): + torque = Torque(value = value, unit = unit) + result = torque*multiplier + + assert isinstance(result, Torque) + assert result.value == torque.value*multiplier + assert result.unit == torque.unit + + + @mark.error + def test_raises_type_error(self, torque_mul_type_error): + with raises(TypeError): + assert basic_torque*torque_mul_type_error + + +@mark.units +class TestTorqueRmul: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000), + unit = sampled_from(elements = units_list), + multiplier = floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000)) + @settings(max_examples = 100) + def test_method(self, value, unit, multiplier): + torque = Torque(value = value, unit = unit) + result = multiplier*torque + + assert isinstance(result, Torque) + assert result.value == torque.value*multiplier + assert result.unit == torque.unit + + + @mark.error + def test_raises_type_error(self, torque_rmul_type_error): + with raises(TypeError): + assert torque_rmul_type_error*basic_torque + + +@mark.units +class TestTorqueTruediv: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000), + unit = sampled_from(elements = units_list), + divider = one_of(floats(allow_nan = False, allow_infinity = False, min_value = -1000, max_value = 1000), + torques(), + inertia_moments())) + @settings(max_examples = 100) + def test_method(self, value, unit, divider): + torque = Torque(value = value, unit = unit) + + if isinstance(divider, Torque): + if abs(divider.value) >= 1e-300: + result = torque/divider + assert isinstance(result, float) + assert result == torque.value/divider.to(unit).value + elif isinstance(divider, InertiaMoment): + if abs(divider.value) >= 1e-300: + result = torque/divider + assert isinstance(result, AngularAcceleration) + assert result.value == torque.to('Nm').value/divider.to('kgm^2').value + assert result.unit == 'rad/s^2' + else: + if divider != 0: + result = torque/divider + assert isinstance(result, Torque) + assert result.value == torque.value/divider + assert result.unit == torque.unit + + + @mark.error + def test_raises_type_error(self, torque_truediv_type_error): + with raises(TypeError): + assert basic_torque/torque_truediv_type_error + + + @mark.error + def test_raises_zero_division_error(self, torque_truediv_zero_division_error): + with raises(ZeroDivisionError): + assert basic_torque/torque_truediv_zero_division_error + + +@mark.units +class TestTorqueEq: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, max_value = 1000, min_value = -1000), + unit = sampled_from(elements = units_list)) + @settings(max_examples = 100) + def test_method(self, value, unit): + torque_1 = Torque(value = value, unit = unit) + torque_2 = Torque(value = value, unit = unit) + + for target_unit in units_list: + assert torque_1 == torque_2.to(target_unit) + + + @mark.error + def test_raises_type_error(self, torque_eq_type_error): + with raises(TypeError): + assert basic_torque == torque_eq_type_error + + +@mark.units +class TestTorqueNe: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, max_value = 1000, min_value = -1000), + unit = sampled_from(elements = units_list), + gap = floats(allow_nan = False, allow_infinity = False, min_value = 1e-10, exclude_min = True, max_value = 1000)) + @settings(max_examples = 100) + def test_method(self, value, unit, gap): + torque_1 = Torque(value = value, unit = unit) + torque_2 = Torque(value = value + gap, unit = unit) + + for target_unit in units_list: + assert torque_1 != torque_2.to(target_unit) + + + @mark.error + def test_raises_type_error(self, torque_ne_type_error): + with raises(TypeError): + assert basic_torque != torque_ne_type_error + + +@mark.units +class TestTorqueGt: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, max_value = 1000, min_value = -1000), + unit = sampled_from(elements = units_list), + gap = floats(allow_nan = False, allow_infinity = False, min_value = 1e-10, exclude_min = True, max_value = 1000)) + @settings(max_examples = 100) + def test_method(self, value, unit, gap): + torque_1 = Torque(value = value + gap, unit = unit) + torque_2 = Torque(value = value, unit = unit) + + for target_unit in units_list: + assert torque_1 > torque_2.to(target_unit) + + + @mark.error + def test_raises_type_error(self, torque_gt_type_error): + with raises(TypeError): + assert basic_torque > torque_gt_type_error + + +@mark.units +class TestTorqueGe: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, max_value = 1000, min_value = -1000), + unit = sampled_from(elements = units_list), + gap = floats(allow_nan = False, allow_infinity = False, min_value = 0, exclude_min = False, max_value = 1000)) + @settings(max_examples = 100) + def test_method(self, value, unit, gap): + torque_1 = Torque(value = value + gap, unit = unit) + torque_2 = Torque(value = value, unit = unit) + + for target_unit in units_list: + assert torque_1 >= torque_2.to(target_unit) + + + @mark.error + def test_raises_type_error(self, torque_ge_type_error): + with raises(TypeError): + assert basic_torque >= torque_ge_type_error + + +@mark.units +class TestTorqueLt: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, max_value = 1000, min_value = -1000), + unit = sampled_from(elements = units_list), + gap = floats(allow_nan = False, allow_infinity = False, min_value = 1e-10, exclude_min = True, max_value = 1000)) + @settings(max_examples = 100) + def test_method(self, value, unit, gap): + if value != 0: + torque_1 = Torque(value = value, unit = unit) + torque_2 = Torque(value = value + gap, unit = unit) + + for target_unit in units_list: + assert torque_1 < torque_2.to(target_unit) + + + @mark.error + def test_raises_type_error(self, torque_lt_type_error): + with raises(TypeError): + assert basic_torque < torque_lt_type_error + + +@mark.units +class TestTorqueLe: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, max_value = 1000, min_value = -1000), + unit = sampled_from(elements = units_list), + gap = floats(allow_nan = False, allow_infinity = False, min_value = 0, exclude_min = False, max_value = 1000)) + @settings(max_examples = 100) + def test_method(self, value, unit, gap): + if value != 0: + torque_1 = Torque(value = value, unit = unit) + torque_2 = Torque(value = value + gap, unit = unit) + + for target_unit in units_list: + assert torque_1 <= torque_2.to(target_unit) + + + @mark.error + def test_raises_type_error(self, torque_le_type_error): + with raises(TypeError): + assert basic_torque <= torque_le_type_error + + +@mark.units +class TestTorqueTo: + + + @mark.genuine + @given(value = floats(allow_nan = False, allow_infinity = False, max_value = 1000, min_value = -1000), + unit = sampled_from(elements = units_list), + inplace = booleans()) + @settings(max_examples = 100) + def test_method(self, value, unit, inplace): + if value != 0: + torque = Torque(value = value, unit = unit) + + for target_unit in units_list: + converted_torque = torque.to(target_unit = target_unit, inplace = inplace) + + assert converted_torque.unit == target_unit + if target_unit != unit: + assert converted_torque.value != value + assert converted_torque.unit != unit + + if inplace: + assert converted_torque.value == torque.value + assert converted_torque.unit == torque.unit + else: + assert converted_torque.value != torque.value + assert converted_torque.unit != torque.unit + else: + assert converted_torque == torque + + + @mark.error + def test_raises_type_error(self, torque_to_type_error): + with raises(TypeError): + basic_torque.to(**torque_to_type_error) + + + @mark.error + def test_raises_key_error(self): + fake_units = [f'fake {unit}' for unit in units_list] + for fake_unit in fake_units: + with raises(KeyError): + basic_torque.to(fake_unit) diff --git a/tests/test_utils.py b/tests/test_utils.py index 86d057c..279b797 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,4 +1,6 @@ -from gearpy import SpurGear, add_gear_mating, add_fixed_joint +from gearpy.gear import SpurGear +from gearpy.units import InertiaMoment +from gearpy.utils import add_gear_mating, add_fixed_joint from hypothesis import given, settings from hypothesis.strategies import floats from pytest import mark, raises @@ -27,15 +29,13 @@ def test_function(self, gear_1, gear_2, efficiency): @mark.error def test_raises_type_error(self, add_gear_mating_type_error): with raises(TypeError): - add_gear_mating(master = add_gear_mating_type_error['gear 1'], - slave = add_gear_mating_type_error['gear 2'], - efficiency = add_gear_mating_type_error['efficiency']) + add_gear_mating(**add_gear_mating_type_error) @mark.error def test_raises_value_error(self, add_gear_mating_value_error): - gear_1 = SpurGear(name = 'gear 1', n_teeth = 10, inertia = 1) - gear_2 = SpurGear(name = 'gear 2', n_teeth = 20, inertia = 1) + gear_1 = SpurGear(name = 'gear 1', n_teeth = 10, inertia_moment = InertiaMoment(1, 'kgm^2')) + gear_2 = SpurGear(name = 'gear 2', n_teeth = 20, inertia_moment = InertiaMoment(1, 'kgm^2')) with raises(ValueError): add_gear_mating(master = gear_1, slave = gear_2, efficiency = add_gear_mating_value_error) @@ -58,5 +58,4 @@ def test_function(self, gear_1, gear_2): @mark.error def test_raises_type_error(self, add_fixed_joint_type_error): with raises(TypeError): - add_fixed_joint(master = add_fixed_joint_type_error['master'], - slave = add_fixed_joint_type_error['slave']) + add_fixed_joint(**add_fixed_joint_type_error) diff --git a/tox.ini b/tox.ini index b0a47c7..6cbe41c 100644 --- a/tox.ini +++ b/tox.ini @@ -26,6 +26,7 @@ markers = dc_motor: DCMotor tests solver: Solver tests transmission: Transmission tests + units: Units tests utils: Utilities functions tests genuine: Genuine method tests with no errors error: Error-raising tests