{"id":38700990,"url":"https://github.com/djpetti/pyunits","last_synced_at":"2026-01-17T10:48:31.199Z","repository":{"id":57457432,"uuid":"221111865","full_name":"djpetti/pyunits","owner":"djpetti","description":"Unit-aware Python with dimensional analysis support.","archived":false,"fork":false,"pushed_at":"2023-08-03T16:49:47.000Z","size":134,"stargazers_count":0,"open_issues_count":6,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-10-27T02:45:46.977Z","etag":null,"topics":["dimensional-analysis","numpy","python36","python37","python38","scientific-computing","unit-conversions"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/djpetti.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-11-12T02:24:22.000Z","updated_at":"2025-10-15T08:23:15.000Z","dependencies_parsed_at":"2024-11-15T03:11:45.547Z","dependency_job_id":"781b8d49-11be-4531-82e7-9814ef3697de","html_url":"https://github.com/djpetti/pyunits","commit_stats":{"total_commits":46,"total_committers":1,"mean_commits":46.0,"dds":0.0,"last_synced_commit":"a2d3ed5dc30f8e20432c0ca8e72a34288d720202"},"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/djpetti/pyunits","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/djpetti%2Fpyunits","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/djpetti%2Fpyunits/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/djpetti%2Fpyunits/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/djpetti%2Fpyunits/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/djpetti","download_url":"https://codeload.github.com/djpetti/pyunits/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/djpetti%2Fpyunits/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28506593,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-17T10:25:30.148Z","status":"ssl_error","status_checked_at":"2026-01-17T10:25:29.718Z","response_time":85,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dimensional-analysis","numpy","python36","python37","python38","scientific-computing","unit-conversions"],"created_at":"2026-01-17T10:48:30.503Z","updated_at":"2026-01-17T10:48:31.187Z","avatar_url":"https://github.com/djpetti.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# PyUnits\n*Use Units in your Python Code*\n\n\u003e **WARNING**: This code is still under development. Unless you are invigorated\n\u003e by the thought of an extended correspondence with me, I would recommend\n\u003e **against** its use for any serious *(read: paid)* project.\n\nHere's something we've all done before:\n```python\nlength1 = 42 # Meters\n \n# ... 500 lines later\n\nlength2 = 12 # Inches\nresult = length1 + length2 # Oops\n```\n\n## Installation\n\n```bash\npip install python-pyunits\n```\n\n## Units are a pain.\n\nAnd any time we're computing with some sort of physical quantities, we almost\ncertainly have to take them into account. Whenever I read (or write) this kind\nof code, I am amazed at how many subtle bugs crop up due to missing or\nincorrect unit conversions.\n\n## WolframAlpha can do it, so why can't Python?\n\nWhy indeed? We're used to scientific software and even search engines auto-magically\nhandling unit conversions and dimensional analysis for us. Python is used for a\nlot of scientific applications, but sadly, this feature isn't built-in to the\nlanguage.\n\nLadies and gentlemen, I give you PyUnits:\n\n```python\nfrom examples import example_units as eu\n\nlength1 = eu.Meters(42)\nlength2 = eu.Inches(12)\n\nprint(length1 + length2) # Prints \"42.3048 m\"\n```\n\nOkay. So maybe you're not turned on by unit conversions. I can understand that.\nBut guess what? PyUnits also understands dimensional analysis. In fact, it\nunderstands it at least as well as [Randall Munroe](https://en.wikipedia.org/wiki/Randall_Munroe)\ndoes:\n\n![Dimensional Analysis](https://imgs.xkcd.com/comics/dimensional_analysis.png)\n\n```python\nfrom examples import example_units as eu\nfrom pyunits.compound_units import Mul, Div\n\nCubicMeters = Mul(eu.Meters, Mul(eu.Meters, eu.Meters))\nCubicIn = Mul(eu.Inches, Mul(eu.Inches, eu.Inches))\nPascals = Div(eu.Joules, CubicMeters)\nMilesPerIn3 = Div(eu.Miles, CubicIn)\n\nplank_energy = eu.Joules(1.956E9)\ncore_pressure = Pascals(3.6E11)\n# Technically, this is the Prius C gas mileage. It seems that Priuses\n# (Priui?) have improved somewhat since the comic was published...\n# Note also that we don't really have the ability to do mpg directly yet,\n# so we do mi / in^3 and divide by 231. (1 gal = 231 in^3)\nprius_mileage = MilesPerIn3(46 / 231)\nchannel_width = eu.Kilometers(33.8)\n\npi = plank_energy / core_pressure * prius_mileage / channel_width\n\nprint(pi) # Prints 3.14\n```\n\n\"But wait\", I hear you cry. \"That's a totally contrived example!\" And it may\nwell be. So let's try something a little more... practical:\n\n![Fermirotica](https://imgs.xkcd.com/comics/fermirotica.png)\n\n```python\nimport math\n\nfrom examples import example_units as eu\nfrom pyunits.compound_units import Mul\n\nSquareMiles = Mul(eu.Miles, eu.Miles)\nSquareMeters = Mul(eu.Meters, eu.Meters)\n\npopulation_density = 18600 / SquareMiles(1.0)\nsex_frequency = 80 / eu.Years(1.0)\nsex_duration = eu.Minutes(30)\n\ndenom_value = math.pi * population_density * sex_frequency * sex_duration\nfraction = 2.0 / denom_value\n\n# Ensure result is in square meters.\nfraction = SquareMeters(fraction)\n\n# Square root has to be done outside of PyUnits, since we don't support that\n# (yet).\nsex_radius = math.sqrt(fraction.raw)\nprint(sex_radius) # Prints \"139.33\", the correct result for these parameters.\n```\n\nAs you can see, PyUnits is suitable for Serious Scientific Work.\n\n# Cookbook\n\nI have a colleague who likes nothing better than copying and pasting code from\nREADME files. He refers to this type of file as a \"cookbook\". Therefore, I will\nbe providing a similar facility in this section.\n\n## Building a Unit Library\n\nSo far, all the examples in this document have used the example unit library\nprovided in this repository under `examples/example_units.py`. That's all well\nand good, but if you're using PyUnits for real, the first thing you're probably\ngoing to want to do is create your own unit library:\n\n```python\nfrom pyunits.unit import StandardUnit, Unit\nfrom pyunits.unit_type import UnitType\n\n\nclass Length(UnitType):\n    \"\"\"\n    Type for length units.\n    \"\"\"\n\n@Length.decorate\nclass Meters(StandardUnit):\n    \"\"\"\n    A meters unit.\n    \"\"\"\n\n    @property\n    def name(self) -\u003e str:\n        \"\"\"\n        See superclass for documentation.\n        \"\"\"\n        return \"m\"\n\n\n@Length.decorate\nclass Centimeters(Unit):\n    \"\"\"\n    A centimeters unit.\n    \"\"\"\n\n    def _from_standard(self, standard_value: StandardUnit) -\u003e None:\n        \"\"\"\n        See superclass for documentation.\n        \"\"\"\n        # Convert from meters.\n        self._set_raw(standard_value.raw * 100)\n\n    def to_standard(self) -\u003e Meters:\n        \"\"\"\n        See superclass for documentation.\n        \"\"\"\n        # Convert to meters.\n        return Meters(self.raw / 100)\n\n    @property\n    def name(self) -\u003e str:\n        \"\"\"\n        See superclass for documentation.\n        \"\"\"\n        return \"cm\"\n```\n\nFundamentally, PyUnits has two types of objects that the user needs to be aware\nof: `Unit` and `UnitType`. The former is easy: a `Unit` instance simply\nrepresents a value with a specific unit. The second is a little harder to\nexplain.\n\nIntuitively, some units can be trivially converted to each-other, eg. Meters\nand Inches, and some can't be, eg. Meters and Seconds. The `UnitType` class\nexists in order to allow PyUnits to understand these relationships. When using\nPyUnits, every `Unit` subclass must be decorated with an appropriate `UnitType`\nsubclass. Two units that are marked with the same `UnitType` can be trivially\nconverted to each-other. Two units that aren't can't easily be converted, and\nPyUnits with raise an error if you try to do so.\n\n### Standard Units\n\nFor all the units of a particular `UnitType`, PyUnits expects the user to choose\na \"standard unit\". In practice, which unit you use as the standard one doesn't\nreally matter, as long as you can tell PyUnits how to convert from every other\nunit to the standard one and vice-versa.\n\nIn practice, this is done by overriding two methods:\n - The `_from_standard()` method takes an instance of the standard unit and\n   initializes this `Unit` instance appropriately with its converted value.\n - The `to_standard()` method returns an instance of the standard unit with\n   an equivalent value to this one.\n   \nOnce we have that set up, PyUnits can convert implicitly between all the units\nof this `UnitType`:\n\n```python\nmeters = Meters(10)\ncentimeters = Centimeters(meters)\n\nprint(centimeters)  # prints \"1000 cm\"\n```\n\nWhich unit is considered to be the standard one is defined by which one\ninherits from the `StandardUnit` class. (This is `Meters` in the above example.)\n\n### Pretty-Printing\n\nPyUnits has (currently limited) support for pretty-printing unit values. This\nis utilized by overriding the `name` property, as seen in the example above.\nThis property should return a suffix that will be appended to the unit value\nwhen printing.\n\n### Numpy Integration\n\nPyUnits can essentially be thought of as a wrapper around Numpy. That is\nbecause `Unit` subclasses actually store and manipulate Numpy arrays internally:\n\n```python\nimport numpy as np\n\nfrom examples import example_units as eu\n\nsecs = eu.Seconds(np.array([1, 2, 3]))\nprint(secs)  # prints \"[1 2 3] s\"\n```\n\nYou can access the raw Numpy value of a unit using the `raw` property:\n\n```python\nprint(secs.raw)  # print \"[1 2 3]\"\n```\n\n## Dimensional Analysis\n\nPyUnits is generally clever about unit operations:\n\n```python\nfrom examples import example_units as eu\n\nmeters = eu.Meters(2)\nseconds = eu.Seconds(4)\n\nprint(meters / seconds)  # prints 0.5 m\\n-\\ns\n```\n\nAs can be seen in the earlier examples, it will even go so far as to\nauto-simplify the results of multiplication and division operations.\n\nAlso, compound unit types can be created manually:\n\n```python\nfrom examples import example_units as eu\nfrom pyunits.compound_units import Mul, Div\n\nWatts = Div(eu.Joules, eu.Seconds)\nSquareMeters = Mul(eu.Meters, eu.Meters)\n\n# These can then be used like normal units.\nwatts = Watts(10)\nsquare_meters = SquareMeters(50)\n```\n\n### Unitless Values\n\nPyUnits has the special concept of \"Unitless\" values, which, paradoxically,\ncan be used like a `Unit` in many cases. These crop up most often when doing\ndivision.\n\n```python\nfrom examples import example_units as eu\n\nmeters1 = eu.Meters(10)\nmeters2 = eu.Meters(5)\n\nprint(meters1 / meters2)  # prints \"2\"\n```\n\nThe result of this division is an instance of the class `Unitless`. This class\ncan also be used directly in order to represent concepts such as \"inverse meters\".\n\n```python\nfrom examples import example_units as eu\nfrom pyunits.compound_units import Div\nfrom pyunits.unitless import Unitless\n\ninverse_meters = Div(Unitless, eu.Meters)\n```\n\nPyUnits will refuse to implicitly convert `Unitless` instances, however. If\nyou want to use this as a value of a unit, you have to explicitly take the raw\nvalue:\n\n\n```python\nfrom examples import example_units as eu\nfrom pyunits.unitless import Unitless\n\nunitless = Unitless(5)\nmeters = eu.Meters(unitless)  # Error!\n\n# The correct way...\nmeters = eu.Meters(unitless.raw)\n```\n\nThis is an explicit design choice that was made to avoid cases where values\nthat did not have units could \"magically\" acquire them.\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdjpetti%2Fpyunits","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdjpetti%2Fpyunits","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdjpetti%2Fpyunits/lists"}