A simple gym calculator: estimate a 1RM from a set, work out which plates to load, and score relative strength (Wilks/DOTS/IPF GL). Pure Python stdlib.
Project description
liftmath
A simple gym calculator. Three things you actually reach for mid-workout, done properly instead of eyeballed.
Three tools:
- 1RM: estimate a one-rep max from any set you just did.
- Plates: what to hang on the bar for a target weight.
- Strength score: Wilks, DOTS, and IPF GL points, so you can compare across bodyweights.
That's it. It used to do a lot more; it does less now, on purpose. Every number traces back to a
named formula, and you can read the whole thing in a sitting. There's also a small lb/kg
converter (convert) bolted on, since plates and strength score already needed exact unit
conversion internally - it's a utility, not a fourth tool.
The fastest way in is the web app: nothing to install, works on your phone at the gym, offline, and the barbell loads itself as you type: https://munzzyy.github.io/liftmath/. Everything below is the same math for people who'd rather script it.
Pure Python standard library. No dependencies, no network calls, no accounts. Use it as a library you import or a command you run.
Install
pip install liftmath
Or from source:
git clone https://github.com/munzzyy/liftmath
cd liftmath
pip install -e .
Requires Python 3.10+. Nothing else.
Command line
Pass --json (before or after the subcommand) to any command for machine-readable output instead
of text. Loads are unit-agnostic; pass --unit kg or --unit lb (default lb).
1RM
Estimate a one-rep max from a weight × reps set. No single formula is most accurate across every rep range, so it runs six published equations and reports the median consensus instead of picking one, dropping the curvilinear formulas past 12 reps where they're known to drift.
$ liftmath 1rm --weight 225 --reps 5
Estimated 1RM from 225lb x 5 reps
No single formula is most accurate across every rep range, so this runs six and
takes the CONSENSUS (median) instead of picking one. Sorted by value, not accuracy.
----------------------------------------------
Brzycki 253.1lb
O'Conner 253.1lb
Lander 255.8lb
Epley 262.5lb
Lombardi 264.3lb
Mayhew 267.8lb
----------------------------------------------
CONSENSUS 259.2lb (median; range 253.1-267.8)
Plates
Which plates to load per side for a target barbell weight, largest first.
$ liftmath plates --target 315
Load 315lb on a 45lb bar:
per side (135lb): 3x45
Home gym or travel kit with a finite set of plates? Pass --inventory with the exact per-side
counts you have and it solves against that instead of assuming an unlimited supply (an exhaustive
search, since greedy picks aren't optimal once supply runs out):
$ liftmath plates --target 405 --inventory 45x3,25x1,10x1
Load 405lb on a 45lb bar (from your inventory):
per side (180lb): 3x45, 1x25, 1x10
[!] can't make it exactly with this inventory - short 10lb/side.
nearest achievable below: 385lb
There are also --bar, --plates (custom denominations), and --preset (womens, metric-no-45)
flags. Run liftmath plates --help.
Strength score
Relative-strength scores from a total, bodyweight, and sex, so a lighter lifter and a heavier one can be compared on one scale.
$ liftmath standards --total 1200 --bodyweight 200 --sex male
Relative-strength scores - 1200lb total @ 200lb bodyweight (male):
Each score below lets you compare lifters across bodyweights on one scale - Wilks
and DOTS are two different curve-fit formulas for it; IPF GL is the federation's own.
----------------------------------------
Wilks (2020) 415.78
Wilks (original) 346.09
DOTS 350.55
IPF GL points 72.08
----------------------------------------
All four are fit to different samples and disagree slightly, especially at the extremes of the bodyweight range. Treat them as independent opinions, not one ground truth.
Convert
A plain lb/kg conversion, using the exact international avoirdupois pound (1 lb = 0.45359237 kg), not a rounded approximation.
$ liftmath convert --weight 225 --unit lb
225lb = 102.06kg
(exact: 1lb = 0.45359237kg, the international avoirdupois pound)
As a library
Every command is a thin wrapper around a plain function that returns a dataclass, so you can use the math directly:
from liftmath import estimate_one_rm, load_plates, score
est = estimate_one_rm(225, 5, unit="lb")
print(est.consensus) # 259.17...
plates = load_plates(245, unit="lb")
print(plates.plates) # [(45, 2), (10, 1)]
s = score(500, 90, "male") # total kg, bodyweight kg, sex
print(s.wilks, s.dots, s.ipf_gl)
The full public API:
from liftmath import (
estimate_one_rm, # six-formula 1RM consensus
load_plates, load_plates_from_inventory, # plate loading (unlimited / finite)
score, # all four scores at once
wilks_score, wilks_original_score, # Wilks 2020 / original
dots_score, ipf_gl_points, # DOTS / IPF GL points
lbs_to_kg, kg_to_lbs, convert_weight, # exact lb/kg conversion
to_dict, to_json, # serialize any result
)
Every result is a plain dataclass. To serialize one (an API response, a log line, whatever) use
to_dict/to_json rather than hand-rolling dataclasses.asdict(); they also carry over read-only
properties like is_exact and exact that asdict() alone would drop:
from liftmath import estimate_one_rm, to_json
print(to_json(estimate_one_rm(225, 5, unit="lb")))
See the module docstrings in src/liftmath/ for the details: onerm.py, plates.py, standards.py,
convert.py.
Where the numbers come from
The 1RM formulas are Epley (1985), Brzycki (1993), Lombardi (1989), O'Conner et al. (1989),
Lander (1985), and Mayhew et al. (1992). Which of these actually degrades worse at high rep counts
is genuinely contested in the secondary literature, and onerm.py's docstring documents that
openly rather than asserting an uncited fix.
The plate solver is a plain greedy largest-first pass for the unlimited case; the finite-inventory
solver does an exhaustive bounded search instead, because greedy isn't optimal once you can run out
of a plate (one 25 + one 25 beats grabbing the single 45 you own). plates.py explains why.
The strength scores come from the IPF's own published GL coefficients (May 2020), the original and
2020-revised Wilks formulas, and the DOTS formula introduced in 2019. These are competition scoring
conventions: the actual formulas federations use, fit by regression to real competition samples,
not "evidence" in the causal sense. The Wilks and DOTS coefficient tables are cross-checked against
OpenPowerlifting's implementations; IPF GL came straight from the IPF's own coefficients PDF.
standards.py has the full citations and a note on when the IPF table is due to refresh.
The lb/kg conversion uses the exact international avoirdupois pound (1 lb = 0.45359237 kg, fixed
by the 1959 international yard-and-pound agreement), not a rounded approximation. It's the same
factor standards.py was already converting with internally for its --unit lb handling.
What this is not
This computes training math. It doesn't design your program, pick your exercises, or replace a coach who can watch you lift. Informational and educational only, not medical advice.
Tests
pip install -e ".[dev]" # or: pip install pytest ruff
pytest
ruff check .
Every formula is pinned against hand-checked reference values in tests/. The web app's JavaScript
math is parity-tested against the Python reference so the two never drift.
Contributing
Questions and contributions are welcome. See CONTRIBUTING.md.
License
Prosperity Public License 3.0.0: free for noncommercial use. Commercial use gets a
30-day free trial, then requires a paid license. See LICENSE for the full terms.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file liftmath-2.1.0.tar.gz.
File metadata
- Download URL: liftmath-2.1.0.tar.gz
- Upload date:
- Size: 29.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1b65c45f8f2c31e81cb1469129b03edbc01b877d8e595ddf431b1eecdd650bd9
|
|
| MD5 |
45d6d725169eafba543b8472655c05ad
|
|
| BLAKE2b-256 |
203c2b84d1ab29d784c4d542b3d96dd0e782e643c1228e7b991e6792a484a6dc
|
Provenance
The following attestation bundles were made for liftmath-2.1.0.tar.gz:
Publisher:
release.yml on munzzyy/liftmath
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
liftmath-2.1.0.tar.gz -
Subject digest:
1b65c45f8f2c31e81cb1469129b03edbc01b877d8e595ddf431b1eecdd650bd9 - Sigstore transparency entry: 2129852518
- Sigstore integration time:
-
Permalink:
munzzyy/liftmath@b654ac2d9a57c2512d7892ab474f6c340338ed01 -
Branch / Tag:
refs/tags/v2.1.0 - Owner: https://github.com/munzzyy
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@b654ac2d9a57c2512d7892ab474f6c340338ed01 -
Trigger Event:
release
-
Statement type:
File details
Details for the file liftmath-2.1.0-py3-none-any.whl.
File metadata
- Download URL: liftmath-2.1.0-py3-none-any.whl
- Upload date:
- Size: 24.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
eb5405551d5ac908b427f043703337cbd08b7cee2a9931cd94f12f4948ab447f
|
|
| MD5 |
d5132c0321969ec17e81ed1afff3f757
|
|
| BLAKE2b-256 |
634d3e9e62df84bca7c04a93cbd1ccd8699fba160d4d539d1e68ea9bc5482093
|
Provenance
The following attestation bundles were made for liftmath-2.1.0-py3-none-any.whl:
Publisher:
release.yml on munzzyy/liftmath
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
liftmath-2.1.0-py3-none-any.whl -
Subject digest:
eb5405551d5ac908b427f043703337cbd08b7cee2a9931cd94f12f4948ab447f - Sigstore transparency entry: 2129852575
- Sigstore integration time:
-
Permalink:
munzzyy/liftmath@b654ac2d9a57c2512d7892ab474f6c340338ed01 -
Branch / Tag:
refs/tags/v2.1.0 - Owner: https://github.com/munzzyy
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@b654ac2d9a57c2512d7892ab474f6c340338ed01 -
Trigger Event:
release
-
Statement type: