pyharm 0.4.3__cp313-cp313-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
pyharm/__init__.py ADDED
@@ -0,0 +1,39 @@
1
+ """
2
+ PyHarm is a Python wrapper for CHarm.
3
+ """
4
+
5
+
6
+ # start delvewheel patch
7
+ def _delvewheel_patch_1_9_0():
8
+ import os
9
+ if os.path.isdir(libs_dir := os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir, 'pyharm.libs'))):
10
+ os.add_dll_directory(libs_dir)
11
+
12
+
13
+ _delvewheel_patch_1_9_0()
14
+ del _delvewheel_patch_1_9_0
15
+ # end delvewheel patch
16
+
17
+ import os as _os
18
+ from ._lib import _load_lib
19
+
20
+ # Name of the shared CHarm library to load
21
+ _libcharmname = 'libcharm'
22
+
23
+ # Directory of "_libcharmname"
24
+ _libcharmdir = _os.path.join(_os.path.dirname(__file__), '')
25
+
26
+ # Load the shared CHarm library
27
+ _libcharm = _load_lib(_libcharmdir, _libcharmname)
28
+
29
+ # Prefix to be added to the CHarm function names. Depends on the format of
30
+ # floating point numbers used to compile CHarm (single or double precision).
31
+ _CHARM = 'charm_'
32
+
33
+ # Prefix to be added to the PyHarm functions when calling "__repr__" methods
34
+ _pyharm = 'pyharm'
35
+
36
+ # The "err" module is intentionally not imported, as users do not interact with
37
+ # it in PyHarm.
38
+ from . import crd, glob, integ, leg, misc, sha, shc, shs
39
+ __all__ = ['crd', 'glob', 'integ', 'leg', 'misc', 'sha', 'shc', 'shs']
pyharm/_check_types.py ADDED
@@ -0,0 +1,144 @@
1
+ """
2
+ Internal checks of data types passed by users to PyHarm.
3
+ """
4
+
5
+
6
+ import numpy as _np
7
+ from ._data_types import _pyharm_ints, _pyharm_flt_all, _pyharm_flt
8
+
9
+
10
+ def _check_int_scalar(x, s):
11
+ """
12
+ Checks whether "x" is an instance of one of the classes in `_pyharm_ints`
13
+ and if so, whether it is a scalar in order to represent the quantity named
14
+ `s`.
15
+
16
+ Parameters
17
+ ----------
18
+ x : any data type
19
+ Data to check
20
+ s : str
21
+ String with the name of the data that is being checked with the first
22
+ letter being uppercase (e.g. 'The \'crd_type\' variable').
23
+ """
24
+
25
+ if not isinstance(x, _pyharm_ints):
26
+ msg = f'{s} must be an instance of one of the following classes: '
27
+ msg += f'{_pyharm_ints}.'
28
+ raise TypeError(msg)
29
+
30
+ if not _np.isscalar(x):
31
+ raise ValueError(f'{s} must be a scalar.')
32
+
33
+
34
+ def _check_flt_scalar(x, s):
35
+ """
36
+ Checks whether `x` is an instance of one of the classes in
37
+ `_pyharm_flt_all` and if so, whether it is a scalar to represent
38
+ the quantity named `s`.
39
+
40
+ Parameters
41
+ ----------
42
+ x : any data type
43
+ Data to check
44
+ s : str
45
+ String with the name of the data that is being checked with the first
46
+ letter being uppercase (e.g. 'Scaling parameter').
47
+ """
48
+
49
+ if not isinstance(x, _pyharm_flt_all):
50
+ msg = f'{s} must be an instance of one of the following classes: '
51
+ msg += f'{_pyharm_flt_all}.'
52
+ raise TypeError(msg)
53
+
54
+ if not _np.isscalar(x):
55
+ raise ValueError(f'{s} must be a scalar.')
56
+
57
+
58
+ def _check_deg_ord(x, do):
59
+ """
60
+ Checks whether `x` satisfies all conditions to represent harmonic degree
61
+ (`do == 'degree'`) or harmonic order (`do == 'order'`).
62
+
63
+ Parameters
64
+ ----------
65
+ x : any data type
66
+ do : str
67
+ Either 'degree' or 'order'
68
+ """
69
+
70
+ # Check whether "x" is of a correct integer class and whether it is
71
+ # a scalar
72
+ _check_int_scalar(x, f'Spherical harmonic {do}')
73
+ if do != 'degree' and do != 'order':
74
+ msg = f'The \'do\' variable must be set either to \'degree\' '
75
+ msg += f'or to \'order\'.'
76
+ raise ValueError(msg)
77
+
78
+ if x < 0:
79
+ msg = f'Spherical harmonic {do} cannot be smaller than zero.'
80
+ raise ValueError(msg)
81
+
82
+
83
+ def _check_radius(x):
84
+ """
85
+ Checks whether `x` satisfies all conditions to represent spherical radius.
86
+
87
+ Parameters
88
+ ----------
89
+ x : any data type
90
+ """
91
+
92
+ _check_flt_scalar(x, 'Spherical radius')
93
+
94
+ if x <= 0.0:
95
+ raise ValueError('Spherical radius must be larger than zero.')
96
+
97
+
98
+ def _check_flt_ndarray(x, n, s):
99
+ """
100
+ Checks whether `x` satisfies all conditions to represent an nD floating
101
+ point numpy array that can safely enter CHarm in order to represent
102
+ quantity named `s`.
103
+
104
+ Parameters
105
+ ----------
106
+ x : any data type
107
+ Object to be checked
108
+ n : integer
109
+ Required number of dimensions of the nD floating point array
110
+ s : str
111
+ String with the name of the object that is being checked with the first
112
+ letter being uppercase (e.g., 'Latitudes')
113
+ """
114
+
115
+ # Must be a "_np.ndarray"
116
+ if not isinstance(x, _np.ndarray):
117
+ msg = f'{s} must be an instance of one of the following classes: '
118
+ msg += f'{_np.ndarray}.'
119
+ raise TypeError(msg)
120
+
121
+ # Must be of the correct "dtype"
122
+ if x.dtype != _pyharm_flt:
123
+ msg = f'{s} must be of the following \'dtype\': {_pyharm_flt}.'
124
+ raise ValueError(msg)
125
+
126
+ # Must have the correct number of dimensions
127
+ if x.ndim != n:
128
+ msg = f'{s} must be of the following dimension: \'ndim={n}\'.'
129
+ raise ValueError(msg)
130
+
131
+ # Must be C-contiguous
132
+ if not x.flags.c_contiguous:
133
+ msg = f'{s} must be C-contiguous in memory (see the \'order\' flag '
134
+ msg += f'of numpy arrays).'
135
+ raise ValueError(msg)
136
+
137
+
138
+ def _check_pointer(pointer, func_name, lib_name):
139
+
140
+ if not pointer:
141
+ msg = f'Function call to \'{func_name}\' from the CHarm library '
142
+ msg += f'\'{lib_name}\' returned a NULL pointer.'
143
+ raise ValueError(msg)
144
+
pyharm/_constants.py ADDED
@@ -0,0 +1,52 @@
1
+ """
2
+ Generated automatically. Do not modify!
3
+ """
4
+
5
+ _globals = {
6
+ 'CHARM_SHA_CELL_AQ': 0,
7
+ 'CHARM_CRD_CELL_GRID': -2,
8
+ 'CHARM_CRD_CELL_SCATTERED': -1,
9
+ 'CHARM_CRD_POINT_SCATTERED': 1,
10
+ 'CHARM_CRD_POINT_GRID': 2,
11
+ 'CHARM_CRD_POINT_GRID_GL': 3,
12
+ 'CHARM_CRD_POINT_GRID_DH1': 4,
13
+ 'CHARM_CRD_POINT_GRID_DH2': 5,
14
+ 'CHARM_ERR_MAX_FILE': 4096,
15
+ 'CHARM_ERR_MAX_FUNC': 256,
16
+ 'CHARM_ERR_MAX_MSG': 4096,
17
+ 'CHARM_ERR_MAX_LEVEL': 10,
18
+ 'CHARM_ERR_MALLOC_FAILURE': 'Memory allocation failure.',
19
+ 'CHARM_ERR_FFTW_INIT_FAILURE': 'FFTW failed to initialize OpenMP threads.',
20
+ 'CHARM_FAILURE': -1,
21
+ 'CHARM_SUCCESS': 0,
22
+ 'CHARM_EMEM': 1,
23
+ 'CHARM_EFUNCARG': 2,
24
+ 'CHARM_EFILEIO': 3,
25
+ 'CHARM_EFFTWINIT': 4,
26
+ 'CHARM_EMPIPROCESS': 5,
27
+ 'CHARM_EXTERN': None,
28
+ 'CHARM_CDECL': None,
29
+ 'CHARM_SHC__NMAX_MODEL': -18446744073709551615,
30
+ 'CHARM_SHC__NMAX_ERROR': -18446744073709551614,
31
+ 'CHARM_SHC_WRITE_N': 0,
32
+ 'CHARM_SHC_WRITE_M': 1,
33
+ 'CHARM_VERSION_MAJOR': 0,
34
+ 'CHARM_VERSION_MINOR': 4,
35
+ 'CHARM_VERSION_PATCH': 3,
36
+ 'CHARM_VERSION_STRING': '0.4.3',
37
+ 'CHARM_LEG_PMNJ': 0,
38
+ 'CHARM_LEG_PMJN': 1,
39
+ 'GRAD_0': 0,
40
+ 'GRAD_1': -1,
41
+ 'GRAD_2': -2,
42
+ 'GRAD_LL': 0,
43
+ 'GRAD_LR': 1,
44
+ 'GRAD_LP': 2,
45
+ 'GRAD_RR': 3,
46
+ 'GRAD_RP': 4,
47
+ 'GRAD_PP': 5,
48
+ 'GRAD_P': 2,
49
+ 'GRAD_L': 0,
50
+ 'GRAD_R': 1,
51
+ }
52
+
pyharm/_data_types.py ADDED
@@ -0,0 +1,43 @@
1
+ """
2
+ Defines data types that:
3
+
4
+ * users can pass to PyHarm,
5
+ * PyHarm passes to CHarm, and
6
+ * CHarm returns to PyHarm.
7
+ """
8
+
9
+ import numpy as _np
10
+ import ctypes as _ct
11
+
12
+ # Data types that user can pass to PyHarm to represent integers
13
+ _pyharm_ints = (int, _np.integer)
14
+
15
+ # Data types that user can pass to PyHarm to represent floating point
16
+ # numbers. Note that "PY_FLOAT" already contains the comma, so no comma
17
+ # between "float," and "_np.float64".
18
+ _pyharm_flt_all = (float, _np.float64,) + _pyharm_ints
19
+
20
+ # Floating point data types for numpy ndarrays. A *single* data type must be
21
+ # defined here! The reason is that it avoids making any internal copies of
22
+ # data entered by the user, as this could be a problem for large arrays.
23
+ _pyharm_flt = _np.float64
24
+
25
+ # This is the data type, to which PyHarm converts the C's "double" (or "float"
26
+ # if in single precision) returned by CHarm
27
+ _charm_flt = _np.float64
28
+
29
+ # C's "int" entering CHarm
30
+ _ct_int = _ct.c_int
31
+
32
+ # C's "unsigned int" entering CHarm
33
+ _ct_uint = _ct.c_uint
34
+
35
+ # C's "size_t" entering CHarm
36
+ _ct_size_t = _ct.c_size_t
37
+
38
+ # C's "unsigned long int" entering CHarm
39
+ _ct_ulong = _ct.c_ulong
40
+
41
+ # C's "double" (or "float" if in single precision) entering CHarm
42
+ _ct_flt = _ct.c_double
43
+
pyharm/_err.py ADDED
@@ -0,0 +1,181 @@
1
+ # Implements the "err" module of PyHarm.
2
+ #
3
+ # NOTE: Unlike in CHarm, users do *not* interact with the "err" module in
4
+ # PyHarm!
5
+
6
+
7
+ import ctypes as _ct
8
+ from . import _libcharm, _libcharmname, _CHARM
9
+ from ._get_module_constants import _get_module_constants
10
+ from ._constants import _globals
11
+ from ._data_types import _ct_int
12
+ from ._check_types import _check_pointer
13
+
14
+
15
+ # Get the module constants from "_constants.py" and add them to the module's
16
+ # namespace
17
+ _get_module_constants('CHARM_ERR_')
18
+
19
+
20
+ # Character to represent the C's termination character
21
+ NULL_CHAR = '\0'
22
+
23
+
24
+ class _Err(_ct.Structure):
25
+ """
26
+ Private class to represent the `charm_err` structure of CHarm.
27
+ """
28
+
29
+ _fields_ = [('level', _ct.c_uint),
30
+ ('file', _ct.POINTER(_ct.POINTER(_ct.c_char))),
31
+ ('line', _ct.POINTER(_ct.c_uint)),
32
+ ('func', _ct.POINTER(_ct.POINTER(_ct.c_char))),
33
+ ('code', _ct_int),
34
+ ('msg', _ct.POINTER(_ct.c_char)),
35
+ ('saturated', _ct.c_bool)]
36
+
37
+
38
+ class CHarmError(Exception):
39
+ pass
40
+
41
+
42
+ def init():
43
+ """
44
+ Creates an empty instance of the `_Err` class.
45
+
46
+ Returns
47
+ -------
48
+ out : _Err
49
+ An instance of the `_Err` class.
50
+ """
51
+
52
+ func = _libcharm[_CHARM + 'err_init']
53
+ func.restype = _ct.POINTER(_Err)
54
+ func.argtypes = None
55
+
56
+ err = func()
57
+ _check_pointer(err, _CHARM + 'err_init', _libcharmname)
58
+
59
+ return err
60
+
61
+
62
+ def free(a):
63
+ """
64
+ Frees the memory associated with an instance `a` of the `_Err` class.
65
+
66
+ Parameters
67
+ ----------
68
+
69
+ a : _Err
70
+ An instance of the `_Err` class to be freed.
71
+ """
72
+
73
+ if a is not None:
74
+ func = _libcharm[_CHARM + 'err_free']
75
+ func.restype = None
76
+ func.argtypes = [_ct.POINTER(_Err)]
77
+ func(a)
78
+
79
+ return
80
+
81
+
82
+ def isempty(a):
83
+ """
84
+ Returns `True` if an instance of the `_Err` class `a` is empty or `False`
85
+ otherwise.
86
+
87
+ Parameters
88
+ ----------
89
+ a : _Err
90
+ An instance of the `_Err` class to be checked
91
+
92
+ Returns
93
+ -------
94
+ out : bool
95
+ `True` if `a` is empty, `False` otherwise.
96
+ """
97
+
98
+ func = _libcharm[_CHARM + 'err_isempty']
99
+ func.restype = _ct.c_bool
100
+ func.argtypes = [_ct.POINTER(_Err)]
101
+
102
+ return func(a)
103
+
104
+
105
+ def reset(a):
106
+ """
107
+ Resets an instance `a` of the `_Err` class.
108
+
109
+ Parameters
110
+ ----------
111
+ a : _Err
112
+ An instance of the `_Err` class to be reset.
113
+
114
+ Returns
115
+ -------
116
+ out : _Err
117
+ `a` reset to empty values
118
+ """
119
+
120
+ func = _libcharm[_CHARM + 'err_reset']
121
+ func.restype = None
122
+ func.argtypes = [_ct.POINTER(_Err)]
123
+
124
+ return func(a)
125
+
126
+
127
+ def handler(a, terminate):
128
+ """
129
+ This is a Python implementation of the CHarm's `charm_err_handler`
130
+ function. This Python implementation allows better error handling in
131
+ Python.
132
+
133
+ Parameters
134
+ ----------
135
+ a : _Err
136
+ An instance of the `_Err` class to be handled.
137
+ terminate : bool
138
+ If `True` and `a` is not empty, the execution of the program
139
+ terminates. If `False` and `a` is not empty, the error is printed and
140
+ the program continues in code execution. If `False` and `a` is empty,
141
+ nothing happens.
142
+ """
143
+
144
+ if isempty(a):
145
+ return
146
+
147
+ e = a.contents
148
+
149
+ err_msg = f'\n'
150
+ err_msg += f'Error code: {e.code} ' \
151
+ 'Traceback (most recent call last)\n'
152
+ err_msg += f'\n'
153
+
154
+ for l in range(e.level - 1, -1, -1):
155
+
156
+ f = e.file[l][:MAX_FILE].decode()
157
+ idx1 = f.find(NULL_CHAR)
158
+
159
+ func = e.func[l][:MAX_FUNC].decode()
160
+ idx2 = func.find(NULL_CHAR)
161
+
162
+ err_msg += f' File \'{f[:idx1]}\', line: {e.line[l]}, '
163
+ err_msg += f'function: \'{func[:idx2]}\'\n\n'
164
+
165
+ if e.saturated:
166
+ err_msg += ' Warning: The error structure is '
167
+ err_msg += 'saturated. Most recent function calls may therefore '
168
+ err_msg += 'not be reported.\n'
169
+ err_msg += f'\n'
170
+
171
+ msg = e.msg[:MAX_MSG].decode()
172
+ idx = msg.find(NULL_CHAR)
173
+ err_msg += f'Error message: {msg[:idx]}\n'
174
+
175
+ if terminate:
176
+ raise CHarmError(err_msg)
177
+ else:
178
+ print(err_msg)
179
+
180
+ return
181
+
@@ -0,0 +1,17 @@
1
+ import numpy as _np
2
+ from ._data_types import _pyharm_flt
3
+
4
+
5
+ def _get_empty_array():
6
+ """
7
+ Private function to return something that PyHarm considers to be an empty
8
+ numpy array.
9
+
10
+ Returns
11
+ -------
12
+ out : numpy floating point array
13
+ Empty numpy ndarray
14
+ """
15
+
16
+ return _np.array([], dtype=_pyharm_flt, order='C')
17
+
@@ -0,0 +1,29 @@
1
+ import sys
2
+ from ._constants import _globals
3
+
4
+
5
+ def _get_module_constants(prefix):
6
+ """
7
+ Private function to dynamically add global constants from the `_globals`
8
+ dictionary inside `_constants.py` to the module's namespace. The variables
9
+ are added under a modified name following the pattern:
10
+
11
+ CHARM_CRD_CELLS_GRID --> CELLS_GRID
12
+
13
+ where `prefix = 'CHARM_CRD_'` is the input variable.
14
+
15
+ Parameters
16
+ ----------
17
+ prefix : str
18
+ Prefix to be removed from the keys in `_globals` before adding to the
19
+ module's namespace. Should be, e.g., 'CHARM_CRD_', 'CHARM_SHA_', etc.
20
+ """
21
+
22
+ namespace = sys._getframe(1).f_globals
23
+
24
+ for name, value in _globals.items():
25
+ if name.startswith(prefix):
26
+ namespace[name[len(prefix):]] = value
27
+
28
+ return
29
+
pyharm/_lib.py ADDED
@@ -0,0 +1,29 @@
1
+ import os
2
+ import numpy as _np
3
+
4
+
5
+ def _load_lib(libdir, libname):
6
+ """
7
+ Private function to load a shared library 'libname' from directory
8
+ 'libdir'.
9
+
10
+ Parameters
11
+ ----------
12
+ libdir : str
13
+ Path to the shared library
14
+ libname : str
15
+ Name of the shared library
16
+
17
+ Returns
18
+ -------
19
+ out : ctypes.CDLL
20
+ The CHarm shared library
21
+ """
22
+
23
+ try:
24
+ return _np.ctypeslib.load_library(libname, libdir)
25
+ except OSError:
26
+ fullpath = os.path.join(libdir, libname)
27
+ msg = f'Failed to load the shared library \'{fullpath}\'.'
28
+ raise OSError(msg)
29
+