recovery-lab-math 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,87 @@
1
+ Metadata-Version: 2.4
2
+ Name: recovery-lab-math
3
+ Version: 1.0.0
4
+ Summary: Home recovery purchase vs gym drop-in, in the terminal: true heater draw, per-session supplies, amortized hardware, and the break-even frequency
5
+ Author: James Verlander
6
+ License: MIT
7
+ Project-URL: Homepage, https://hackedself.com/
8
+ Project-URL: Source Code, https://github.com/scrapertweeter3-prog/recovery-lab-math
9
+ Project-URL: Bug Tracker, https://github.com/scrapertweeter3-prog/recovery-lab-math/issues
10
+ Keywords: sauna,cold-plunge,recovery,biohacking,cost-per-session,break-even,longevity,cli
11
+ Classifier: Development Status :: 5 - Production/Stable
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: End Users/Desktop
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Topic :: Scientific/Engineering
18
+ Classifier: Topic :: Utilities
19
+ Requires-Python: >=3.8
20
+ Description-Content-Type: text/markdown
21
+
22
+ # Recoveryspend - Home recovery lab cost-per-session + break-even
23
+
24
+ A terminal calculator that answers one question: does the home unit beat
25
+ the gym-studio drop-in, and when? Costs any home recovery purchase (sauna,
26
+ cold plunge, red-light panel, massage gun, per-session amenity) against a
27
+ drop-in price, using the exact method from the HackedSelf article: true
28
+ energy draw from nameplate watts and your electric rate, filter/service
29
+ supplies per session, amortized purchase over a realistic session count,
30
+ and the weekly-frequency level where the home setup breaks even in years.
31
+
32
+ Python 3.8+, standard library only. No install, no network, nothing leaves
33
+ your machine.
34
+
35
+ ![recoveryspend terminal output](docs/terminal.png)
36
+
37
+ ## The method
38
+
39
+ - **Energy per session** = heater draw (W) / 1000 x hours x $/kWh
40
+ (preheat counted at half draw)
41
+ - **Supplies per session** = annual filter/treatment cost / sessions per year
42
+ - **Amortized hardware** = purchase price / sessions before it's paid off,
43
+ shown separately so you can watch it fall toward zero
44
+ - **Break even** = solve price / sessions-per-week for the frequency where
45
+ the home marginal cost equals the drop-in price, in weeks and years
46
+
47
+ The gym comparison uses the drop-in price only. If you already pay for a
48
+ membership that includes the amenity, the drop-in is $0 and the home unit
49
+ never wins - recoveryspend tells you that plainly.
50
+
51
+ ## Usage
52
+
53
+ ```
54
+ $ python3 recoveryspend.py --price 4500 --watts 6000 --kwh 0.17 \
55
+ --sessions-year 150 --dropin 35 --name "2-person infrared sauna"
56
+ == 2-person infrared sauna vs drop-in
57
+ Energy per session 5.78 (4.5 kW effective, 0.75 h, $0.170/kWh)
58
+ Supplies per session 0.50 ($75/yr filters, 150 sessions/yr)
59
+ Marginal cost/session 6.28
60
+ Drop-in price 35.00
61
+ Break even 2.6 sessions/wk (0.7 yr)
62
+
63
+ $ python3 recoveryspend.py --json ...
64
+ {"verdict": "home wins", "marginal": 6.28, "dropin": 35.0, ...}
65
+ ```
66
+
67
+ ## Also check
68
+
69
+ - `--preheat 1.0` full-draw preheat hour instead of the half-draw default
70
+ - `--life-sessions 500` amortization window (default 500 sessions)
71
+ - `--supplies-year 0` no filter/treatment line
72
+ - Exit code 1 when the home unit loses at the current frequency, so the
73
+ check can gate a purchase decision in a script
74
+
75
+ ## Honest limits
76
+
77
+ Energy math is nameplate math: real duty cycles, ambient temperature and
78
+ insulation move the true number, so treat the figure as the optimistic
79
+ floor and the break-even as the optimistic edge. Sessions-per-year is a
80
+ behavioral guess only you can make; the break-even output is exactly the
81
+ place that guess gets honest. No study effect sizes are cited anywhere in
82
+ this repo, because there are none here - this tool is dollars, not dose.
83
+
84
+ ## Related
85
+
86
+ The full protocol and the worked example behind this method:
87
+ https://hackedself.com/recovery/home-recovery-lab-cost-per-session/
@@ -0,0 +1,66 @@
1
+ # Recoveryspend - Home recovery lab cost-per-session + break-even
2
+
3
+ A terminal calculator that answers one question: does the home unit beat
4
+ the gym-studio drop-in, and when? Costs any home recovery purchase (sauna,
5
+ cold plunge, red-light panel, massage gun, per-session amenity) against a
6
+ drop-in price, using the exact method from the HackedSelf article: true
7
+ energy draw from nameplate watts and your electric rate, filter/service
8
+ supplies per session, amortized purchase over a realistic session count,
9
+ and the weekly-frequency level where the home setup breaks even in years.
10
+
11
+ Python 3.8+, standard library only. No install, no network, nothing leaves
12
+ your machine.
13
+
14
+ ![recoveryspend terminal output](docs/terminal.png)
15
+
16
+ ## The method
17
+
18
+ - **Energy per session** = heater draw (W) / 1000 x hours x $/kWh
19
+ (preheat counted at half draw)
20
+ - **Supplies per session** = annual filter/treatment cost / sessions per year
21
+ - **Amortized hardware** = purchase price / sessions before it's paid off,
22
+ shown separately so you can watch it fall toward zero
23
+ - **Break even** = solve price / sessions-per-week for the frequency where
24
+ the home marginal cost equals the drop-in price, in weeks and years
25
+
26
+ The gym comparison uses the drop-in price only. If you already pay for a
27
+ membership that includes the amenity, the drop-in is $0 and the home unit
28
+ never wins - recoveryspend tells you that plainly.
29
+
30
+ ## Usage
31
+
32
+ ```
33
+ $ python3 recoveryspend.py --price 4500 --watts 6000 --kwh 0.17 \
34
+ --sessions-year 150 --dropin 35 --name "2-person infrared sauna"
35
+ == 2-person infrared sauna vs drop-in
36
+ Energy per session 5.78 (4.5 kW effective, 0.75 h, $0.170/kWh)
37
+ Supplies per session 0.50 ($75/yr filters, 150 sessions/yr)
38
+ Marginal cost/session 6.28
39
+ Drop-in price 35.00
40
+ Break even 2.6 sessions/wk (0.7 yr)
41
+
42
+ $ python3 recoveryspend.py --json ...
43
+ {"verdict": "home wins", "marginal": 6.28, "dropin": 35.0, ...}
44
+ ```
45
+
46
+ ## Also check
47
+
48
+ - `--preheat 1.0` full-draw preheat hour instead of the half-draw default
49
+ - `--life-sessions 500` amortization window (default 500 sessions)
50
+ - `--supplies-year 0` no filter/treatment line
51
+ - Exit code 1 when the home unit loses at the current frequency, so the
52
+ check can gate a purchase decision in a script
53
+
54
+ ## Honest limits
55
+
56
+ Energy math is nameplate math: real duty cycles, ambient temperature and
57
+ insulation move the true number, so treat the figure as the optimistic
58
+ floor and the break-even as the optimistic edge. Sessions-per-year is a
59
+ behavioral guess only you can make; the break-even output is exactly the
60
+ place that guess gets honest. No study effect sizes are cited anywhere in
61
+ this repo, because there are none here - this tool is dollars, not dose.
62
+
63
+ ## Related
64
+
65
+ The full protocol and the worked example behind this method:
66
+ https://hackedself.com/recovery/home-recovery-lab-cost-per-session/
@@ -0,0 +1,34 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "recovery-lab-math"
7
+ version = "1.0.0"
8
+ description = "Home recovery purchase vs gym drop-in, in the terminal: true heater draw, per-session supplies, amortized hardware, and the break-even frequency"
9
+ readme = "README.md"
10
+ requires-python = ">=3.8"
11
+ license = {text = "MIT"}
12
+ authors = [{name = "James Verlander"}]
13
+ keywords = ["sauna", "cold-plunge", "recovery", "biohacking", "cost-per-session", "break-even", "longevity", "cli"]
14
+ classifiers = [
15
+ "Development Status :: 5 - Production/Stable",
16
+ "Environment :: Console",
17
+ "Intended Audience :: End Users/Desktop",
18
+ "License :: OSI Approved :: MIT License",
19
+ "Operating System :: OS Independent",
20
+ "Programming Language :: Python :: 3",
21
+ "Topic :: Scientific/Engineering",
22
+ "Topic :: Utilities",
23
+ ]
24
+
25
+ [project.urls]
26
+ Homepage = "https://hackedself.com/"
27
+ "Source Code" = "https://github.com/scrapertweeter3-prog/recovery-lab-math"
28
+ "Bug Tracker" = "https://github.com/scrapertweeter3-prog/recovery-lab-math/issues"
29
+
30
+ [project.scripts]
31
+ recoveryspend = "recoveryspend:main"
32
+
33
+ [tool.setuptools]
34
+ py-modules = ["recoveryspend"]
@@ -0,0 +1,87 @@
1
+ Metadata-Version: 2.4
2
+ Name: recovery-lab-math
3
+ Version: 1.0.0
4
+ Summary: Home recovery purchase vs gym drop-in, in the terminal: true heater draw, per-session supplies, amortized hardware, and the break-even frequency
5
+ Author: James Verlander
6
+ License: MIT
7
+ Project-URL: Homepage, https://hackedself.com/
8
+ Project-URL: Source Code, https://github.com/scrapertweeter3-prog/recovery-lab-math
9
+ Project-URL: Bug Tracker, https://github.com/scrapertweeter3-prog/recovery-lab-math/issues
10
+ Keywords: sauna,cold-plunge,recovery,biohacking,cost-per-session,break-even,longevity,cli
11
+ Classifier: Development Status :: 5 - Production/Stable
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: End Users/Desktop
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Topic :: Scientific/Engineering
18
+ Classifier: Topic :: Utilities
19
+ Requires-Python: >=3.8
20
+ Description-Content-Type: text/markdown
21
+
22
+ # Recoveryspend - Home recovery lab cost-per-session + break-even
23
+
24
+ A terminal calculator that answers one question: does the home unit beat
25
+ the gym-studio drop-in, and when? Costs any home recovery purchase (sauna,
26
+ cold plunge, red-light panel, massage gun, per-session amenity) against a
27
+ drop-in price, using the exact method from the HackedSelf article: true
28
+ energy draw from nameplate watts and your electric rate, filter/service
29
+ supplies per session, amortized purchase over a realistic session count,
30
+ and the weekly-frequency level where the home setup breaks even in years.
31
+
32
+ Python 3.8+, standard library only. No install, no network, nothing leaves
33
+ your machine.
34
+
35
+ ![recoveryspend terminal output](docs/terminal.png)
36
+
37
+ ## The method
38
+
39
+ - **Energy per session** = heater draw (W) / 1000 x hours x $/kWh
40
+ (preheat counted at half draw)
41
+ - **Supplies per session** = annual filter/treatment cost / sessions per year
42
+ - **Amortized hardware** = purchase price / sessions before it's paid off,
43
+ shown separately so you can watch it fall toward zero
44
+ - **Break even** = solve price / sessions-per-week for the frequency where
45
+ the home marginal cost equals the drop-in price, in weeks and years
46
+
47
+ The gym comparison uses the drop-in price only. If you already pay for a
48
+ membership that includes the amenity, the drop-in is $0 and the home unit
49
+ never wins - recoveryspend tells you that plainly.
50
+
51
+ ## Usage
52
+
53
+ ```
54
+ $ python3 recoveryspend.py --price 4500 --watts 6000 --kwh 0.17 \
55
+ --sessions-year 150 --dropin 35 --name "2-person infrared sauna"
56
+ == 2-person infrared sauna vs drop-in
57
+ Energy per session 5.78 (4.5 kW effective, 0.75 h, $0.170/kWh)
58
+ Supplies per session 0.50 ($75/yr filters, 150 sessions/yr)
59
+ Marginal cost/session 6.28
60
+ Drop-in price 35.00
61
+ Break even 2.6 sessions/wk (0.7 yr)
62
+
63
+ $ python3 recoveryspend.py --json ...
64
+ {"verdict": "home wins", "marginal": 6.28, "dropin": 35.0, ...}
65
+ ```
66
+
67
+ ## Also check
68
+
69
+ - `--preheat 1.0` full-draw preheat hour instead of the half-draw default
70
+ - `--life-sessions 500` amortization window (default 500 sessions)
71
+ - `--supplies-year 0` no filter/treatment line
72
+ - Exit code 1 when the home unit loses at the current frequency, so the
73
+ check can gate a purchase decision in a script
74
+
75
+ ## Honest limits
76
+
77
+ Energy math is nameplate math: real duty cycles, ambient temperature and
78
+ insulation move the true number, so treat the figure as the optimistic
79
+ floor and the break-even as the optimistic edge. Sessions-per-year is a
80
+ behavioral guess only you can make; the break-even output is exactly the
81
+ place that guess gets honest. No study effect sizes are cited anywhere in
82
+ this repo, because there are none here - this tool is dollars, not dose.
83
+
84
+ ## Related
85
+
86
+ The full protocol and the worked example behind this method:
87
+ https://hackedself.com/recovery/home-recovery-lab-cost-per-session/
@@ -0,0 +1,8 @@
1
+ README.md
2
+ pyproject.toml
3
+ recoveryspend.py
4
+ recovery_lab_math.egg-info/PKG-INFO
5
+ recovery_lab_math.egg-info/SOURCES.txt
6
+ recovery_lab_math.egg-info/dependency_links.txt
7
+ recovery_lab_math.egg-info/entry_points.txt
8
+ recovery_lab_math.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ recoveryspend = recoveryspend:main
@@ -0,0 +1,149 @@
1
+ #!/usr/bin/env python3
2
+ """recoveryspend: home recovery purchase vs drop-in price, per session.
3
+
4
+ Costs a home sauna / cold plunge / red-light panel against a studio drop-in
5
+ using nameplate energy math, per-session supplies, and amortized hardware,
6
+ then reports payoff time at your weekly frequency.
7
+
8
+ Stdlib only. Python 3.8+. No network.
9
+ """
10
+ import argparse
11
+ import json
12
+ import sys
13
+
14
+ AMORT_SESSIONS_DEFAULT = 500 # sessions before hardware is considered paid off
15
+
16
+
17
+ def money(x):
18
+ return round(x + 1e-9, 2)
19
+
20
+
21
+ def cost_model(price, watts, hours, preheat_frac, kwh, supplies_year,
22
+ sessions_year, life_sessions):
23
+ kw = watts / 1000.0
24
+ energy = money(kw * (hours + preheat_frac) * kwh)
25
+ supplies = money(supplies_year / sessions_year)
26
+ marginal = money(energy + supplies)
27
+ amortized = money(price / max(life_sessions, 1))
28
+ allin = money(marginal + amortized)
29
+ return {
30
+ "kw": round(kw, 2),
31
+ "hours_incl_preheat": round(hours + preheat_frac, 2),
32
+ "kwh_rate": kwh,
33
+ "energy_per_session": energy,
34
+ "supplies_per_session": supplies,
35
+ "marginal_per_session": marginal,
36
+ "amortized_per_session": amortized,
37
+ "allin_per_session": allin,
38
+ }
39
+
40
+
41
+ def payoff(price, marginal, dropin, freq, life_sessions):
42
+ """Weeks/years to pay off the purchase at `freq` sessions per week.
43
+
44
+ Returns (weeks, years, note). weeks is None when it never pays off.
45
+ """
46
+ gap = dropin - marginal
47
+ if dropin <= 0:
48
+ return None, None, "gym is free (membership includes it): home never wins"
49
+ if gap <= 0:
50
+ return None, None, "energy+supplies already exceed the drop-in price"
51
+ weeks = price / (gap * freq)
52
+ return round(weeks, 1), round(weeks / 52.0, 1), None
53
+
54
+
55
+ def verdict_for(m, dropin, freq, life_sessions):
56
+ """Honest verdict: does the purchase pay off inside the amortization
57
+ window? That happens iff dropin >= marginal + amortized (the all-in)."""
58
+ if dropin <= 0:
59
+ return "keep the membership: the amenity is already free", 0
60
+ if m["marginal_per_session"] >= dropin:
61
+ return "home loses: running cost alone exceeds the drop-in", 1
62
+ if m["allin_per_session"] <= dropin:
63
+ return "home wins", 0
64
+ return ("never amortizes in %d sessions: ownership play only"
65
+ % life_sessions), 0
66
+
67
+
68
+ def main(argv=None):
69
+ p = argparse.ArgumentParser(
70
+ description="Home recovery purchase vs drop-in: cost per session "
71
+ "and payoff time.")
72
+ p.add_argument("--price", type=float, required=True,
73
+ help="purchase price of the home unit ($)")
74
+ p.add_argument("--watts", type=float, required=True,
75
+ help="nameplate heater draw (W)")
76
+ p.add_argument("--hours", type=float, default=0.75,
77
+ help="session length in hours (default 0.75)")
78
+ p.add_argument("--preheat", type=float, default=0.5,
79
+ help="preheat draw in full-draw hours (default 0.5)")
80
+ p.add_argument("--kwh", type=float, required=True,
81
+ help="your electric rate ($/kWh)")
82
+ p.add_argument("--supplies-year", type=float, default=0.0,
83
+ help="annual filter/treatment cost ($)")
84
+ p.add_argument("--sessions-year", type=int, default=100,
85
+ help="expected sessions per year (supplies divisor)")
86
+ p.add_argument("--life-sessions", type=int,
87
+ default=AMORT_SESSIONS_DEFAULT,
88
+ help="sessions over which to amortize the purchase")
89
+ p.add_argument("--dropin", type=float, required=True,
90
+ help="studio/gym drop-in price per session ($)")
91
+ p.add_argument("--freq", type=float, default=1.0,
92
+ help="planned sessions per week for the payoff clock "
93
+ "(default 1.0)")
94
+ p.add_argument("--name", default="home unit", help="label for output")
95
+ p.add_argument("--json", action="store_true", help="JSON output")
96
+ args = p.parse_args(argv)
97
+
98
+ if args.sessions_year <= 0:
99
+ p.error("--sessions-year must be positive")
100
+ if args.life_sessions <= 0:
101
+ p.error("--life-sessions must be positive")
102
+ if args.freq <= 0:
103
+ p.error("--freq must be positive")
104
+
105
+ m = cost_model(args.price, args.watts, args.hours, args.preheat,
106
+ args.kwh, args.supplies_year, args.sessions_year,
107
+ args.life_sessions)
108
+ weeks, years, note = payoff(args.price, m["marginal_per_session"],
109
+ args.dropin, args.freq, args.life_sessions)
110
+ verdict, rc = verdict_for(m, args.dropin, args.freq, args.life_sessions)
111
+
112
+ out = {
113
+ "name": args.name,
114
+ "dropin": args.dropin,
115
+ "freq_per_week": args.freq,
116
+ **m,
117
+ "payoff_weeks_at_freq": weeks,
118
+ "payoff_years_at_freq": years,
119
+ "note": note,
120
+ "verdict": verdict,
121
+ }
122
+
123
+ if args.json:
124
+ print(json.dumps(out, indent=2))
125
+ return rc
126
+
127
+ print("== %s vs drop-in" % args.name)
128
+ print("Energy per session %6.2f (%.2f kW, %.2f h incl. preheat, "
129
+ "$%.3f/kWh)" % (m["energy_per_session"], m["kw"],
130
+ m["hours_incl_preheat"], args.kwh))
131
+ print("Supplies per session %6.2f ($%.0f/yr, %d sessions/yr)"
132
+ % (m["supplies_per_session"], args.supplies_year,
133
+ args.sessions_year))
134
+ print("Marginal cost/session %6.2f" % m["marginal_per_session"])
135
+ print("Amortized/session %6.2f (over %d sessions)"
136
+ % (m["amortized_per_session"], args.life_sessions))
137
+ print("All-in/session %6.2f" % m["allin_per_session"])
138
+ print("Drop-in price %6.2f" % args.dropin)
139
+ if note:
140
+ print("Payoff %s" % note)
141
+ else:
142
+ print("Payoff at %.1f/wk %6.1f wk (%.1f yr)"
143
+ % (args.freq, weeks, years))
144
+ print("Verdict: %s" % verdict)
145
+ return rc
146
+
147
+
148
+ if __name__ == "__main__":
149
+ sys.exit(main())
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+