pyant 1.0.0__py3-none-any.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.
- clibbeam/Makefile +26 -0
- clibbeam/array.c +18 -0
- clibbeam/beam.c +20 -0
- clibbeam/beam.h +11 -0
- pyant/__init__.py +29 -0
- pyant/beam.py +301 -0
- pyant/beam_fitting.py +2 -0
- pyant/beams/__init__.py +7 -0
- pyant/beams/arrays.py +29 -0
- pyant/clib.py +17 -0
- pyant/coordinates.py +369 -0
- pyant/models/__init__.py +11 -0
- pyant/models/airy.py +91 -0
- pyant/models/array.py +234 -0
- pyant/models/cassegrain.py +102 -0
- pyant/models/finite_cylindrical_parabola.py +198 -0
- pyant/models/gaussian.py +124 -0
- pyant/models/harmonic_series.py +6 -0
- pyant/models/interpolated.py +79 -0
- pyant/models/interpolated_array.py +318 -0
- pyant/models/isotropic.py +21 -0
- pyant/models/measured.py +59 -0
- pyant/models/phased_finite_cylindrical_parabola.py +172 -0
- pyant/models/polynomial.py +6 -0
- pyant/plotting.py +554 -0
- pyant/py.typed +0 -0
- pyant/statistics.py +49 -0
- pyant/types.py +27 -0
- pyant/version.py +3 -0
- pyant-1.0.0.dist-info/METADATA +110 -0
- pyant-1.0.0.dist-info/RECORD +34 -0
- pyant-1.0.0.dist-info/WHEEL +5 -0
- pyant-1.0.0.dist-info/licenses/LICENSE +21 -0
- pyant-1.0.0.dist-info/top_level.txt +2 -0
clibbeam/Makefile
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
CC=gcc
|
|
2
|
+
CFLAGS=-fPIC
|
|
3
|
+
LIBS=-lm
|
|
4
|
+
|
|
5
|
+
SOURCES=$(wildcard *.c)
|
|
6
|
+
OBJECTS=$(SOURCES:.c=.o)
|
|
7
|
+
EXT=$(shell python -c "import sysconfig; print(sysconfig.get_config_var('EXT_SUFFIX'))")
|
|
8
|
+
OUTLIB=clibbeam$(EXT)
|
|
9
|
+
|
|
10
|
+
all: $(OUTLIB)
|
|
11
|
+
|
|
12
|
+
%.o: %.c
|
|
13
|
+
@echo "pyant installation -> Compiling source file $< ..."
|
|
14
|
+
$(CC) -c $(CFLAGS) -o $@ $<
|
|
15
|
+
|
|
16
|
+
$(OUTLIB): $(OBJECTS)
|
|
17
|
+
@echo "pyant installation -> Linking shared library $@ ..."
|
|
18
|
+
$(CC) $(CFLAGS) -shared $(OBJECTS) $(LIBS) -o $@
|
|
19
|
+
@mv $@ ../pyant/ -v
|
|
20
|
+
@echo "pyant installation -> The shared library $< has been created successfully."
|
|
21
|
+
|
|
22
|
+
clean:
|
|
23
|
+
@echo "pyant installation -> Removing object files *.o ..."
|
|
24
|
+
@-rm -f *.o
|
|
25
|
+
@echo "pyant installation -> Removing shared library *.so ..."
|
|
26
|
+
@-rm -f *.so
|
clibbeam/array.c
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#include <stdio.h>
|
|
2
|
+
#include <complex.h>
|
|
3
|
+
#include <math.h>
|
|
4
|
+
#include "beam.h"
|
|
5
|
+
|
|
6
|
+
// todo: test if this is actually faster or not
|
|
7
|
+
// do we really need this??
|
|
8
|
+
void
|
|
9
|
+
array_sensor_response(
|
|
10
|
+
double *k,
|
|
11
|
+
double complex *G,
|
|
12
|
+
size_t channels
|
|
13
|
+
)
|
|
14
|
+
{
|
|
15
|
+
for(size_t ch = 0; ch < channels; ch++) {
|
|
16
|
+
G[ch] = 1.0;
|
|
17
|
+
}
|
|
18
|
+
}
|
clibbeam/beam.c
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#include <stdio.h>
|
|
2
|
+
#include <complex.h>
|
|
3
|
+
#include <math.h>
|
|
4
|
+
#include "beam.h"
|
|
5
|
+
|
|
6
|
+
int
|
|
7
|
+
main(int argc, char *argv[])
|
|
8
|
+
{
|
|
9
|
+
double kvec[3];
|
|
10
|
+
size_t channels = 1;
|
|
11
|
+
double complex G[channels];
|
|
12
|
+
kvec[0] = 0.0;
|
|
13
|
+
kvec[1] = 0.0;
|
|
14
|
+
kvec[2] = 1.0;
|
|
15
|
+
array_sensor_response(kvec, G, channels);
|
|
16
|
+
|
|
17
|
+
printf("Hello, G = %.2f + %.2fi\n", creal(G[0]), cimag(G[0]));
|
|
18
|
+
|
|
19
|
+
return 0;
|
|
20
|
+
}
|
clibbeam/beam.h
ADDED
pyant/__init__.py
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
|
|
3
|
+
""" """
|
|
4
|
+
import types
|
|
5
|
+
import importlib
|
|
6
|
+
from .version import __version__
|
|
7
|
+
|
|
8
|
+
# Modules
|
|
9
|
+
if importlib.util.find_spec("matplotlib") is not None:
|
|
10
|
+
from . import plotting
|
|
11
|
+
else:
|
|
12
|
+
|
|
13
|
+
class _MissingModule(types.ModuleType):
|
|
14
|
+
def __getattr__(self, name):
|
|
15
|
+
raise ImportError(
|
|
16
|
+
"The optional dependency `matplotlib` for 'plotting' module is missing.\n"
|
|
17
|
+
"Install it with `pip install pyant[plotting]` or `pip install matplotlib`."
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
plotting = _MissingModule("plotting")
|
|
21
|
+
|
|
22
|
+
from . import coordinates
|
|
23
|
+
from . import models
|
|
24
|
+
from . import statistics
|
|
25
|
+
from . import beams
|
|
26
|
+
|
|
27
|
+
# from . import clib
|
|
28
|
+
|
|
29
|
+
from .beam import Beam
|
pyant/beam.py
ADDED
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
|
|
3
|
+
"""Defines an antenna's or entire radar system's radiation pattern"""
|
|
4
|
+
from abc import ABC, abstractmethod
|
|
5
|
+
import collections
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
from numpy.typing import NDArray
|
|
9
|
+
import scipy.constants
|
|
10
|
+
|
|
11
|
+
from . import coordinates
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Beam(ABC):
|
|
15
|
+
"""Defines the radiation pattern, i.e gain, of a radar. Gain here means
|
|
16
|
+
amplification of the electromagnetic wave amplitude when transferred to a
|
|
17
|
+
complex voltage amplitude.
|
|
18
|
+
|
|
19
|
+
Notes
|
|
20
|
+
------
|
|
21
|
+
There are four possible ways of broadcasting input arrays over the parameters:
|
|
22
|
+
|
|
23
|
+
The additional axis of all parameters lines up with the input wave vectors additional
|
|
24
|
+
axis size, in which case each input k-vector gets evaluated versus each set of
|
|
25
|
+
parameters.
|
|
26
|
+
Input size (3, n), parameter shapes (..., n), output size (n,).
|
|
27
|
+
|
|
28
|
+
The parameters are all scalars and the input k-vector gets evaluated over this single set.
|
|
29
|
+
Input size (3, n), parameter shapes (...), output size (n,).
|
|
30
|
+
|
|
31
|
+
The additional axis of all parameters line up and the input k-vector is a single vector,
|
|
32
|
+
in which case this vector gets computed for all sets of parameters.
|
|
33
|
+
Input size (3,), parameter shapes (..., n), output size (n,).
|
|
34
|
+
|
|
35
|
+
The parameters are all scalars and the input k-vector is a single vector.
|
|
36
|
+
Input size (3,), parameter shapes (...), output size ().
|
|
37
|
+
|
|
38
|
+
These ways allow for any set of computations and broadcasts (although they need to be prepared
|
|
39
|
+
outside the scope of this class) to be set-up using the Beam interface.
|
|
40
|
+
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
def __init__(self):
|
|
44
|
+
"""Basic constructor."""
|
|
45
|
+
self.parameters = collections.OrderedDict()
|
|
46
|
+
self.parameters_shape = {}
|
|
47
|
+
|
|
48
|
+
def _get_parameter_len(self, key: str):
|
|
49
|
+
"""Get the length of a parameter axis, its always the last array dimension"""
|
|
50
|
+
obj = self.parameters[key]
|
|
51
|
+
if isinstance(obj, np.ndarray):
|
|
52
|
+
if key in self.parameters_shape:
|
|
53
|
+
shape = self.parameters_shape[key]
|
|
54
|
+
if len(obj.shape) == len(shape):
|
|
55
|
+
return 0
|
|
56
|
+
else:
|
|
57
|
+
return obj.shape[-1]
|
|
58
|
+
else:
|
|
59
|
+
return obj.shape[-1]
|
|
60
|
+
else:
|
|
61
|
+
return 0
|
|
62
|
+
|
|
63
|
+
@property
|
|
64
|
+
def size(self):
|
|
65
|
+
"""The additional dimensions added to the output Gain if broadcasting is enabled."""
|
|
66
|
+
shape = [self._get_parameter_len(key) for key in self.parameters]
|
|
67
|
+
if len(shape) == 0:
|
|
68
|
+
return 0
|
|
69
|
+
assert all(x == shape[0] for x in shape), (
|
|
70
|
+
"all parameter shapes must line up:"
|
|
71
|
+
f"{list(self.parameters.keys())} -> {shape}"
|
|
72
|
+
)
|
|
73
|
+
return shape[0]
|
|
74
|
+
|
|
75
|
+
def validate_parameter_shapes(self):
|
|
76
|
+
"""Helper function to validate the input parameter shapes are correct"""
|
|
77
|
+
# TODO: maybe change to raise custom exceptions?
|
|
78
|
+
size = None
|
|
79
|
+
for key, p in self.parameters.items():
|
|
80
|
+
if size is None:
|
|
81
|
+
size = self._get_parameter_len(key)
|
|
82
|
+
assert size == self._get_parameter_len(key), "all parameter shapes must line up"
|
|
83
|
+
if key in self.parameters_shape:
|
|
84
|
+
shape = self.parameters_shape[key]
|
|
85
|
+
assert len(p.shape) <= len(shape) + 1 and len(p.shape) >= len(
|
|
86
|
+
shape
|
|
87
|
+
), f"{key} can only have {len(shape)} or {len(shape) + 1} axis, not {len(p.shape)}"
|
|
88
|
+
assert (
|
|
89
|
+
p.shape[: len(shape)] == shape
|
|
90
|
+
), f"{key} needs at least {shape} dimensions, not {p.shape}"
|
|
91
|
+
|
|
92
|
+
def validate_k_shape(self, k):
|
|
93
|
+
"""Helper function to validate the input direction vector shape is correct"""
|
|
94
|
+
# TODO: maybe change to raise custom exceptions?
|
|
95
|
+
size = self.size
|
|
96
|
+
k_len = k.shape[1] if len(k.shape) > 1 else 0
|
|
97
|
+
if size > 0:
|
|
98
|
+
assert (
|
|
99
|
+
size == k_len or k_len == 0
|
|
100
|
+
), "input k vector must either be single vector or line up with parameter dimensions"
|
|
101
|
+
assert len(k.shape) <= 2, "k vector can only be vectorized along one extra axis"
|
|
102
|
+
assert k.shape[0] == 3, f"pointing vector must at least be a 3-vector, not {k.shape[0]}"
|
|
103
|
+
return k_len
|
|
104
|
+
|
|
105
|
+
@property
|
|
106
|
+
def keys(self):
|
|
107
|
+
"""Current list of parameters."""
|
|
108
|
+
return self.parameters.keys()
|
|
109
|
+
|
|
110
|
+
@staticmethod
|
|
111
|
+
def _azel_to_numpy(azimuth: NDArray | float, elevation: NDArray | float) -> NDArray:
|
|
112
|
+
"""Convert input azimuth and elevation to spherical coordinates states,
|
|
113
|
+
i.e a `shape=(3,n)` numpy array.
|
|
114
|
+
"""
|
|
115
|
+
|
|
116
|
+
az_len = azimuth.size if isinstance(azimuth, np.ndarray) else None
|
|
117
|
+
el_len = elevation.size if isinstance(elevation, np.ndarray) else None
|
|
118
|
+
|
|
119
|
+
if el_len is not None and az_len is not None:
|
|
120
|
+
assert el_len == az_len, f"azimuth {az_len} and elevation {el_len} sizes must agree"
|
|
121
|
+
|
|
122
|
+
if az_len is not None:
|
|
123
|
+
shape = (3, az_len)
|
|
124
|
+
elif el_len is not None:
|
|
125
|
+
shape = (3, el_len)
|
|
126
|
+
else:
|
|
127
|
+
shape = (3, )
|
|
128
|
+
|
|
129
|
+
sph = np.empty(shape, dtype=np.float64)
|
|
130
|
+
sph[0, ...] = azimuth
|
|
131
|
+
sph[1, ...] = elevation
|
|
132
|
+
sph[2, ...] = 1.0
|
|
133
|
+
|
|
134
|
+
return sph
|
|
135
|
+
|
|
136
|
+
def copy(self):
|
|
137
|
+
"""Return a copy of the current instance."""
|
|
138
|
+
raise NotImplementedError("")
|
|
139
|
+
|
|
140
|
+
def to_h5(self, path):
|
|
141
|
+
"""Write defining parameters to a h5 file"""
|
|
142
|
+
raise NotImplementedError("")
|
|
143
|
+
|
|
144
|
+
@classmethod
|
|
145
|
+
def from_h5(cls):
|
|
146
|
+
"""Load defining parameters from a h5 file and instantiate a beam"""
|
|
147
|
+
raise NotImplementedError("")
|
|
148
|
+
|
|
149
|
+
@property
|
|
150
|
+
def frequency(self):
|
|
151
|
+
"""The radar wavelength."""
|
|
152
|
+
return self.parameters["frequency"]
|
|
153
|
+
|
|
154
|
+
@frequency.setter
|
|
155
|
+
def frequency(self, val):
|
|
156
|
+
self.parameters["frequency"] = val
|
|
157
|
+
|
|
158
|
+
@property
|
|
159
|
+
def wavelength(self):
|
|
160
|
+
"""The radar wavelength."""
|
|
161
|
+
return scipy.constants.c / self.frequency
|
|
162
|
+
|
|
163
|
+
@wavelength.setter
|
|
164
|
+
def wavelength(self, val):
|
|
165
|
+
self.frequency = scipy.constants.c / val
|
|
166
|
+
|
|
167
|
+
def sph_point(
|
|
168
|
+
self, azimuth: NDArray | float, elevation: NDArray | float, degrees: bool = False
|
|
169
|
+
):
|
|
170
|
+
"""Point beam towards azimuth and elevation coordinate.
|
|
171
|
+
|
|
172
|
+
Parameters
|
|
173
|
+
----------
|
|
174
|
+
azimuth : float
|
|
175
|
+
Azimuth east of north of pointing direction.
|
|
176
|
+
elevation : float
|
|
177
|
+
Elevation from horizon of pointing direction.
|
|
178
|
+
degrees : bool
|
|
179
|
+
If :code:`True` all input/output angles are in degrees,
|
|
180
|
+
else they are in radians. Defaults to instance
|
|
181
|
+
settings :code:`self.radians`.
|
|
182
|
+
|
|
183
|
+
"""
|
|
184
|
+
sph = Beam._azel_to_numpy(azimuth, elevation)
|
|
185
|
+
self.parameters["pointing"] = coordinates.sph_to_cart(sph, degrees=degrees)
|
|
186
|
+
|
|
187
|
+
def point(self, k: NDArray):
|
|
188
|
+
"""Point beam in local Cartesian direction.
|
|
189
|
+
|
|
190
|
+
Parameters
|
|
191
|
+
----------
|
|
192
|
+
k : numpy.ndarray
|
|
193
|
+
Pointing direction in local coordinates.
|
|
194
|
+
|
|
195
|
+
"""
|
|
196
|
+
self.parameters["pointing"] = k / np.linalg.norm(k, axis=0)
|
|
197
|
+
|
|
198
|
+
def sph_angle(
|
|
199
|
+
self, azimuth: NDArray | float, elevation: NDArray | float, degrees: bool = False
|
|
200
|
+
) -> NDArray | float:
|
|
201
|
+
"""Get angle between azimuth and elevation and pointing direction.
|
|
202
|
+
|
|
203
|
+
Parameters
|
|
204
|
+
----------
|
|
205
|
+
azimuth : float or NDArray
|
|
206
|
+
Azimuth east of north of pointing direction.
|
|
207
|
+
elevation : float or NDArray
|
|
208
|
+
Elevation from horizon of pointing direction.
|
|
209
|
+
degrees : bool
|
|
210
|
+
If :code:`True` all input/output angles are in degrees,
|
|
211
|
+
else they are in radians.
|
|
212
|
+
|
|
213
|
+
Returns
|
|
214
|
+
-------
|
|
215
|
+
float or NDArray
|
|
216
|
+
Angle between pointing and given direction.
|
|
217
|
+
|
|
218
|
+
"""
|
|
219
|
+
sph = Beam._azel_to_numpy(azimuth, elevation)
|
|
220
|
+
k = coordinates.sph_to_cart(sph, degrees=degrees)
|
|
221
|
+
return self.angle(k, degrees=degrees)
|
|
222
|
+
|
|
223
|
+
def angle(self, k: NDArray, degrees: bool = False) -> NDArray | float:
|
|
224
|
+
"""Get angle between local direction and pointing direction.
|
|
225
|
+
|
|
226
|
+
Parameters
|
|
227
|
+
----------
|
|
228
|
+
k : numpy.ndarray
|
|
229
|
+
Direction to evaluate angle to.
|
|
230
|
+
degrees : bool
|
|
231
|
+
If :code:`True` all input/output angles are in degrees,
|
|
232
|
+
else they are in radians. Defaults to instance
|
|
233
|
+
settings :code:`self.radians`.
|
|
234
|
+
|
|
235
|
+
Returns
|
|
236
|
+
-------
|
|
237
|
+
float or NDArray
|
|
238
|
+
Angle between pointing and given direction.
|
|
239
|
+
|
|
240
|
+
"""
|
|
241
|
+
pt: NDArray = self.parameters["pointing"]
|
|
242
|
+
return coordinates.vector_angle(pt, k, degrees=degrees)
|
|
243
|
+
|
|
244
|
+
@abstractmethod
|
|
245
|
+
def gain(self, k: NDArray, polarization: NDArray | None = None):
|
|
246
|
+
"""Return the gain in the given direction. This method should be
|
|
247
|
+
vectorized in the `k` variable.
|
|
248
|
+
|
|
249
|
+
Parameters
|
|
250
|
+
----------
|
|
251
|
+
k : numpy.ndarray
|
|
252
|
+
Direction in local coordinates to evaluate
|
|
253
|
+
gain in. Must be a `(3,)` vector or a `(3,n)` matrix.
|
|
254
|
+
polarization : numpy.ndarray
|
|
255
|
+
The Jones vector of the incoming
|
|
256
|
+
plane waves, if applicable for the beam in question.
|
|
257
|
+
|
|
258
|
+
Returns
|
|
259
|
+
-------
|
|
260
|
+
float/numpy.ndarray
|
|
261
|
+
Radar gain in the given direction. If input is a `(3,)`
|
|
262
|
+
vector, output is a float. If input is a `(3,n)` matrix output
|
|
263
|
+
is a `(n,)` vector of gains.
|
|
264
|
+
|
|
265
|
+
"""
|
|
266
|
+
pass
|
|
267
|
+
|
|
268
|
+
def sph_gain(
|
|
269
|
+
self,
|
|
270
|
+
azimuth: NDArray | float,
|
|
271
|
+
elevation: NDArray | float,
|
|
272
|
+
polarization: NDArray | None = None,
|
|
273
|
+
degrees: bool = False,
|
|
274
|
+
):
|
|
275
|
+
"""Return the gain in the given direction.
|
|
276
|
+
|
|
277
|
+
Parameters
|
|
278
|
+
----------
|
|
279
|
+
azimuth : float
|
|
280
|
+
Azimuth east of north to evaluate gain in.
|
|
281
|
+
elevation : float
|
|
282
|
+
Elevation from horizon to evaluate gain in.
|
|
283
|
+
degrees : bool
|
|
284
|
+
If :code:`True` all input/output angles are in degrees,
|
|
285
|
+
else they are in radians. Defaults to instance
|
|
286
|
+
settings :code:`self.radians`.
|
|
287
|
+
|
|
288
|
+
Returns
|
|
289
|
+
-------
|
|
290
|
+
float/numpy.ndarray
|
|
291
|
+
Radar gain in the given direction. If input is a `(3,)`
|
|
292
|
+
vector, output is a float. If input is a `(3,n)` matrix output
|
|
293
|
+
is a `(n,)` vector of gains.
|
|
294
|
+
"""
|
|
295
|
+
sph = Beam._azel_to_numpy(azimuth, elevation)
|
|
296
|
+
|
|
297
|
+
k = coordinates.sph_to_cart(sph, degrees=degrees)
|
|
298
|
+
return self.gain(
|
|
299
|
+
k,
|
|
300
|
+
polarization=polarization,
|
|
301
|
+
)
|
pyant/beam_fitting.py
ADDED
pyant/beams/__init__.py
ADDED
pyant/beams/arrays.py
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from ..models import Array
|
|
4
|
+
from .. import coordinates
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def equidistant_archimedian_spiral(
|
|
8
|
+
antenna_num,
|
|
9
|
+
arc_separation,
|
|
10
|
+
range_coefficient,
|
|
11
|
+
frequency,
|
|
12
|
+
pointing,
|
|
13
|
+
degrees=True,
|
|
14
|
+
):
|
|
15
|
+
# https://math.stackexchange.com/a/2216736
|
|
16
|
+
antennas = np.zeros((3, antenna_num))
|
|
17
|
+
for ind in range(1, antenna_num):
|
|
18
|
+
d_theta = arc_separation / np.sqrt(1 + antennas[0, ind - 1] ** 2)
|
|
19
|
+
antennas[0, ind] = antennas[0, ind - 1] + d_theta
|
|
20
|
+
antennas[2, ind] = range_coefficient * antennas[0, ind]
|
|
21
|
+
|
|
22
|
+
antennas = coordinates.sph_to_cart(antennas, degrees=False)
|
|
23
|
+
antennas = antennas.reshape((3, 1, antenna_num))
|
|
24
|
+
|
|
25
|
+
return Array(
|
|
26
|
+
pointing=pointing,
|
|
27
|
+
frequency=frequency,
|
|
28
|
+
antennas=antennas,
|
|
29
|
+
)
|
pyant/clib.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""python-interface to c-library beam
|
|
2
|
+
"""
|
|
3
|
+
import sysconfig
|
|
4
|
+
import pathlib
|
|
5
|
+
import ctypes
|
|
6
|
+
|
|
7
|
+
# Load the C-lib
|
|
8
|
+
suffix = sysconfig.get_config_var("EXT_SUFFIX")
|
|
9
|
+
if suffix is None:
|
|
10
|
+
suffix = ".so"
|
|
11
|
+
|
|
12
|
+
# We start by making a path to the current directory.
|
|
13
|
+
pymodule_dir = pathlib.Path(__file__).resolve().parent
|
|
14
|
+
__libpath__ = pymodule_dir / ("clibbeam" + suffix)
|
|
15
|
+
|
|
16
|
+
# Then we open the created shared clibbeam file
|
|
17
|
+
clib_beam = ctypes.cdll.LoadLibrary(__libpath__)
|