diff --git a/examples/pycuc/README.md b/examples/pycuc/README.md new file mode 100644 index 0000000..c89ecd9 --- /dev/null +++ b/examples/pycuc/README.md @@ -0,0 +1,18 @@ +# pycuc Examples + +Each sub-directory contains a self-contained example. The order in +which the examples are to appear is specified in `order.json` (an +array of directory names in the expected order). + +In each example directory you'll find: + +* `config.toml` - must conform to the specification outlined here: + https://docs.pyscript.net/latest/user-guide/configuration/ This is + parsed and ultimately turned into a JSON representation as part of + the package's API object. +* `setup.py` - Python code for contextual and environmental setup, + NOT SEEN BY THE END USER, but is run before the `code.py` code is + evaluated. Allows us to create useful (IPython) shims, avoid + repeating boilerplate and whatnot. +* `code.py` - the actual code added to the editor which forms the + practical example of using the package. diff --git a/examples/pycuc/converter_objects/code.py b/examples/pycuc/converter_objects/code.py new file mode 100644 index 0000000..c0183a0 --- /dev/null +++ b/examples/pycuc/converter_objects/code.py @@ -0,0 +1,48 @@ +# --------------------------------------------------------------------- +# Reusable converter objects with create_cuc() +# --------------------------------------------------------------------- +import pycuc + + +heading("Carry a value around as a converter object") +note( + "When the same quantity is expressed in many units, " + "pycuc.create_cuc(value, unit) wraps it in a small object whose " + ".convert(target) method returns the value in any compatible unit." +) + +# A weather station logged a gust of 22 m/s. Let's express it in a few +# transport-friendly units without retyping the value each time. +wind_gust = pycuc.create_cuc(22, "m/s") + +reports = { + "km/h": wind_gust.convert("km/h"), + "mph": wind_gust.convert("mph"), + "knot": wind_gust.convert("knot"), + "ft/s": wind_gust.convert("ft/s"), +} + +note("A 22 m/s gust, in several velocity units:") +for unit, value in reports.items(): + note(f"  {value:7.2f} {unit}") + +heading("A small conversion table") +note( + "Build a tidy table of energy values by looping over a list of " + "target units. The same pattern works for any PyCUC category." +) + +battery_energy = pycuc.create_cuc(1.5, "kWh") +energy_units = ["J", "kJ", "Wh", "kWh", "cal", "kcal", "BTU"] + +rows = "".join( + f"{unit}" + f"{battery_energy.convert(unit):,.3f}" + for unit in energy_units +) +table_html = ( + "" + "" + f"{rows}
Unit1.5 kWh as...
" +) +display(HTML(table_html), append=True) diff --git a/examples/pycuc/converter_objects/config.toml b/examples/pycuc/converter_objects/config.toml new file mode 100644 index 0000000..682f5d8 --- /dev/null +++ b/examples/pycuc/converter_objects/config.toml @@ -0,0 +1 @@ +packages = ["pycuc"] diff --git a/examples/pycuc/converter_objects/setup.py b/examples/pycuc/converter_objects/setup.py new file mode 100644 index 0000000..1e59ef1 --- /dev/null +++ b/examples/pycuc/converter_objects/setup.py @@ -0,0 +1,20 @@ +"""Lightweight setup for the second example. Mirrors the names from cell 1.""" +import js +from pyscript import window, HTML, display as _display + +js.alert = window.alert + + +def display(*args, **kwargs): + return _display( + *args, **kwargs, target=__pyscript_display_target__, + ) + + +def heading(text, level=2): + display(HTML(f"{text}"), append=True) + + +def note(text): + display(HTML(f"

{text}

"), append=True) + diff --git a/examples/pycuc/custom_units/code.py b/examples/pycuc/custom_units/code.py new file mode 100644 index 0000000..a5a48c0 --- /dev/null +++ b/examples/pycuc/custom_units/code.py @@ -0,0 +1,42 @@ +# --------------------------------------------------------------------- +# Defining your own units with add_custom_unit() +# --------------------------------------------------------------------- +import pycuc + + +heading("Teaching PyCUC a new unit") +note( + "Suppose your team measures heat capacity in J/mol.K and kJ/mol.K, " + "and you'd like PyCUC to handle that family. add_custom_unit(name, " + "factor) registers each unit relative to a chosen reference value " + "of 1." +) + +# Start from a measured heat capacity in J/(mol.K). +heat_capacity = pycuc.create_cuc(75.3, "J/mol.K") + +# Register the family. The factor is "how many of the reference unit +# (J/mol.K, factor 1) make up one of this unit". So 1 kJ/mol.K is +# 1000 J/mol.K. +heat_capacity.add_custom_unit("J/mol.K", 1) +heat_capacity.add_custom_unit("kJ/mol.K", 1000) +heat_capacity.add_custom_unit("cal/mol.K", 4.184) + +note("The same value, expressed across our newly defined units:") +for unit in ["J/mol.K", "kJ/mol.K", "cal/mol.K"]: + note(f"  {heat_capacity.convert(unit):.4f} {unit}") + +heading("Inspecting the custom registry") +note( + "check_reference('custom') returns the units you have added on " + "this converter object, alongside their conversion factors." +) +display(heat_capacity.check_reference("custom"), append=True) + +heading("Mixing built-in and custom units") +note( + "Built-in references stay available on the same object. Here we " + "ask the same converter about pressure units, which PyCUC ships " + "with out of the box." +) +display(heat_capacity.check_reference("pressure"), append=True) diff --git a/examples/pycuc/custom_units/config.toml b/examples/pycuc/custom_units/config.toml new file mode 100644 index 0000000..682f5d8 --- /dev/null +++ b/examples/pycuc/custom_units/config.toml @@ -0,0 +1 @@ +packages = ["pycuc"] diff --git a/examples/pycuc/custom_units/setup.py b/examples/pycuc/custom_units/setup.py new file mode 100644 index 0000000..a280b76 --- /dev/null +++ b/examples/pycuc/custom_units/setup.py @@ -0,0 +1,20 @@ +"""Lightweight setup for the third example.""" +import js +from pyscript import window, HTML, display as _display + +js.alert = window.alert + + +def display(*args, **kwargs): + return _display( + *args, **kwargs, target=__pyscript_display_target__, + ) + + +def heading(text, level=2): + display(HTML(f"{text}"), append=True) + + +def note(text): + display(HTML(f"

{text}

"), append=True) + diff --git a/examples/pycuc/order.json b/examples/pycuc/order.json new file mode 100644 index 0000000..8e69bbb --- /dev/null +++ b/examples/pycuc/order.json @@ -0,0 +1,5 @@ +[ + "quick_conversions", + "converter_objects", + "custom_units" +] diff --git a/examples/pycuc/quick_conversions/code.py b/examples/pycuc/quick_conversions/code.py new file mode 100644 index 0000000..1569065 --- /dev/null +++ b/examples/pycuc/quick_conversions/code.py @@ -0,0 +1,56 @@ +""" +A first look at PyCUC: convert quantities between units in a single +line, using either a from/to pair or a compact "MPa => Pa" string. + +PyCUC bundles conversions for many physical quantities (pressure, +temperature, energy, length, mass, volume, ...). See the project +docs at https://github.com/sinagilassi/pycuc for the full list. +""" +from IPython.core.display import display, HTML +import pycuc + + +heading("Reactor pressure: from megapascals to friendlier units") +note( + "An engineer recorded a reactor pressure of 2.5 MPa and wants " + "to share it in pascals, bar, and psi for different audiences." +) + +reactor_pressure_mpa = 2.5 + +# convert_from_to(value, from_unit, to_unit) is the most explicit form. +in_pascals = pycuc.convert_from_to(reactor_pressure_mpa, "MPa", "Pa") +in_bar = pycuc.convert_from_to(reactor_pressure_mpa, "MPa", "bar") +in_psi = pycuc.convert_from_to(reactor_pressure_mpa, "MPa", "psi") + +note( + f"{reactor_pressure_mpa} MPa is " + f"{in_pascals:,.0f} Pa, " + f"{in_bar:.2f} bar, " + f"and {in_psi:.2f} psi." +) + +heading("The shorthand: pycuc.to(value, 'from => to')") +note( + "If you prefer something terser, pycuc.to() takes a single " + "string describing the conversion." +) + +oven_temperature_c = 180.0 +oven_in_kelvin = pycuc.to(oven_temperature_c, "C => K") +oven_in_fahrenheit = pycuc.to(oven_temperature_c, "C => F") + +note( + f"An oven at {oven_temperature_c} °C is " + f"{oven_in_kelvin:.2f} K or " + f"{oven_in_fahrenheit:.1f} °F." +) + +heading("Discovering what units are available") +note( + "pycuc.check_reference(category) lists the units PyCUC knows " + "about for a given quantity. Here are the pressure units:" +) + +pressure_units = pycuc.check_reference("pressure") +display(pressure_units, append=True) diff --git a/examples/pycuc/quick_conversions/config.toml b/examples/pycuc/quick_conversions/config.toml new file mode 100644 index 0000000..682f5d8 --- /dev/null +++ b/examples/pycuc/quick_conversions/config.toml @@ -0,0 +1 @@ +packages = ["pycuc"] diff --git a/examples/pycuc/quick_conversions/setup.py b/examples/pycuc/quick_conversions/setup.py new file mode 100644 index 0000000..07879f9 --- /dev/null +++ b/examples/pycuc/quick_conversions/setup.py @@ -0,0 +1,42 @@ +""" +Shim IPython's display API onto PyScript so example code written in a +Jupyter/IPython idiom runs unmodified in the browser. +""" + +import sys +import types +import js +from pyscript import window, HTML, display as _display + +js.alert = window.alert + + +def display(*args, **kwargs): + """Wrap pyscript.display so output lands in the example target.""" + return _display( + *args, **kwargs, target=__pyscript_display_target__, + ) + + +ipython = types.ModuleType("IPython") +core = types.ModuleType("IPython.core") +core_display = types.ModuleType("IPython.core.display") +core_display.display = display +core_display.HTML = HTML +ipython.core = core +core.display = core_display +ipython.get_ipython = lambda: None +ipython.display = core_display +sys.modules["IPython"] = ipython +sys.modules["IPython.core"] = core +sys.modules["IPython.core.display"] = core_display +sys.modules["IPython.display"] = core_display + + +def heading(text, level=2): + display(HTML(f"{text}"), append=True) + + +def note(text): + display(HTML(f"

{text}

"), append=True) +