Coverage for middlelayer / ngect / utils.py: 66.67%
21 statements
« prev ^ index » next coverage.py v7.13.4, created at 2026-03-16 06:04 +0000
« prev ^ index » next coverage.py v7.13.4, created at 2026-03-16 06:04 +0000
1# Copyright 2023 Associated Universities, Inc.
2#
3# This file is part of ngVLA Exposure Calculator Tool (ngECT).
4#
5# ngECT is free software: you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation, either version 3 of the License, or
8# any later version.
9#
10# ngECT is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with ngECT. If not, see <https://www.gnu.org/licenses/>.
18import bisect
19import glob
20import logging
21import os
22import re
24import numpy as np
26logger = logging.getLogger(__name__)
29def get_si_prefix(value: float, select: str = "mu", lztol: int = 0) -> tuple:
30 """Choose a readable SI prefix for a numeric value.
32 Determines the prefix that minimizes leading zeros (within a tolerance)
33 and keeps a small number of digits before the decimal point after
34 scaling the value.
36 Parameters
37 ----------
38 value : float
39 The numeric value to scale.
40 select : str, optional
41 String containing the set of candidate SI prefixes to consider.
42 Must be a subset of ``"yzafpnum kMGTPEZY"`` (a space represents no prefix).
43 Defaults to ``"mu"`` (micro and none).
44 lztol : int, optional
45 Leading-zero tolerance used when selecting the prefix. Higher values
46 allow more leading zeros before switching to a smaller unit. Default is ``0``.
48 Returns
49 -------
50 tuple
51 ``(prefix: str, scale: float)`` where ``prefix`` is the chosen SI
52 prefix symbol (e.g., ``"m"``, ``"u"``, ``"k"``, or ``""`` for none)
53 and ``scale`` is the multiplicative factor corresponding to that prefix
54 (e.g., ``1e-3`` for ``"m"``, ``1e6`` for ``"M"``).
56 Notes
57 -----
58 The full ordered table of supported prefixes is::
60 "yzafpnum kMGTPEZY"
62 The space character denotes the base unit (no prefix).
64 Examples
65 --------
66 Frequency value in Hz:
68 >>> get_si_prefix(10**7, select='kMGT')
69 ('M', 1000000.0)
71 Flux value in Jy:
73 >>> get_si_prefix(1.0, select='um')
74 ('', 1.0)
75 >>> get_si_prefix(0.0, select='um')
76 ('', 1.0)
77 >>> get_si_prefix(-0.9, select='um')
78 ('m', 0.001)
79 >>> get_si_prefix(0.9, select='um', lztol=1)
80 ('', 1.0)
81 >>> get_si_prefix(1e-7, select='um')
82 ('u', 1e-06)
83 >>> get_si_prefix(1e3, select='um')
84 ('', 1.0)
85 """
86 if value == 0:
87 return "", 1.0
88 else:
89 sp_tab = "yzafpnum kMGTPEZY"
90 sp_list, sp_pow = zip(*[(p, (idx - 8) * 3.0) for idx, p in enumerate(sp_tab) if p in select + " "])
91 idx = bisect.bisect(sp_pow, np.log10(abs(value)) + lztol)
92 idx = max(idx - 1, 0)
94 return sp_list[idx].strip(), 10.0 ** sp_pow[idx]
97def check_dir(filename):
98 """Ensure the parent directory of a file path exists.
100 Parameters
101 ----------
102 filename : str
104 The file path whose parent directory should be checked/created.
106 Returns
107 -------
108 bool
109 ``True`` if the directory already existed, ``False`` if it was created
110 by this call.
112 Notes
113 -----
114 This helper does not create the file itself; it only ensures that the
115 directory component of ``filename`` exists. If ``filename`` has no
116 directory component (e.g., ``"file.txt"``), this function returns ``True``
117 and performs no action.
118 """
120 dirname = os.path.dirname(filename)
121 if dirname and not os.path.exists(dirname):
122 os.makedirs(dirname)
123 return False
124 else:
125 return True