Development¤
$ git clone https://github.com/abc8747/isqx.git
$ cd isqx
$ uv venv
$ uv sync --dev
We use utility scripts under scripts/
to lint, format and check for
typing issues. Dependencies are automatically managed by uv
.
$ ./scripts/make.py
Usage: make.py [OPTIONS] COMMAND [ARGS]...
╭─ Commands ───────────────────────────────────────────────────────────────────╮
│ copy-docs-file Copy a file from `path_in` to `path_out` with warnings. │
│ check │
│ check-katex Highlight underscores in katex │
│ fix │
╰──────────────────────────────────────────────────────────────────────────────╯
$ ./scripts/wikidata.py
Usage: wikidata.py [OPTIONS] COMMAND [ARGS]...
╭─ Commands ───────────────────────────────────────────────────────────────────╮
│ fetch-json Execute a SPARQL query from a file and save the raw │
│ response as JSON. │
│ quantities-to-parquet Organise the raw quantities JSON response in a │
│ parquet file. │
│ quantities-to-md Organise the quantities parquet file in a │
│ human-readable markdown file. │
╰──────────────────────────────────────────────────────────────────────────────╯
To build and serve the documentation:
$ uv run mkdocs serve
Note that this loads in some custom extensions under src/isqx/mkdocs
and
overwrites docs/index.md
with the one from the top level README.md
.
Defining units and quantity kinds.¤
The ISO/IEC 80000 (iso80000.py
) contains many useful core objects and is
considered complete. If you would like to add new definitions for your own
subfield, you should place them in two files:
- Core definitions (
src/isqx/{{domain}}.py
): this is where runtime objects likeQtyKind
,BaseUnit
etc. are defined. - Descriptive Details (
src/isqx/details/{{domain}}.py
): this is where non-essential metadata likeWikidata
,Symbol
andEquation
etc. are defined.
Units and constants¤
First, define the essential runtime objects.
- use
SCREAMING_SNAKE_CASE
- for constants, prefer using stdlib
decimal.Decimal
andfractions.Fraction
. downstream users are responsible for casting to float if they wish.
from decimal import Decimal
from isqx import BaseDimension, BaseUnit
# base units
DIM_TIME = BaseDimension("T")
S = BaseUnit(DIM_TIME, "second")
# derived units
N = (KG * M * S**-2).alias("newton", allow_prefix=True)
# scaled units
FT = (Decimal("0.3048") * M).alias("foot")
# constants
from isqx import StdUncertainty # optional
from typing import Annotated
G0: Annotated[Decimal, M * S**-2, StdUncertainty("12")] = Decimal("9.8065")
# do not do this:
G0 = isqx.Quantity(9.8065, M * S**-2)
Quantity kinds¤
from isqx import QtyKind
# must be defined with coherent SI units (i.e. not scaled)
# it can have optional tags.
ENERGY = QtyKind(J)
# add a tag: kinetic energy can be thought of a "subclass" of length.
KINETIC_ENERGY = LENGTH["kinetic"]
# alternatively, if it doesn't make sense to "subclass".
WORK_DONE = QtyKind(J, ("work_done",))
from isqx.usc import BTU
ENERGY_BTU = QtyKind(BTU) # BAD!
Defining details¤
Details are entirely optional.
from isqx.definitions import _iso80000
MECHANICS: Details = {
# keys should be direct object references
_iso80000.MASS: (
# values can be a tuple of detail objects or just a bare object
# it is not required to specify the equation if it does not have one.
Wikidata("Q11423"),
Symbol(r"m")
),
_iso80000.FORCE: (
Wikidata("Q11402"),
Equation(
r"\boldsymbol{F} = m\boldsymbol{a}",
{
r"\boldsymbol{F}": SELF,
"m": _iso80000.MASS,
r"\boldsymbol{a}": _iso80000.ACCELERATION,
},
),
),
}
Equations and the where clause¤
- use the KaTeX subset (no macros)
- Vectors: use
\boldsymbol{...}
for vector quantities like force, velocity - use
\mathrm{...}
for subscripts (where applicable) - use
\text{...}
for subscripts that are descriptive words.
# good
r"p = p_\text{total} - p_\text{static}"
r"(\eta_\text{th})_\text{max}"
# bad
r"p = p_{total} - p_{static}"
r"(\eta_{th})_{max}"
The where clause connects symbols in the KaTeX string to their meanings.
from isqx import SELF
# good
_iso80000.PHASE_SPEED: (
Equation(
r"c = \frac{\omega}{k}",
{
"c": SELF, # special marker to refer to the quantity being defined
...
},
assumptions={...}
),
Symbol("v"), # alternative symbols are fine
# do not add redundant `Symbol("c")`
),
Always reference other quantities where possible. If it is awkward to define a formal definition (e.g. "around some axis of rotation"), use a fragments of strings.
# good
{"m": _iso80000.MASS}
# bad
{"m": "mass"}
# do not create objects inline: mkdocs will be unable to generate a reference
{"m": _iso80000.MASS["body"]}
Symbols¤
Use it to define alternative symbols, or when a quantity has no defining equation.
_iso80000.MOLE_FRACTION: (
# ...
Symbol(
r"y_\mathrm{X}",
where={r"\mathrm{X}": "Substance"},
remarks="for gaseous mixtures", # provide context on when it should be used
),
)