Skip to content

Commit d1049f1

Browse files
committed
Added support for percentages
1 parent 1c6c51b commit d1049f1

File tree

3 files changed

+71
-9
lines changed

3 files changed

+71
-9
lines changed

ipycalc/calc.py

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,13 @@
6969
kPa = ureg.kilopascal
7070
MPa = ureg.megapascal
7171
GPa = ureg.gigapascal
72+
percent = ureg.percent
73+
pct = ureg.pct
7274

7375
unit_list = ['inch', 'feet', 'ft', 'mi', 'ozf', 'lbf', 'lbm', 'kip', 'ton', 'tonf', 'plf', 'klf', 'psi', 'psf',
7476
'ksi', 'ksf', 'pcf', 'kcf', 'lbin', 'lbft', 'kipin', 'kipft', 'kin', 'kft', 'mph',
7577
'rpm', 'Hz', 'deg', 'rad', 'sec', 'minute', 'hr', 'gal', 'degF', 'degC', 'mm', 'cm', 'm', 'km', 'N', 'kN', 'kgf', 'tonne', 'tonnef', 'Pa',
76-
'kPa', 'MPa', 'GPa']
78+
'kPa', 'MPa', 'GPa', 'percent', 'pct']
7779

7880
#%%
7981
@register_cell_magic
@@ -167,6 +169,8 @@ def sync_namespaces(local_ns):
167169
local_ns['GPa'] = ureg.gigapascal
168170
local_ns['tonne'] = ureg.tonne
169171
local_ns['tonnef'] = ureg.tonnef
172+
local_ns['percent'] = ureg.percent
173+
local_ns['pct'] = ureg.pct
170174

171175
# Provide the IPython console with access to the `funit` method
172176
local_ns['funit'] = funit
@@ -209,6 +213,10 @@ def process_line(calc_line, local_ns):
209213
equation = equation.replace('^prime', '_prime')
210214
equation = equation.replace('^', '**')
211215
equation = equation.replace('lambda', 'lamb')
216+
217+
# Convert percentage symbols to percent units
218+
# Match % symbols that follow numbers/variables (but not in strings)
219+
equation = re.sub(r'(\d+(?:\.\d+)?)\s*%', r'\1*percent', equation)
212220

213221
# Resolve prime symbols in the variable before we compare it to the equation, which has already had them resolved
214222
variable = variable.replace('^prime', '_prime')
@@ -479,15 +487,20 @@ def replace_string(match):
479487

480488
# Provide a space before any units
481489
text = text.replace('*inch', ' \\ in')
490+
491+
# Handle percent specially - use % symbol instead of word
492+
text = text.replace('*percent', '\\%')
493+
text = text.replace('*pct', '\\%')
482494
for unit in unit_list:
483-
text = text.replace('*' + unit, ' \\ ' + unit)
495+
if unit not in ['percent', 'pct']: # Skip percent as we already handled it
496+
text = text.replace('*' + unit, ' \\ ' + unit)
484497

485498
# Replace multiplication symbols in front of numbers with a multiplication dot
486499
for i in range(10):
487500
text = text.replace('*' + str(i), '\\cdot{}' + str(i))
488501

489-
# Remove all other multiplication symbols
490-
text = text.replace('*', '')
502+
# Convert remaining multiplication symbols to centered dots for better readability
503+
text = text.replace('*', '\\cdot{}')
491504

492505
# Return the Latex text
493506
return text
@@ -688,6 +701,9 @@ def funit(value, precision=None):
688701
latex_value = str(round(value, precision))
689702
else:
690703
latex_value = str(value)
704+
705+
# Replace pct unit with % symbol early
706+
latex_value = latex_value.replace('pct', '%')
691707

692708
# Step through each character in the value
693709
for i, char in enumerate(latex_value):
@@ -700,7 +716,10 @@ def funit(value, precision=None):
700716

701717
# The round function tags on a .0 after it rounds floats. It doesn't do it to integers. Correct this.
702718
if precision == 0:
703-
latex_value = latex_value.replace('.0', '')
719+
latex_value = latex_value.replace('.0', '')
720+
721+
# Escape the % symbol for LaTeX
722+
latex_value = latex_value.replace('%', '\\%')
704723

705724
# Return the formatted value
706725
return latex_value

ipycalc/ipycalc_en.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,9 @@ count = []
155155
# Conversion factors are exact (except when noted),
156156
# although floating-point conversion may introduce inaccuracies
157157

158+
# Dimensionless
159+
percent = 0.01 = pct
160+
158161
# Angle
159162
turn = 2 * π * radian = _ = circle
160163
cycle = 1 # Dimensionless count for frequency (1 cycle = 1 complete oscillation)

test_notebook.ipynb

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"cells": [
33
{
44
"cell_type": "code",
5-
"execution_count": 10,
5+
"execution_count": 1,
66
"id": "056b8594-3f8a-4fd3-89f5-ee9fdf999752",
77
"metadata": {
88
"editable": true,
@@ -20,7 +20,7 @@
2020
},
2121
{
2222
"cell_type": "code",
23-
"execution_count": 17,
23+
"execution_count": 2,
2424
"id": "de54315d",
2525
"metadata": {
2626
"editable": true,
@@ -217,7 +217,7 @@
217217
"$\\begin{array}{l l l}\n",
218218
"\\begin{array}{l} {\\small{\\textsf{Specified Yield Strength: }}} \\end{array}&\\begin{array}{l} {\\small{F_{y}=50 \\ ksi}} \\end{array}&\\begin{array}{l} {\\small{\\textsf{Testing}}} \\\\ {\\small{\\textsf{reference line breaks}}} \\end{array}\\\\ \n",
219219
"\\begin{array}{l} {\\small{\\textsf{Plastic Section}}} \\\\ {\\small{\\textsf{Modulus: }}} \\end{array}&\\begin{array}{l} {\\small{Z_{x}=15 \\ in^{3}}} \\end{array}&\\begin{array}{l} {\\small{\\textsf{Testing description line breaks}}} \\end{array}\\\\ \n",
220-
"\\begin{array}{l} {\\small{\\textsf{Nominal Yield Strength: }}} \\end{array}&\\begin{array}{l} {\\small{M_{y}=F_{y}Z_{x}+}} \\\\ \\hspace{2em} {\\small{0 \\ kft=62 \\ kft}} \\end{array}&\\begin{array}{l} {\\small{\\textsf{Added + 0*kft to test equation line breaks}}} \\end{array}\\\\ \n",
220+
"\\begin{array}{l} {\\small{\\textsf{Nominal Yield Strength: }}} \\end{array}&\\begin{array}{l} {\\small{M_{y}=F_{y}\\cdot{}Z_{x}+}} \\\\ \\hspace{2em} {\\small{0 \\ kft=62 \\ kft}} \\end{array}&\\begin{array}{l} {\\small{\\textsf{Added + 0*kft to test equation line breaks}}} \\end{array}\\\\ \n",
221221
"\\end{array}$"
222222
],
223223
"text/plain": [
@@ -234,11 +234,51 @@
234234
"Plastic Section\\\\Modulus: Z_x = 15*inch**3 #Testing description line breaks\n",
235235
"Nominal Yield Strength: M_y = F_y*Z_x +\\\\0*kft -> 0*kft #Added + 0*kft to test equation line breaks"
236236
]
237+
},
238+
{
239+
"cell_type": "markdown",
240+
"id": "bc1ff481",
241+
"metadata": {},
242+
"source": [
243+
"## **Testing Percentages**"
244+
]
245+
},
246+
{
247+
"cell_type": "code",
248+
"execution_count": 8,
249+
"id": "3cfb5465",
250+
"metadata": {},
251+
"outputs": [
252+
{
253+
"data": {
254+
"text/markdown": [
255+
"$\\begin{array}{l l l}\n",
256+
"\\begin{array}{l} {\\small{\\textsf{Tax Rate: }}} \\end{array}&\\begin{array}{l} {\\small{tax_{rate}=15\\%=15 \\ \\%}} \\end{array}&\\begin{array}{l} {\\small{\\textsf{}}} \\end{array}\\\\ \n",
257+
"\\begin{array}{l} {\\small{\\textsf{Price: }}} \\end{array}&\\begin{array}{l} {\\small{price=100}} \\end{array}&\\begin{array}{l} {\\small{\\textsf{}}} \\end{array}\\\\ \n",
258+
"\\begin{array}{l} {\\small{\\textsf{Tax Amount: }}} \\end{array}&\\begin{array}{l} {\\small{tax=price\\cdot{}tax_{rate}=15.0}} \\end{array}&\\begin{array}{l} {\\small{\\textsf{}}} \\end{array}\\\\ \n",
259+
"\\begin{array}{l} {\\small{\\textsf{Total: }}} \\end{array}&\\begin{array}{l} {\\small{total=price+tax=115.0}} \\end{array}&\\begin{array}{l} {\\small{\\textsf{}}} \\end{array}\\\\ \n",
260+
"\\end{array}$"
261+
],
262+
"text/plain": [
263+
"<IPython.core.display.Markdown object>"
264+
]
265+
},
266+
"metadata": {},
267+
"output_type": "display_data"
268+
}
269+
],
270+
"source": [
271+
"%%calc\n",
272+
"Tax Rate: tax_rate = 15% -> 3*percent\n",
273+
"Price: price = 100\n",
274+
"Tax Amount: tax = price * tax_rate -> 2\n",
275+
"Total: total = price + tax -> 2"
276+
]
237277
}
238278
],
239279
"metadata": {
240280
"kernelspec": {
241-
"display_name": "Python 3 (ipykernel)",
281+
"display_name": "venv (3.13.11)",
242282
"language": "python",
243283
"name": "python3"
244284
},

0 commit comments

Comments
 (0)