iliekturtles / uom

Units of measurement -- type-safe zero-cost dimensional analysis

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

New units `VolumetricFlux` and `FlowResistivity`

indietyp opened this issue · comments

I have a PR ready for two new units:

  • VolumetricFlux (m³·s⁻¹·m⁻²), a measure of the volume of fluid flowing through a surface per unit of time (equivalent to mean flow velocity)
  • FlowResistivity (Pa·s·m⁻²), a measure of the ability of a porous material to resist the flow of air through it.

As one can see, both have three factors (two of them being SI metrics) I ended up generating all of the different possible units through a python script and as expected, there are quite a lot... (for VolumetricFlux to be specific 2.6k) I don't think that's feasible. What should the criteria be for the inclusion of a unit?

The script:

from itertools import combinations, product

prefix = {
    "yotta": "Y",
    "zetta": "Z",
    "exa": "E",
    "peta": "P",
    "tera": "T",
    "giga": "G",
    "mega": "M",
    "kilo": "k",
    "hecto": "h",
    "deca": "da",
    "deci": "d",
    "centi": "c",
    "milli": "m",
    "micro": "µ",
    "nano": "n",
    "pico": "p",
    "femto": "f",
    "atto": "a",
    "zepto": "z",
    "yocto": "y",
    "second": "s",
    "minute": "min",
    "hour": "h"
}

first = ["yotta", "zetta", "exa", "peta", "tera", "giga", "mega", "kilo", "hecto", "deca",
         None, "deci", "centi", "milli", "micro", "nano", "pico", "femto", "atto",
         "zepto", "yocto"]

second = ["second", "minute", "hour"]

third = ["yotta", "zetta", "exa", "peta", "tera", "giga", "mega", "kilo", "hecto", "deca",
         None, "deci", "centi", "milli", "micro", "nano", "pico", "femto", "atto",
         "zepto", "yocto"]


combinations = list(product(first, second, third))

# we need to do two runs, first for cubic meters, then for liters.

for first, second, third in combinations:
    first_str = first if first is not None else ""
    third_str = third if third is not None else ""

    unit_name = f'@cubic_{first_str}meter_per_{second}_per_square_{third_str}meter'

    first_factor = '*'.join((f'prefix!({first})', ) * 3) if first is not None else "1.0E0"
    match second:
        case "second":
            second_factor = "1.0E0"
        case "minute":
            second_factor = "60.0E0"
        case "hour":
            second_factor = "3600.0E0"
        case _:
            raise ValueError(f"Unknown unit {second}")

    third_factor = '*'.join((f'prefix!({third})', ) * 2) if third is not None else "1.0E0"

    conversion_factor = f'({first_factor})/({second_factor})/({third_factor})'

    first_prefix = prefix[first] if first is not None else ""
    second_prefix = prefix[second]
    third_prefix = prefix[third] if third is not None else ""

    unit = f'{first_prefix}m³·{second_prefix}·{third_prefix}m⁻²'
    singular = f'cubic {first_str}meter per {second} per square {third_str}meter'
    # collapse multiple spaces into one
    singular = ' '.join(singular.split())

    plural = f'cubic {first_str}meters per {second} per square {third_str}meter'
    # collapse multiple spaces into one
    plural = ' '.join(plural.split())

    print(f'{unit_name}: {conversion_factor}; "{unit}", "{singular}", "{plural}";')



for first, second, third in combinations:
    first_str = first if first is not None else ""
    third_str = third if third is not None else ""

    unit_name = f'@{first_str}liter_per_{second}_per_square_{third_str}meter'

    first_factor = '*'.join(('prefix!(milli)',) +(f'prefix!({first})', ) * 3) if first is not None else "1.0E0"
    match second:
        case "second":
            second_factor = "1.0E0"
        case "minute":
            second_factor = "60.0E0"
        case "hour":
            second_factor = "3600.0E0"
        case _:
            raise ValueError(f"Unknown unit {second}")

    third_factor = '*'.join((f'prefix!({third})', ) * 2) if third is not None else "1.0E0"

    conversion_factor = f'({first_factor})/({second_factor})/({third_factor})'

    first_prefix = prefix[first] if first is not None else ""
    second_prefix = prefix[second]
    third_prefix = prefix[third] if third is not None else ""

    unit = f'{first_prefix}{second_prefix}·{third_prefix}m⁻²'
    singular = f'{first_str}liter per {second} per square {third_str}meter'
    # collapse multiple spaces into one
    singular = ' '.join(singular.split())

    plural = f'{first_str}liters per {second} per square {third_str}meter'
    # collapse multiple spaces into one
    plural = ' '.join(plural.split())

    print(f'{unit_name}: {conversion_factor}; "{unit}", "{singular}", "{plural}";')

For quantities like this with a lot of unit combinations I'll usually do yotta through yocto for the dimension that is most commonly used and then hand pick other commonly used units. For these quantities perhaps start with yotta-yocto+hour/minute/second+no prefix and no prefix+hour/minute/second+yotta-yocto. I believe that will give 120 combinations each which isn't too bad.