Coverage for app/utility/numbers.py: 100%
29 statements
« prev ^ index » next coverage.py v7.10.5, created at 2025-08-28 09:13 +0000
« prev ^ index » next coverage.py v7.10.5, created at 2025-08-28 09:13 +0000
1import math
2import numpy as np
5def get_power_html(
6 value: float | list[float],
7 n: None | int = 3,
8) -> str | list[str]:
9 """Converts a value to html-formatted text with base 10
10 :param value: positive value
11 :param n: number of decimals. None to keep only the exponent bit. -1 to display all significant digits."""
13 if isinstance(value, list):
14 return [get_power_html(val, n) for val in value]
16 if value == 0:
17 return "0"
19 base10 = math.floor(np.log10(abs(value)))
20 mantissa = value / 10**base10
22 if n is None:
23 mantissa_str = ""
25 # Dynamic number of digits
26 elif n == -1:
27 mantissa_str = f"{mantissa:g}"
29 # Fixed number of digits
30 else:
31 mantissa_str = f"{mantissa:.{n}f}"
33 if base10 == 0:
34 return mantissa_str
35 else:
36 return (mantissa_str + f" ✕ 10<sup>{base10}</sup>").strip()
39def to_scientific(value: float | int | list[float | int] | None) -> str:
40 """Convert a number to scientific notation without trailing zeros
42 Examples
43 --------
44 >>> to_scientific(1.4e-4)
45 '1.4E-04'
46 >>> to_scientific(None)
47 ''
48 >>> to_scientific([1e-4, 1e-5])
49 '1E-04, 1E-05'"""
51 if value is None:
52 return ""
53 if value == 0:
54 return "0"
55 elif isinstance(value, (float, int, np.integer)):
56 string = "%E" % value
57 mantissa, exponent = string.split("E")
58 return (mantissa[0] + mantissa[1:].strip("0")).strip(".") + "E" + exponent
59 else:
60 return ", ".join([to_scientific(f) for f in value])
63def get_concentrations_html(N0s: list[float]) -> list[str]:
64 """Get the carrier concentration labels in html format
65 :param N0s: initial carrier concentrations"""
67 return ["N<sub>0</sub> = " + get_power_html(N0, -1) + " cm<sup>-3</sup>" for N0 in N0s]