emsutil 0.2.2__tar.gz → 0.4.0__tar.gz
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.
- {emsutil-0.2.2 → emsutil-0.4.0}/.bumpversion.toml +1 -1
- {emsutil-0.2.2 → emsutil-0.4.0}/PKG-INFO +3 -1
- emsutil-0.4.0/emerge-install-mumps +32 -0
- {emsutil-0.2.2 → emsutil-0.4.0}/pyproject.toml +3 -1
- {emsutil-0.2.2 → emsutil-0.4.0}/src/emsutil/__init__.py +2 -1
- {emsutil-0.2.2 → emsutil-0.4.0}/src/emsutil/emdata.py +31 -16
- emsutil-0.4.0/src/emsutil/file.py +192 -0
- emsutil-0.4.0/src/emsutil/inexport/ffdata.py +304 -0
- {emsutil-0.2.2 → emsutil-0.4.0}/src/emsutil/material.py +37 -26
- {emsutil-0.2.2 → emsutil-0.4.0}/src/emsutil/themes.py +58 -0
- {emsutil-0.2.2 → emsutil-0.4.0}/uv.lock +79 -1
- emsutil-0.2.2/src/emsutil/inexport/ffdata.py +0 -58
- {emsutil-0.2.2 → emsutil-0.4.0}/.gitignore +0 -0
- {emsutil-0.2.2 → emsutil-0.4.0}/.python-version +0 -0
- {emsutil-0.2.2 → emsutil-0.4.0}/README.md +0 -0
- {emsutil-0.2.2 → emsutil-0.4.0}/src/emsutil/const.py +0 -0
- {emsutil-0.2.2 → emsutil-0.4.0}/src/emsutil/inexport/strutil.py +0 -0
- {emsutil-0.2.2 → emsutil-0.4.0}/src/emsutil/isola.py +0 -0
- {emsutil-0.2.2 → emsutil-0.4.0}/src/emsutil/lib.py +0 -0
- {emsutil-0.2.2 → emsutil-0.4.0}/src/emsutil/plot/__init__.py +0 -0
- {emsutil-0.2.2 → emsutil-0.4.0}/src/emsutil/plot/plot2d.py +0 -0
- {emsutil-0.2.2 → emsutil-0.4.0}/src/emsutil/pyvista/__init__.py +0 -0
- {emsutil-0.2.2 → emsutil-0.4.0}/src/emsutil/pyvista/cmap_maker.py +0 -0
- {emsutil-0.2.2 → emsutil-0.4.0}/src/emsutil/pyvista/display.py +0 -0
- {emsutil-0.2.2 → emsutil-0.4.0}/src/emsutil/pyvista/display_settings.py +0 -0
- {emsutil-0.2.2 → emsutil-0.4.0}/src/emsutil/pyvista/textures/background.png +0 -0
- {emsutil-0.2.2 → emsutil-0.4.0}/src/emsutil/pyvista/textures/tex1.png +0 -0
- {emsutil-0.2.2 → emsutil-0.4.0}/src/emsutil/pyvista/textures/tex2.png +0 -0
- {emsutil-0.2.2 → emsutil-0.4.0}/src/emsutil/pyvista/textures/tex3.png +0 -0
- {emsutil-0.2.2 → emsutil-0.4.0}/src/emsutil/pyvista/textures/tex4.png +0 -0
- {emsutil-0.2.2 → emsutil-0.4.0}/src/emsutil/pyvista/textures/tex5.png +0 -0
- {emsutil-0.2.2 → emsutil-0.4.0}/src/emsutil/pyvista/textures/tex6.png +0 -0
- {emsutil-0.2.2 → emsutil-0.4.0}/src/emsutil/pyvista/utils.py +0 -0
- {emsutil-0.2.2 → emsutil-0.4.0}/src/emsutil/rogers.py +0 -0
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: emsutil
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Common utilities for Emerge projects EMerge, Optycal and Heavi
|
|
5
5
|
Project-URL: Homepage, https://github.com/FennisRobert/emsutil
|
|
6
6
|
Project-URL: Issues, https://github.com/FennisRobert/emsutil/issues
|
|
7
7
|
Requires-Python: >=3.10
|
|
8
8
|
Requires-Dist: loguru>=0.7.3
|
|
9
9
|
Requires-Dist: matplotlib>=3.8.0
|
|
10
|
+
Requires-Dist: msgpack-numpy>=0.4.8
|
|
11
|
+
Requires-Dist: msgpack>=1.1.2
|
|
10
12
|
Requires-Dist: numpy<2.3,>=1.24
|
|
11
13
|
Requires-Dist: pyvista>=0.46.4
|
|
12
14
|
Requires-Dist: scipy>=1.14.0
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -e
|
|
3
|
+
|
|
4
|
+
# Resolve Python environment
|
|
5
|
+
PYTHON="$(command -v python)"
|
|
6
|
+
|
|
7
|
+
if [ -z "$PYTHON" ]; then
|
|
8
|
+
echo "ERROR: No active Python environment found."
|
|
9
|
+
exit 1
|
|
10
|
+
fi
|
|
11
|
+
|
|
12
|
+
echo "Using Python:"
|
|
13
|
+
echo " $PYTHON"
|
|
14
|
+
|
|
15
|
+
# Location of bundled installer
|
|
16
|
+
INSTALLER_ROOT="$HOME/.emerge/installer"
|
|
17
|
+
|
|
18
|
+
if [ ! -f "$INSTALLER_ROOT/install.sh" ]; then
|
|
19
|
+
echo "ERROR: MUMPS installer not found."
|
|
20
|
+
echo "Expected:"
|
|
21
|
+
echo " $INSTALLER_ROOT/install.sh"
|
|
22
|
+
exit 1
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
# Run installer
|
|
26
|
+
bash "$INSTALLER_ROOT/install.sh"
|
|
27
|
+
|
|
28
|
+
# Install Python package into *this* environment
|
|
29
|
+
"$PYTHON" -m pip install "$HOME/.emerge/mumps_py"
|
|
30
|
+
|
|
31
|
+
echo
|
|
32
|
+
echo "MUMPS successfully installed for this Python environment."
|
|
@@ -4,13 +4,15 @@ allow-direct-references = true
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "emsutil"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.4.0"
|
|
8
8
|
description = "Common utilities for Emerge projects EMerge, Optycal and Heavi"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
11
11
|
dependencies = [
|
|
12
12
|
"loguru>=0.7.3",
|
|
13
13
|
"matplotlib>=3.8.0",
|
|
14
|
+
"msgpack>=1.1.2",
|
|
15
|
+
"msgpack-numpy>=0.4.8",
|
|
14
16
|
"numpy>=1.24, <2.3",
|
|
15
17
|
"pyvista>=0.46.4",
|
|
16
18
|
"scipy>=1.14.0",
|
|
@@ -4,4 +4,5 @@ from .emdata import FarFieldComponent, EHFieldFF, EHField
|
|
|
4
4
|
from .material import Material, MatProperty, FreqCoordDependent, FreqDependent, CoordDependent
|
|
5
5
|
from .lib import EISO, EOMNI, EPS0, AIR, PEC, COPPER
|
|
6
6
|
from .pyvista.display_settings import EMergeTheme
|
|
7
|
-
from . import themes
|
|
7
|
+
from . import themes
|
|
8
|
+
from .file import Saveable, save_object, load_object
|
|
@@ -3,10 +3,13 @@ from dataclasses import dataclass, field
|
|
|
3
3
|
from typing import Literal, Callable
|
|
4
4
|
from .const import Z0, EPS0
|
|
5
5
|
from .lib import EISO, EOMNI
|
|
6
|
+
from .file import Saveable
|
|
6
7
|
|
|
7
8
|
@dataclass
|
|
8
|
-
class FarFieldComponent:
|
|
9
|
+
class FarFieldComponent(Saveable):
|
|
9
10
|
F: np.ndarray
|
|
11
|
+
_th: np.ndarray
|
|
12
|
+
_ph: np.ndarray
|
|
10
13
|
|
|
11
14
|
@property
|
|
12
15
|
def x(self) -> np.ndarray:
|
|
@@ -22,16 +25,16 @@ class FarFieldComponent:
|
|
|
22
25
|
|
|
23
26
|
@property
|
|
24
27
|
def theta(self) -> np.ndarray:
|
|
25
|
-
thx = np.cos(self.
|
|
26
|
-
thy = np.cos(self.
|
|
27
|
-
thz = -np.sin(self.
|
|
28
|
+
thx = np.cos(self._th)*np.cos(self._ph)
|
|
29
|
+
thy = np.cos(self._th)*np.sin(self._ph)
|
|
30
|
+
thz = -np.sin(self._th)
|
|
28
31
|
return thx*self.F[0,:] + thy*self.F[1,:] + thz*self.F[2,:]
|
|
29
32
|
|
|
30
33
|
@property
|
|
31
34
|
def phi(self) -> np.ndarray:
|
|
32
|
-
phx = -np.sin(self.
|
|
33
|
-
phy = np.cos(self.
|
|
34
|
-
phz = np.zeros_like(self.
|
|
35
|
+
phx = -np.sin(self._ph)
|
|
36
|
+
phy = np.cos(self._ph)
|
|
37
|
+
phz = np.zeros_like(self._th)
|
|
35
38
|
return phx*self.F[0,:] + phy*self.F[1,:] + phz*self.F[2,:]
|
|
36
39
|
|
|
37
40
|
@property
|
|
@@ -55,7 +58,7 @@ class FarFieldComponent:
|
|
|
55
58
|
|
|
56
59
|
|
|
57
60
|
@dataclass
|
|
58
|
-
class FieldPlotData:
|
|
61
|
+
class FieldPlotData(Saveable):
|
|
59
62
|
x: np.ndarray
|
|
60
63
|
y: np.ndarray
|
|
61
64
|
z: np.ndarray
|
|
@@ -80,6 +83,16 @@ class FieldPlotData:
|
|
|
80
83
|
"""
|
|
81
84
|
return (self.x, self.y, self.z, self.F)
|
|
82
85
|
|
|
86
|
+
@property
|
|
87
|
+
def xyzvec(self) -> tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
|
|
88
|
+
"""Returns the X, Y, Z, F arrays.
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: The X, Y, Z, F arrays
|
|
92
|
+
"""
|
|
93
|
+
return (self.x, self.y, self.z, self.vx, self.vy, self.vz)
|
|
94
|
+
|
|
95
|
+
|
|
83
96
|
@property
|
|
84
97
|
def xyzftri(self) -> tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
|
|
85
98
|
"""Returns the X, Y, Z, F + triangulation arrays.
|
|
@@ -115,13 +128,14 @@ class FieldPlotData:
|
|
|
115
128
|
yield self.F
|
|
116
129
|
|
|
117
130
|
@dataclass
|
|
118
|
-
class EHFieldFF:
|
|
131
|
+
class EHFieldFF(Saveable):
|
|
119
132
|
_E: np.ndarray
|
|
120
133
|
_H: np.ndarray
|
|
121
134
|
theta: np.ndarray
|
|
122
135
|
phi: np.ndarray
|
|
123
136
|
Ptot: float
|
|
124
137
|
ang: np.ndarray | None = field(default=None)
|
|
138
|
+
freq: float | None = field(default=None)
|
|
125
139
|
|
|
126
140
|
def total_radiated_power_integral(
|
|
127
141
|
self,
|
|
@@ -183,11 +197,11 @@ class EHFieldFF:
|
|
|
183
197
|
|
|
184
198
|
@property
|
|
185
199
|
def E(self) -> np.ndarray:
|
|
186
|
-
return FarFieldComponent(self._E)
|
|
200
|
+
return FarFieldComponent(self._E, self.theta, self.phi)
|
|
187
201
|
|
|
188
202
|
@property
|
|
189
203
|
def H(self) -> np.ndarray:
|
|
190
|
-
return FarFieldComponent(self._H)
|
|
204
|
+
return FarFieldComponent(self._H, self.theta, self.phi)
|
|
191
205
|
|
|
192
206
|
@property
|
|
193
207
|
def Ex(self) -> np.ndarray:
|
|
@@ -244,16 +258,16 @@ class EHFieldFF:
|
|
|
244
258
|
@property
|
|
245
259
|
def gain(self, kind: Literal['iso','omni'] = 'iso') -> FarFieldComponent:
|
|
246
260
|
if kind=='iso':
|
|
247
|
-
return FarFieldComponent(self._E/EISO)
|
|
261
|
+
return FarFieldComponent(self._E/EISO, self.theta, self.phi)
|
|
248
262
|
else:
|
|
249
|
-
return FarFieldComponent(self._E/EOMNI)
|
|
263
|
+
return FarFieldComponent(self._E/EOMNI, self.theta, self.phi)
|
|
250
264
|
|
|
251
265
|
@property
|
|
252
266
|
def dir(self, kind: Literal['iso','omni'] = 'iso') -> FarFieldComponent:
|
|
253
267
|
if kind=='iso':
|
|
254
|
-
return FarFieldComponent(self._E/(EISO*(self.Ptot)**0.5))
|
|
268
|
+
return FarFieldComponent(self._E/(EISO*(self.Ptot)**0.5), self.theta, self.phi)
|
|
255
269
|
else:
|
|
256
|
-
return FarFieldComponent(self._E/(EOMNI*(self.Ptot)**0.5))
|
|
270
|
+
return FarFieldComponent(self._E/(EOMNI*(self.Ptot)**0.5), self.theta, self.phi)
|
|
257
271
|
|
|
258
272
|
@property
|
|
259
273
|
def normE(self) -> np.ndarray:
|
|
@@ -306,7 +320,7 @@ class EHFieldFF:
|
|
|
306
320
|
return FieldPlotData(x=xs, y=ys, z=zs, F=F)
|
|
307
321
|
|
|
308
322
|
@dataclass
|
|
309
|
-
class EHField:
|
|
323
|
+
class EHField(Saveable):
|
|
310
324
|
_E: np.ndarray
|
|
311
325
|
_H: np.ndarray
|
|
312
326
|
x: np.ndarray
|
|
@@ -626,6 +640,7 @@ class EHField:
|
|
|
626
640
|
"""
|
|
627
641
|
Fx, Fy, Fz = getattr(self, field)
|
|
628
642
|
|
|
643
|
+
|
|
629
644
|
if metric=='real':
|
|
630
645
|
Fx, Fy, Fz = Fx.real, Fy.real, Fz.real
|
|
631
646
|
elif metric=='imag':
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
import numpy as np
|
|
3
|
+
from scipy.sparse import csc_matrix, csr_matrix, isspmatrix
|
|
4
|
+
from numbers import Number
|
|
5
|
+
import msgpack
|
|
6
|
+
import msgpack_numpy as m
|
|
7
|
+
|
|
8
|
+
m.patch()
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Saveable:
|
|
12
|
+
save_fields: list[str] = None
|
|
13
|
+
skip_fields: list[str] = None
|
|
14
|
+
_registry = dict()
|
|
15
|
+
|
|
16
|
+
def __init_subclass__(cls, **kwargs):
|
|
17
|
+
super().__init_subclass__(**kwargs)
|
|
18
|
+
cls._registry[cls.__name__] = cls
|
|
19
|
+
|
|
20
|
+
@classmethod
|
|
21
|
+
def is_saveable(cls, obj) -> bool:
|
|
22
|
+
if not isinstance(obj, dict):
|
|
23
|
+
return False
|
|
24
|
+
if '__SAVEABLE__' in obj:
|
|
25
|
+
return True
|
|
26
|
+
|
|
27
|
+
def pack(self) -> dict:
|
|
28
|
+
if self.skip_fields is None:
|
|
29
|
+
skip = []
|
|
30
|
+
else:
|
|
31
|
+
skip = self.skip_fields
|
|
32
|
+
|
|
33
|
+
if self.save_fields is None:
|
|
34
|
+
data = dict()
|
|
35
|
+
data['__CLASS__'] = self.__class__.__name__
|
|
36
|
+
data['__SAVEABLE__'] = True
|
|
37
|
+
data['__FIELDS__'] = {key: value for key, value in self.__dict__.items()
|
|
38
|
+
if '__' not in key and key not in skip}
|
|
39
|
+
return data
|
|
40
|
+
else:
|
|
41
|
+
data = dict()
|
|
42
|
+
data['__CLASS__'] = self.__class__.__name__
|
|
43
|
+
data['__SAVEABLE__'] = True
|
|
44
|
+
data['__FIELDS__'] = {key: value for key, value in self.__dict__.items() if key in self.save_fields and key not in skip}
|
|
45
|
+
return data
|
|
46
|
+
|
|
47
|
+
@staticmethod
|
|
48
|
+
def unpack(data: dict, collection: dict) -> Saveable:
|
|
49
|
+
cls = Saveable._registry[data['__CLASS__']]
|
|
50
|
+
fields = data['__FIELDS__']
|
|
51
|
+
obj = cls.__new__(cls)
|
|
52
|
+
for field, value in fields.items():
|
|
53
|
+
setattr(obj, field, _unpack_data(value, collection))
|
|
54
|
+
return obj
|
|
55
|
+
|
|
56
|
+
def _is_set(data) -> bool:
|
|
57
|
+
if not isinstance(data, dict):
|
|
58
|
+
return False
|
|
59
|
+
if '__ISSET__' in data:
|
|
60
|
+
return True
|
|
61
|
+
|
|
62
|
+
def _pack_set(data) -> dict:
|
|
63
|
+
return {
|
|
64
|
+
'__ISSET__': True,
|
|
65
|
+
'__DATA__': list(data)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
def _is_type(data) -> bool:
|
|
69
|
+
if not isinstance(data, dict):
|
|
70
|
+
return False
|
|
71
|
+
if '__CLASS_TYPE__' in data:
|
|
72
|
+
return True
|
|
73
|
+
|
|
74
|
+
def _pack_type(data) -> dict:
|
|
75
|
+
return {
|
|
76
|
+
'__CLASS_TYPE__': True,
|
|
77
|
+
'__CLASS_NAME__': f"{data.__module__}.{data.__qualname__}"
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
def _is_saved(data) -> bool:
|
|
81
|
+
if not isinstance(data, dict):
|
|
82
|
+
return False
|
|
83
|
+
if '__SAVED_OBJ__' in data:
|
|
84
|
+
return True
|
|
85
|
+
|
|
86
|
+
def _pack_saved(data) -> dict:
|
|
87
|
+
return {
|
|
88
|
+
'__SAVED_OBJ__': True,
|
|
89
|
+
'__OBJ_ID__': id(data)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
def _unpack_saved(data: dict) -> int:
|
|
93
|
+
return data['__OBJ_ID__']
|
|
94
|
+
|
|
95
|
+
def _unpack_type(data: dict):
|
|
96
|
+
class_name = data['__CLASS_NAME__']
|
|
97
|
+
module_name, _, cls_name = class_name.rpartition('.')
|
|
98
|
+
module = __import__(module_name, fromlist=[cls_name])
|
|
99
|
+
cls = getattr(module, cls_name)
|
|
100
|
+
return cls
|
|
101
|
+
|
|
102
|
+
def _unpack_set(data: dict) -> set:
|
|
103
|
+
return set(data['__DATA__'])
|
|
104
|
+
|
|
105
|
+
def _pack_object(obj, _obj_collector: dict = None) -> dict:
|
|
106
|
+
if obj is None:
|
|
107
|
+
return obj
|
|
108
|
+
if isinstance(obj, (str, Number, np.ndarray, bool)):
|
|
109
|
+
return obj
|
|
110
|
+
elif isinstance(obj, set):
|
|
111
|
+
return _pack_set(obj)
|
|
112
|
+
elif isspmatrix(obj):
|
|
113
|
+
return obj
|
|
114
|
+
elif isinstance(obj, list):
|
|
115
|
+
return [_pack_object(item, _obj_collector) for item in obj]
|
|
116
|
+
elif isinstance(obj, tuple):
|
|
117
|
+
return tuple(_pack_object(item, _obj_collector) for item in obj)
|
|
118
|
+
elif isinstance(obj, dict):
|
|
119
|
+
return {key: _pack_object(value, _obj_collector) for key, value in obj.items()}
|
|
120
|
+
elif isinstance(obj, Saveable):
|
|
121
|
+
if id(obj) in _obj_collector:
|
|
122
|
+
return _obj_collector[id(obj)]['reference']
|
|
123
|
+
data = _pack_saved(obj)
|
|
124
|
+
_obj_collector[data['__OBJ_ID__']] = {'object': _pack_object(obj.pack(), _obj_collector), 'reference': data}
|
|
125
|
+
return data
|
|
126
|
+
elif isinstance(obj, type):
|
|
127
|
+
return _pack_type(obj)
|
|
128
|
+
else:
|
|
129
|
+
print(f'Trying to unpack {obj} of type {type(obj)}')
|
|
130
|
+
raise TypeError(f"Cannot pack object of type {type(obj)}")
|
|
131
|
+
|
|
132
|
+
def _unpack_data(data, _obj_collection):
|
|
133
|
+
if _is_saved(data):
|
|
134
|
+
obj_id = _unpack_saved(data)
|
|
135
|
+
saved_data = _obj_collection[obj_id]['object']
|
|
136
|
+
return _unpack_data(saved_data, _obj_collection)
|
|
137
|
+
if Saveable.is_saveable(data):
|
|
138
|
+
return Saveable.unpack(data, _obj_collection)
|
|
139
|
+
elif data is None:
|
|
140
|
+
return None
|
|
141
|
+
elif _is_type(data):
|
|
142
|
+
return _unpack_type(data)
|
|
143
|
+
elif _is_set(data):
|
|
144
|
+
return _unpack_set(data)
|
|
145
|
+
elif isinstance(data, np.ndarray):
|
|
146
|
+
return data.copy()
|
|
147
|
+
elif isinstance(data, (str, Number, np.ndarray, bool)):
|
|
148
|
+
return data
|
|
149
|
+
elif isinstance(data, list):
|
|
150
|
+
return [_unpack_data(item, _obj_collection) for item in data]
|
|
151
|
+
elif isinstance(data, tuple):
|
|
152
|
+
return tuple(_unpack_data(item, _obj_collection) for item in data)
|
|
153
|
+
elif isinstance(data, dict):
|
|
154
|
+
return {key: _unpack_data(value, _obj_collection) for key, value in data.items()}
|
|
155
|
+
elif isspmatrix(data):
|
|
156
|
+
return data
|
|
157
|
+
else:
|
|
158
|
+
raise TypeError(f"Cannot unpack data of type {type(data)}")
|
|
159
|
+
|
|
160
|
+
def save_object(filename: str, data: dict):
|
|
161
|
+
"""Saves a class to a file using msgpack serialization.
|
|
162
|
+
|
|
163
|
+
Args:
|
|
164
|
+
filename (str): The filename for the final object
|
|
165
|
+
data (dict): The data to store
|
|
166
|
+
"""
|
|
167
|
+
obj_collector = dict()
|
|
168
|
+
dataset = _pack_object(data, obj_collector)
|
|
169
|
+
save_dict = {
|
|
170
|
+
'data': dataset,
|
|
171
|
+
'objects': obj_collector
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
with open(filename, 'wb') as f:
|
|
175
|
+
f.write(msgpack.packb(save_dict, use_bin_type=True))
|
|
176
|
+
|
|
177
|
+
def load_object(filename: str) -> dict:
|
|
178
|
+
"""Loads a class from a file using msgpack serialization.
|
|
179
|
+
|
|
180
|
+
Args:
|
|
181
|
+
filename (str): The filename of the dataset to load
|
|
182
|
+
|
|
183
|
+
Returns:
|
|
184
|
+
dict: The dict() object with the loaded data.
|
|
185
|
+
"""
|
|
186
|
+
with open(filename, 'rb') as f:
|
|
187
|
+
loaded = msgpack.unpackb(f.read(), raw=False, strict_map_key=False, use_list=False)
|
|
188
|
+
|
|
189
|
+
data = loaded['data']
|
|
190
|
+
obj_collection = loaded['objects']
|
|
191
|
+
return _unpack_data(data, obj_collection)
|
|
192
|
+
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from .strutil import arry_to_line, matrix_to_lines, arry_to_fwl
|
|
3
|
+
from ..emdata import EHFieldFF
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from typing import List
|
|
6
|
+
from loguru import logger
|
|
7
|
+
import os
|
|
8
|
+
|
|
9
|
+
@dataclass
|
|
10
|
+
class Column:
|
|
11
|
+
name: str
|
|
12
|
+
values: np.ndarray
|
|
13
|
+
|
|
14
|
+
@staticmethod
|
|
15
|
+
def from_array(name: str, data: np.ndarray) -> List["Column"]:
|
|
16
|
+
"""
|
|
17
|
+
Returns:
|
|
18
|
+
- [Column] if data is real
|
|
19
|
+
- [Column(real), Column(imag)] if data is complex
|
|
20
|
+
"""
|
|
21
|
+
data = np.asarray(data)
|
|
22
|
+
|
|
23
|
+
if np.iscomplexobj(data):
|
|
24
|
+
return [
|
|
25
|
+
Column(f"{name}[re]", data.real),
|
|
26
|
+
Column(f"{name}[im]", data.imag),
|
|
27
|
+
]
|
|
28
|
+
else:
|
|
29
|
+
return [Column(name, data)]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def to_lines(
|
|
33
|
+
columns: List[Column],
|
|
34
|
+
header_prefix: str = "$ ",
|
|
35
|
+
separator: str = " ",
|
|
36
|
+
fmt: str = "{:g}",
|
|
37
|
+
) -> List[str]:
|
|
38
|
+
"""
|
|
39
|
+
Convert columns to equally spaced text lines.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
# Convert all values to strings first
|
|
43
|
+
str_cols = [[fmt.format(v) for v in col.values] for col in columns]
|
|
44
|
+
headers = [col.name for col in columns]
|
|
45
|
+
headers[0] = header_prefix + headers[0]
|
|
46
|
+
for i in range(len(headers)-1):
|
|
47
|
+
headers[i] = headers[i] + ';'
|
|
48
|
+
# Compute column widths (including separator!)
|
|
49
|
+
widths = []
|
|
50
|
+
for h, col in zip(headers, str_cols):
|
|
51
|
+
widths.append(max(len(h), max(len(v) for v in col)))
|
|
52
|
+
|
|
53
|
+
def format_row(items, sep: str):
|
|
54
|
+
return sep.join(item.ljust(w) for item, w in zip(items, widths))
|
|
55
|
+
|
|
56
|
+
lines = []
|
|
57
|
+
|
|
58
|
+
# Header
|
|
59
|
+
lines.append(format_row(headers, separator))
|
|
60
|
+
|
|
61
|
+
# Data rows
|
|
62
|
+
nrows = len(columns[0].values)
|
|
63
|
+
for i in range(nrows):
|
|
64
|
+
row = [col[i] for col in str_cols]
|
|
65
|
+
lines.append(format_row(row, separator))
|
|
66
|
+
|
|
67
|
+
return lines
|
|
68
|
+
|
|
69
|
+
class _fieldGetter:
|
|
70
|
+
|
|
71
|
+
def __init__(self, fields: list[EHFieldFF]):
|
|
72
|
+
self.names: list[str] = []
|
|
73
|
+
self.data: list = fields
|
|
74
|
+
|
|
75
|
+
@property
|
|
76
|
+
def name(self) -> str:
|
|
77
|
+
return '.'.join([n.capitalize() for n in self.names])
|
|
78
|
+
|
|
79
|
+
def __getattr__(self, name: str) -> EHFieldFF:
|
|
80
|
+
self.names.append(name)
|
|
81
|
+
self.data = [getattr(item, name) for item in self.data]
|
|
82
|
+
return self
|
|
83
|
+
|
|
84
|
+
class FarFieldExporter:
|
|
85
|
+
"""A class to export far-field data to a text file.
|
|
86
|
+
|
|
87
|
+
"""
|
|
88
|
+
def __init__(self, filename: str, fields: list[EHFieldFF], precision: int = 4, todeg: bool = True):
|
|
89
|
+
self.filename: str = filename
|
|
90
|
+
self.fields: list[EHFieldFF] = fields
|
|
91
|
+
self.precision: int = precision
|
|
92
|
+
self.columns: list[_fieldGetter] = []
|
|
93
|
+
self.todeg: bool = todeg
|
|
94
|
+
|
|
95
|
+
def addcol(self) -> EHFieldFF:
|
|
96
|
+
"""Returns a field getter to specify which field component to export.
|
|
97
|
+
|
|
98
|
+
Example:
|
|
99
|
+
>>> exporter = FarFieldExporter('data.txt', field_data)
|
|
100
|
+
>>> exporter.addcol().Ex
|
|
101
|
+
>>> exporter.addcol().gain.theta
|
|
102
|
+
>>> exporter.write()
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
EHFieldFF(_fieldGetter): Field getter to specify field component.
|
|
106
|
+
"""
|
|
107
|
+
getter = _fieldGetter(self.fields)
|
|
108
|
+
self.columns.append(getter)
|
|
109
|
+
return getter
|
|
110
|
+
|
|
111
|
+
def write(self) -> None:
|
|
112
|
+
"""Saves the far-field data to a text file.
|
|
113
|
+
"""
|
|
114
|
+
base = self.fields[0]
|
|
115
|
+
|
|
116
|
+
thetas = base.theta
|
|
117
|
+
phis = base.phi
|
|
118
|
+
|
|
119
|
+
theta_lin = thetas[0,:].flatten()
|
|
120
|
+
phi_lin = phis[:,0].flatten()
|
|
121
|
+
p = self.precision
|
|
122
|
+
mult = 1.0
|
|
123
|
+
unit = 'rad'
|
|
124
|
+
if self.todeg:
|
|
125
|
+
mult = 180.0 / np.pi
|
|
126
|
+
theta_lin = theta_lin * mult
|
|
127
|
+
phi_lin = phi_lin * mult
|
|
128
|
+
thetas = thetas * mult
|
|
129
|
+
phis = phis * mult
|
|
130
|
+
unit = 'deg'
|
|
131
|
+
|
|
132
|
+
datalines = []
|
|
133
|
+
datalines.append(f'% Theta({unit})')
|
|
134
|
+
datalines.append(' '.join([f'{x:.{p}g}' for x in theta_lin]))
|
|
135
|
+
datalines.append(f'% Phi({unit})')
|
|
136
|
+
datalines.append(' '.join([f'{x:.{p}g}' for x in phi_lin]))
|
|
137
|
+
datalines.append('% Frequencies(Hz)')
|
|
138
|
+
datalines.append(' '.join([f'{field.freq:.{p}g}' for field in self.fields]))
|
|
139
|
+
datalines.append('')
|
|
140
|
+
thcol = Column(f'Theta({unit})', thetas.flatten())
|
|
141
|
+
phcol = Column(f'Phi({unit})', phis.flatten())
|
|
142
|
+
|
|
143
|
+
for i, field in enumerate(self.fields):
|
|
144
|
+
freq = field.freq
|
|
145
|
+
cols = []
|
|
146
|
+
datalines.append(f'# {freq:.{p}g} Hz')
|
|
147
|
+
for col in self.columns:
|
|
148
|
+
cols.extend(Column.from_array(col.name, col.data[i].flatten()))
|
|
149
|
+
|
|
150
|
+
datalines.extend(to_lines([thcol, phcol] + cols, fmt=f"{{:.{p}g}}"))
|
|
151
|
+
datalines.append('')
|
|
152
|
+
|
|
153
|
+
#replace any extension of filename with.emff
|
|
154
|
+
filename = os.path.splitext(self.filename)[0] + '.emff'
|
|
155
|
+
self.filename = filename
|
|
156
|
+
with open(self.filename, 'w') as f:
|
|
157
|
+
f.write('\n'.join(datalines))
|
|
158
|
+
logger.info(f'Wrote far-field data to {self.filename}')
|
|
159
|
+
|
|
160
|
+
def import_farfield(filename: str, degrees: bool = True) -> dict[str, np.ndarray]:
|
|
161
|
+
"""Reads emerge farfiel datafiles created by FarFieldExporter.
|
|
162
|
+
They can be recognized by the .emff extension.
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
filename (str): The path to the far-field data file.
|
|
166
|
+
degrees (bool): If True, theta and phi angles are converted to degrees.
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
dict[str, np.ndarray]: The imported far-field data structured as:
|
|
170
|
+
{
|
|
171
|
+
"theta": np.ndarray, # Theta grid (nTheta, nPhi)
|
|
172
|
+
"phi": np.ndarray, # Phi grid (nTheta, nPhi)
|
|
173
|
+
"freq": np.ndarray, # Frequencies (nF,)
|
|
174
|
+
"Ex": np.ndarray, # Electric field component Ex (nF, nTheta, nPhi)
|
|
175
|
+
"Ey": np.ndarray, # Electric field component Ey (nF, nTheta, nPhi)
|
|
176
|
+
...
|
|
177
|
+
"""
|
|
178
|
+
with open(filename, "r") as f:
|
|
179
|
+
lines = f.readlines()
|
|
180
|
+
|
|
181
|
+
# ---------------- helpers ----------------
|
|
182
|
+
def parse_vector(header):
|
|
183
|
+
i = lines.index(header) + 1
|
|
184
|
+
return np.array(lines[i].split(), float)
|
|
185
|
+
|
|
186
|
+
def strip_unit(name):
|
|
187
|
+
if "(" in name and ")" in name:
|
|
188
|
+
base, unit = name.split("(")
|
|
189
|
+
return base.strip(), unit.rstrip(")")
|
|
190
|
+
return name.strip(), None
|
|
191
|
+
|
|
192
|
+
def to_rad(values, unit):
|
|
193
|
+
if unit == "deg":
|
|
194
|
+
return np.deg2rad(values)
|
|
195
|
+
return values
|
|
196
|
+
|
|
197
|
+
# ---------------- global grids ----------------
|
|
198
|
+
theta_name, theta_unit = strip_unit("% Theta(deg)")
|
|
199
|
+
phi_name, phi_unit = strip_unit("% Phi(deg)")
|
|
200
|
+
|
|
201
|
+
# Find actual headers (deg or rad)
|
|
202
|
+
theta_header = next(l for l in lines if l.startswith("% Theta"))
|
|
203
|
+
phi_header = next(l for l in lines if l.startswith("% Phi"))
|
|
204
|
+
|
|
205
|
+
theta_vals = parse_vector(theta_header)
|
|
206
|
+
phi_vals = parse_vector(phi_header)
|
|
207
|
+
freqs = parse_vector("% Frequencies(Hz)\n")
|
|
208
|
+
|
|
209
|
+
theta_unit = theta_header.split("(")[1].split(")")[0]
|
|
210
|
+
phi_unit = phi_header.split("(")[1].split(")")[0]
|
|
211
|
+
|
|
212
|
+
theta_vals = to_rad(theta_vals, theta_unit)
|
|
213
|
+
phi_vals = to_rad(phi_vals, phi_unit)
|
|
214
|
+
|
|
215
|
+
nTheta = len(theta_vals)
|
|
216
|
+
nPhi = len(phi_vals)
|
|
217
|
+
nF = len(freqs)
|
|
218
|
+
|
|
219
|
+
theta_grid, phi_grid = np.meshgrid(theta_vals, phi_vals, indexing="ij")
|
|
220
|
+
|
|
221
|
+
result = {
|
|
222
|
+
"theta": theta_grid,
|
|
223
|
+
"phi": phi_grid,
|
|
224
|
+
"freq": freqs,
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
# ---------------- frequency blocks ----------------
|
|
228
|
+
field_buffers = {}
|
|
229
|
+
column_map = {}
|
|
230
|
+
header_info = {}
|
|
231
|
+
fidx = -1
|
|
232
|
+
header_cols = None
|
|
233
|
+
data_rows = []
|
|
234
|
+
|
|
235
|
+
for line in lines:
|
|
236
|
+
line = line.strip()
|
|
237
|
+
if not line:
|
|
238
|
+
continue
|
|
239
|
+
|
|
240
|
+
# Frequency block
|
|
241
|
+
if line.startswith("#"):
|
|
242
|
+
fidx += 1
|
|
243
|
+
data_rows = []
|
|
244
|
+
continue
|
|
245
|
+
|
|
246
|
+
# Header line
|
|
247
|
+
if line.startswith("$"):
|
|
248
|
+
header_cols = []
|
|
249
|
+
column_map.clear()
|
|
250
|
+
header_info.clear()
|
|
251
|
+
|
|
252
|
+
raw_cols = line[1:].split(";")
|
|
253
|
+
for i, raw in enumerate(raw_cols):
|
|
254
|
+
name, unit = strip_unit(raw)
|
|
255
|
+
header_cols.append(name)
|
|
256
|
+
header_info[name] = unit
|
|
257
|
+
|
|
258
|
+
if "[" in name and "]" in name:
|
|
259
|
+
base, comp = name.split("[")
|
|
260
|
+
comp = comp.rstrip("]")
|
|
261
|
+
column_map.setdefault(base, {})[comp] = i
|
|
262
|
+
else:
|
|
263
|
+
column_map[name] = i
|
|
264
|
+
continue
|
|
265
|
+
|
|
266
|
+
if line.startswith("%"):
|
|
267
|
+
continue
|
|
268
|
+
|
|
269
|
+
parts = line.split()
|
|
270
|
+
if header_cols is None or len(parts) < len(header_cols):
|
|
271
|
+
continue
|
|
272
|
+
|
|
273
|
+
data_rows.append([float(p) for p in parts])
|
|
274
|
+
|
|
275
|
+
if len(data_rows) == nTheta * nPhi:
|
|
276
|
+
data = np.array(data_rows)
|
|
277
|
+
|
|
278
|
+
for field, comps in column_map.items():
|
|
279
|
+
unit = header_info.get(field)
|
|
280
|
+
|
|
281
|
+
if isinstance(comps, dict): # complex
|
|
282
|
+
re = data[:, comps["re"]]
|
|
283
|
+
im = data[:, comps["im"]]
|
|
284
|
+
arr = re + 1j * im
|
|
285
|
+
else: # real
|
|
286
|
+
arr = data[:, comps]
|
|
287
|
+
|
|
288
|
+
if unit in ("deg", "rad"):
|
|
289
|
+
arr = to_rad(arr, unit)
|
|
290
|
+
|
|
291
|
+
arr = arr.reshape(nTheta, nPhi)
|
|
292
|
+
field_buffers.setdefault(field, []).append(arr)
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
# Stack frequencies
|
|
296
|
+
for field, blocks in field_buffers.items():
|
|
297
|
+
result[field] = np.stack(blocks, axis=0)
|
|
298
|
+
|
|
299
|
+
if degrees:
|
|
300
|
+
result["theta"] = np.rad2deg(result["theta"])
|
|
301
|
+
result["phi"] = np.rad2deg(result["phi"])
|
|
302
|
+
result["Theta"] = np.rad2deg(result["Theta"])
|
|
303
|
+
result["Phi"] = np.rad2deg(result["Phi"])
|
|
304
|
+
return result
|
|
@@ -19,6 +19,7 @@ from __future__ import annotations
|
|
|
19
19
|
import numpy as np
|
|
20
20
|
from typing import Callable
|
|
21
21
|
import inspect
|
|
22
|
+
from .file import Saveable
|
|
22
23
|
|
|
23
24
|
C0 = 299792458
|
|
24
25
|
|
|
@@ -45,15 +46,16 @@ def _to_mat(value: float | complex | int | np.ndarray) -> np.ndarray:
|
|
|
45
46
|
else:
|
|
46
47
|
return ValueError(f'Trying to parse {value} as a material property tensor but it cant be identified as scalar, vector or matrix')
|
|
47
48
|
|
|
48
|
-
class MatProperty:
|
|
49
|
+
class MatProperty(Saveable):
|
|
49
50
|
_freq_dependent: bool = False
|
|
50
51
|
_coord_dependent: bool = False
|
|
51
52
|
_pickle_exclude = {"_func","_fmax"}
|
|
53
|
+
skip_fields = ("_func","_fmax")
|
|
52
54
|
"""The MatProperty class is an interface for EMerge to deal with frequency and coordinate dependent material properties
|
|
53
55
|
"""
|
|
54
56
|
|
|
55
57
|
def __init__(self, value: float | complex | int | np.ndarray):
|
|
56
|
-
self.
|
|
58
|
+
self._value: np.ndarray = _to_mat(value)
|
|
57
59
|
|
|
58
60
|
self._apply_to: np.ndarray = np.array([], dtype=np.int64)
|
|
59
61
|
self._x: np.ndarray = np.array([], dtype=np.float64)
|
|
@@ -69,11 +71,15 @@ class MatProperty:
|
|
|
69
71
|
self._z = np.concatenate([self._z, z])
|
|
70
72
|
|
|
71
73
|
def __call__(self, f: float, data: np.ndarray) -> np.ndarray:
|
|
72
|
-
data[:,:,self._apply_to] = np.repeat(self.
|
|
74
|
+
data[:,:,self._apply_to] = np.repeat(self._value[:,:,np.newaxis], self._apply_to.shape[0], axis=2)
|
|
73
75
|
return data
|
|
74
76
|
|
|
77
|
+
@property
|
|
78
|
+
def value(self) -> float:
|
|
79
|
+
return self._value[0,0]
|
|
80
|
+
|
|
75
81
|
def scalar(self, f: float):
|
|
76
|
-
return self.
|
|
82
|
+
return self._value[0,0]
|
|
77
83
|
|
|
78
84
|
def reset(self) -> None:
|
|
79
85
|
self._apply_to: np.ndarray = np.array([], dtype=np.int64)
|
|
@@ -93,10 +99,10 @@ class MatProperty:
|
|
|
93
99
|
for k in self._pickle_exclude:
|
|
94
100
|
setattr(self, k, None)
|
|
95
101
|
|
|
96
|
-
class FreqDependent(MatProperty):
|
|
102
|
+
class FreqDependent(MatProperty, Saveable):
|
|
97
103
|
_freq_dependent: bool = True
|
|
98
104
|
_coord_dependent: bool = False
|
|
99
|
-
|
|
105
|
+
skip_fields = ("_func","_fmax")
|
|
100
106
|
def __init__(self,
|
|
101
107
|
scalar: Callable | None = None,
|
|
102
108
|
vector: Callable | None = None,
|
|
@@ -115,8 +121,6 @@ class FreqDependent(MatProperty):
|
|
|
115
121
|
vector (Callable | None, optional): The diagonal rank-2 tensor function returning a (3,) array. Defaults to None.
|
|
116
122
|
matrix (Callable | None, optional): The rank-2 tensor function returning a (3,3) array. Defaults to None.
|
|
117
123
|
|
|
118
|
-
Returns:
|
|
119
|
-
_type_: _description_
|
|
120
124
|
"""
|
|
121
125
|
if scalar is not None:
|
|
122
126
|
def _func(f: float) -> np.ndarray:
|
|
@@ -144,12 +148,17 @@ class FreqDependent(MatProperty):
|
|
|
144
148
|
data[:,:,self._apply_to] = np.repeat(self._func(f)[:,:,np.newaxis], self._apply_to.shape[0], axis=2)
|
|
145
149
|
return data
|
|
146
150
|
|
|
151
|
+
@property
|
|
152
|
+
def value(self) -> float:
|
|
153
|
+
raise ValueError('Frequency dependent material properties have no fixed value. Use the scalar(f) method to get the value at a specific frequency.')
|
|
154
|
+
|
|
147
155
|
def scalar(self, f: float):
|
|
148
156
|
return self._func(f)[0,0]
|
|
149
157
|
|
|
150
|
-
class CoordDependent(MatProperty):
|
|
158
|
+
class CoordDependent(MatProperty,Saveable):
|
|
151
159
|
_freq_dependent: bool = False
|
|
152
160
|
_coord_dependent: bool = True
|
|
161
|
+
skip_fields = ("_func","_fmax")
|
|
153
162
|
def __init__(self,
|
|
154
163
|
max_value: float,
|
|
155
164
|
scalar: Callable | None = None,
|
|
@@ -173,9 +182,7 @@ class CoordDependent(MatProperty):
|
|
|
173
182
|
scalar (Callable | None, optional): The scalar value function returning a float/complex. Defaults to None.
|
|
174
183
|
vector (Callable | None, optional): The diagonal rank-2 tensor function returning a (3,) array. Defaults to None.
|
|
175
184
|
matrix (Callable | None, optional): The rank-2 tensor function returning a (3,3) array. Defaults to None.
|
|
176
|
-
|
|
177
|
-
Returns:
|
|
178
|
-
_type_: _description_
|
|
185
|
+
|
|
179
186
|
"""
|
|
180
187
|
|
|
181
188
|
if scalar is not None:
|
|
@@ -211,13 +218,17 @@ class CoordDependent(MatProperty):
|
|
|
211
218
|
data[:,:,self._apply_to] = self._func(self._x, self._y, self._z)
|
|
212
219
|
return data
|
|
213
220
|
|
|
221
|
+
@property
|
|
222
|
+
def value(self) -> float:
|
|
223
|
+
return self._func(0,0,0)[0,0]
|
|
224
|
+
|
|
214
225
|
def scalar(self, f: float):
|
|
215
226
|
return self._func(0,0,0)[0,0]
|
|
216
227
|
|
|
217
|
-
class FreqCoordDependent(MatProperty):
|
|
228
|
+
class FreqCoordDependent(MatProperty, Saveable):
|
|
218
229
|
_freq_dependent: bool = True
|
|
219
230
|
_coord_dependent: bool = True
|
|
220
|
-
|
|
231
|
+
skip_fields = ("_func","_fmax")
|
|
221
232
|
def __init__(self,
|
|
222
233
|
max_value: float,
|
|
223
234
|
scalar: Callable | None = None,
|
|
@@ -240,8 +251,6 @@ class FreqCoordDependent(MatProperty):
|
|
|
240
251
|
vector (Callable | None, optional): The diagonal rank-2 tensor function returning a (3,) array. Defaults to None.
|
|
241
252
|
matrix (Callable | None, optional): The rank-2 tensor function returning a (3,3) array. Defaults to None.
|
|
242
253
|
|
|
243
|
-
Returns:
|
|
244
|
-
_type_: _description_
|
|
245
254
|
"""
|
|
246
255
|
if scalar is not None:
|
|
247
256
|
def _func(f, x, y, z) -> np.ndarray:
|
|
@@ -277,10 +286,14 @@ class FreqCoordDependent(MatProperty):
|
|
|
277
286
|
data[:,:,self._apply_to] = self._func(f,self._x, self._y,self._z)
|
|
278
287
|
return data
|
|
279
288
|
|
|
289
|
+
@property
|
|
290
|
+
def value(self) -> float:
|
|
291
|
+
raise ValueError('Frequency and coordinate dependent material properties have no fixed value. Use the scalar(f) method to get the value at a specific frequency and coordinate.')
|
|
292
|
+
|
|
280
293
|
def scalar(self, f: float):
|
|
281
294
|
return self._func(f, 0,0,0)[0,0]
|
|
282
295
|
|
|
283
|
-
class Material:
|
|
296
|
+
class Material(Saveable):
|
|
284
297
|
"""The Material class generalizes a material in the EMerge FEM environment.
|
|
285
298
|
|
|
286
299
|
If a scalar value is provided for the relative permittivity or the relative permeability
|
|
@@ -295,7 +308,7 @@ class Material:
|
|
|
295
308
|
|
|
296
309
|
"""
|
|
297
310
|
_pickle_exclude = {"_neff"}
|
|
298
|
-
|
|
311
|
+
skip_fields = ("_neff",)
|
|
299
312
|
def __init__(self,
|
|
300
313
|
er: float | complex | np.ndarray | MatProperty = 1.0,
|
|
301
314
|
ur: float | complex | np.ndarray | MatProperty = 1.0,
|
|
@@ -330,10 +343,12 @@ class Material:
|
|
|
330
343
|
self._neff: Callable = lambda f: np.sqrt(self.ur._fmax(f)*self.er._fmax(f))
|
|
331
344
|
else:
|
|
332
345
|
self._neff: Callable = lambda f: _neff
|
|
333
|
-
hex_str = self.color.lstrip('#')
|
|
334
|
-
self._color_rgb = tuple(int(hex_str[i:i+2], 16)/255.0 for i in (0, 2, 4))
|
|
335
346
|
self._metal: bool = _metal
|
|
336
347
|
|
|
348
|
+
@property
|
|
349
|
+
def _color_rgb(self) -> tuple[float,float,float]:
|
|
350
|
+
return tuple(int(self.color.lstrip('#')[i:i+2], 16)/255.0 for i in (0, 2, 4))
|
|
351
|
+
|
|
337
352
|
def __getstate__(self):
|
|
338
353
|
state = self.__dict__.copy()
|
|
339
354
|
for k in self._pickle_exclude:
|
|
@@ -382,18 +397,14 @@ class Material:
|
|
|
382
397
|
@property
|
|
383
398
|
def frequency_dependent(self) -> bool:
|
|
384
399
|
"""If The material property are at all frequency dependent.
|
|
385
|
-
|
|
386
|
-
Returns:
|
|
387
|
-
bool: _description_
|
|
400
|
+
|
|
388
401
|
"""
|
|
389
402
|
return self.er._freq_dependent or self.ur._freq_dependent or self.tand._freq_dependent or self.cond._freq_dependent
|
|
390
403
|
|
|
391
404
|
@property
|
|
392
405
|
def coordinate_dependent(self) -> bool:
|
|
393
406
|
"""If the material properties are at all coordinate dependent
|
|
394
|
-
|
|
395
|
-
Returns:
|
|
396
|
-
bool: _description_
|
|
407
|
+
|
|
397
408
|
"""
|
|
398
409
|
return self.er._coord_dependent or self.ur._coord_dependent or self.tand._coord_dependent or self.cond._coord_dependent
|
|
399
410
|
|
|
@@ -184,8 +184,66 @@ class _Document(EMergeTheme):
|
|
|
184
184
|
"#4DBEEEFF",
|
|
185
185
|
]
|
|
186
186
|
|
|
187
|
+
class Stylish(EMergeTheme):
|
|
188
|
+
""" A custom EMerge theme. """
|
|
189
|
+
def define(self):
|
|
190
|
+
self.backgroung_grad_1 = "#FFFFFF"
|
|
191
|
+
self.backgroung_grad_2 = "#FFFFFF"
|
|
192
|
+
self.grid_color = "#676767FF"
|
|
193
|
+
self.brightness = 1.0
|
|
194
|
+
|
|
195
|
+
self.label_color = "#FFFFFF"
|
|
196
|
+
self.text_color = "#000000FF"
|
|
197
|
+
self.render_metal = True
|
|
198
|
+
self.line_width = 3.0
|
|
199
|
+
|
|
200
|
+
self.geo_edge_width = 3.0
|
|
201
|
+
self.geo_edge_color = "#000000ff"
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
self.draw_xax = False
|
|
205
|
+
self.draw_yax = False
|
|
206
|
+
self.draw_zax = False
|
|
207
|
+
self.draw_xplane = False
|
|
208
|
+
self.draw_yplane = False
|
|
209
|
+
self.draw_zplane = False
|
|
210
|
+
self.draw_xgrid = False
|
|
211
|
+
self.draw_ygrid = False
|
|
212
|
+
self.draw_zgrid = False
|
|
213
|
+
|
|
214
|
+
self.axis_x_color = "#FF0000FF"
|
|
215
|
+
self.axis_y_color = "#00FF00FF"
|
|
216
|
+
self.axis_z_color = "#0000FFFF"
|
|
187
217
|
|
|
218
|
+
self.aa_active = True
|
|
219
|
+
self.aa_samples = 5
|
|
220
|
+
self.cmap_npts = 64
|
|
221
|
+
|
|
222
|
+
self.draw_pvgrid = False
|
|
223
|
+
self.render_shadows = True
|
|
224
|
+
# Basic clear academic scales
|
|
225
|
+
# Amplitude is Jet
|
|
226
|
+
# Wave is blue to transparent to red
|
|
227
|
+
|
|
228
|
+
self.colormaps = {
|
|
229
|
+
'amplitude': (("#0000FF", "#00FFFF", "#00FF00", "#FFFF00", "#FF0000"),(0.0, 0.25, 0.5, 0.75, 1.0)),
|
|
230
|
+
'wave': (("#FF0000", "#FFAAAA00","#0000FF00", "#0000FF"), (0.0, 0.49, 0.51, 1.0)),
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
self.render_style = 'surface'
|
|
234
|
+
|
|
235
|
+
# Clear high contrast matlab colors for paper
|
|
236
|
+
self.line_color_cycle = [
|
|
237
|
+
"#003E67FF",
|
|
238
|
+
"#7F2600FF",
|
|
239
|
+
"#5B09A7FF",
|
|
240
|
+
"#0C5D14FF",
|
|
241
|
+
"#A35100FF",
|
|
242
|
+
"#6C006AFF",
|
|
243
|
+
]
|
|
244
|
+
|
|
188
245
|
VaporWave = _VaporWave()
|
|
189
246
|
Vintage = _Vintage()
|
|
190
247
|
Tron = _Tron()
|
|
191
248
|
Document = _Document()
|
|
249
|
+
Stylish = Stylish()
|
|
@@ -279,11 +279,13 @@ wheels = [
|
|
|
279
279
|
|
|
280
280
|
[[package]]
|
|
281
281
|
name = "emsutil"
|
|
282
|
-
version = "0.
|
|
282
|
+
version = "0.4.0"
|
|
283
283
|
source = { editable = "." }
|
|
284
284
|
dependencies = [
|
|
285
285
|
{ name = "loguru" },
|
|
286
286
|
{ name = "matplotlib" },
|
|
287
|
+
{ name = "msgpack" },
|
|
288
|
+
{ name = "msgpack-numpy" },
|
|
287
289
|
{ name = "numpy" },
|
|
288
290
|
{ name = "pyvista" },
|
|
289
291
|
{ name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
|
|
@@ -294,6 +296,8 @@ dependencies = [
|
|
|
294
296
|
requires-dist = [
|
|
295
297
|
{ name = "loguru", specifier = ">=0.7.3" },
|
|
296
298
|
{ name = "matplotlib", specifier = ">=3.8.0" },
|
|
299
|
+
{ name = "msgpack", specifier = ">=1.1.2" },
|
|
300
|
+
{ name = "msgpack-numpy", specifier = ">=0.4.8" },
|
|
297
301
|
{ name = "numpy", specifier = ">=1.24,<2.3" },
|
|
298
302
|
{ name = "pyvista", specifier = ">=0.46.4" },
|
|
299
303
|
{ name = "scipy", specifier = ">=1.14.0" },
|
|
@@ -560,6 +564,80 @@ wheels = [
|
|
|
560
564
|
{ url = "https://files.pythonhosted.org/packages/73/e4/6d6f14b2a759c622f191b2d67e9075a3f56aaccb3be4bb9bb6890030d0a0/matplotlib-3.10.8-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ae029229a57cd1e8fe542485f27e7ca7b23aa9e8944ddb4985d0bc444f1eca2", size = 8713867, upload-time = "2025-12-10T22:56:48.954Z" },
|
|
561
565
|
]
|
|
562
566
|
|
|
567
|
+
[[package]]
|
|
568
|
+
name = "msgpack"
|
|
569
|
+
version = "1.1.2"
|
|
570
|
+
source = { registry = "https://pypi.org/simple" }
|
|
571
|
+
sdist = { url = "https://files.pythonhosted.org/packages/4d/f2/bfb55a6236ed8725a96b0aa3acbd0ec17588e6a2c3b62a93eb513ed8783f/msgpack-1.1.2.tar.gz", hash = "sha256:3b60763c1373dd60f398488069bcdc703cd08a711477b5d480eecc9f9626f47e", size = 173581, upload-time = "2025-10-08T09:15:56.596Z" }
|
|
572
|
+
wheels = [
|
|
573
|
+
{ url = "https://files.pythonhosted.org/packages/f5/a2/3b68a9e769db68668b25c6108444a35f9bd163bb848c0650d516761a59c0/msgpack-1.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0051fffef5a37ca2cd16978ae4f0aef92f164df86823871b5162812bebecd8e2", size = 81318, upload-time = "2025-10-08T09:14:38.722Z" },
|
|
574
|
+
{ url = "https://files.pythonhosted.org/packages/5b/e1/2b720cc341325c00be44e1ed59e7cfeae2678329fbf5aa68f5bda57fe728/msgpack-1.1.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a605409040f2da88676e9c9e5853b3449ba8011973616189ea5ee55ddbc5bc87", size = 83786, upload-time = "2025-10-08T09:14:40.082Z" },
|
|
575
|
+
{ url = "https://files.pythonhosted.org/packages/71/e5/c2241de64bfceac456b140737812a2ab310b10538a7b34a1d393b748e095/msgpack-1.1.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b696e83c9f1532b4af884045ba7f3aa741a63b2bc22617293a2c6a7c645f251", size = 398240, upload-time = "2025-10-08T09:14:41.151Z" },
|
|
576
|
+
{ url = "https://files.pythonhosted.org/packages/b7/09/2a06956383c0fdebaef5aa9246e2356776f12ea6f2a44bd1368abf0e46c4/msgpack-1.1.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:365c0bbe981a27d8932da71af63ef86acc59ed5c01ad929e09a0b88c6294e28a", size = 406070, upload-time = "2025-10-08T09:14:42.821Z" },
|
|
577
|
+
{ url = "https://files.pythonhosted.org/packages/0e/74/2957703f0e1ef20637d6aead4fbb314330c26f39aa046b348c7edcf6ca6b/msgpack-1.1.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:41d1a5d875680166d3ac5c38573896453bbbea7092936d2e107214daf43b1d4f", size = 393403, upload-time = "2025-10-08T09:14:44.38Z" },
|
|
578
|
+
{ url = "https://files.pythonhosted.org/packages/a5/09/3bfc12aa90f77b37322fc33e7a8a7c29ba7c8edeadfa27664451801b9860/msgpack-1.1.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:354e81bcdebaab427c3df4281187edc765d5d76bfb3a7c125af9da7a27e8458f", size = 398947, upload-time = "2025-10-08T09:14:45.56Z" },
|
|
579
|
+
{ url = "https://files.pythonhosted.org/packages/4b/4f/05fcebd3b4977cb3d840f7ef6b77c51f8582086de5e642f3fefee35c86fc/msgpack-1.1.2-cp310-cp310-win32.whl", hash = "sha256:e64c8d2f5e5d5fda7b842f55dec6133260ea8f53c4257d64494c534f306bf7a9", size = 64769, upload-time = "2025-10-08T09:14:47.334Z" },
|
|
580
|
+
{ url = "https://files.pythonhosted.org/packages/d0/3e/b4547e3a34210956382eed1c85935fff7e0f9b98be3106b3745d7dec9c5e/msgpack-1.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:db6192777d943bdaaafb6ba66d44bf65aa0e9c5616fa1d2da9bb08828c6b39aa", size = 71293, upload-time = "2025-10-08T09:14:48.665Z" },
|
|
581
|
+
{ url = "https://files.pythonhosted.org/packages/2c/97/560d11202bcd537abca693fd85d81cebe2107ba17301de42b01ac1677b69/msgpack-1.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2e86a607e558d22985d856948c12a3fa7b42efad264dca8a3ebbcfa2735d786c", size = 82271, upload-time = "2025-10-08T09:14:49.967Z" },
|
|
582
|
+
{ url = "https://files.pythonhosted.org/packages/83/04/28a41024ccbd67467380b6fb440ae916c1e4f25e2cd4c63abe6835ac566e/msgpack-1.1.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:283ae72fc89da59aa004ba147e8fc2f766647b1251500182fac0350d8af299c0", size = 84914, upload-time = "2025-10-08T09:14:50.958Z" },
|
|
583
|
+
{ url = "https://files.pythonhosted.org/packages/71/46/b817349db6886d79e57a966346cf0902a426375aadc1e8e7a86a75e22f19/msgpack-1.1.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:61c8aa3bd513d87c72ed0b37b53dd5c5a0f58f2ff9f26e1555d3bd7948fb7296", size = 416962, upload-time = "2025-10-08T09:14:51.997Z" },
|
|
584
|
+
{ url = "https://files.pythonhosted.org/packages/da/e0/6cc2e852837cd6086fe7d8406af4294e66827a60a4cf60b86575a4a65ca8/msgpack-1.1.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:454e29e186285d2ebe65be34629fa0e8605202c60fbc7c4c650ccd41870896ef", size = 426183, upload-time = "2025-10-08T09:14:53.477Z" },
|
|
585
|
+
{ url = "https://files.pythonhosted.org/packages/25/98/6a19f030b3d2ea906696cedd1eb251708e50a5891d0978b012cb6107234c/msgpack-1.1.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7bc8813f88417599564fafa59fd6f95be417179f76b40325b500b3c98409757c", size = 411454, upload-time = "2025-10-08T09:14:54.648Z" },
|
|
586
|
+
{ url = "https://files.pythonhosted.org/packages/b7/cd/9098fcb6adb32187a70b7ecaabf6339da50553351558f37600e53a4a2a23/msgpack-1.1.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bafca952dc13907bdfdedfc6a5f579bf4f292bdd506fadb38389afa3ac5b208e", size = 422341, upload-time = "2025-10-08T09:14:56.328Z" },
|
|
587
|
+
{ url = "https://files.pythonhosted.org/packages/e6/ae/270cecbcf36c1dc85ec086b33a51a4d7d08fc4f404bdbc15b582255d05ff/msgpack-1.1.2-cp311-cp311-win32.whl", hash = "sha256:602b6740e95ffc55bfb078172d279de3773d7b7db1f703b2f1323566b878b90e", size = 64747, upload-time = "2025-10-08T09:14:57.882Z" },
|
|
588
|
+
{ url = "https://files.pythonhosted.org/packages/2a/79/309d0e637f6f37e83c711f547308b91af02b72d2326ddd860b966080ef29/msgpack-1.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:d198d275222dc54244bf3327eb8cbe00307d220241d9cec4d306d49a44e85f68", size = 71633, upload-time = "2025-10-08T09:14:59.177Z" },
|
|
589
|
+
{ url = "https://files.pythonhosted.org/packages/73/4d/7c4e2b3d9b1106cd0aa6cb56cc57c6267f59fa8bfab7d91df5adc802c847/msgpack-1.1.2-cp311-cp311-win_arm64.whl", hash = "sha256:86f8136dfa5c116365a8a651a7d7484b65b13339731dd6faebb9a0242151c406", size = 64755, upload-time = "2025-10-08T09:15:00.48Z" },
|
|
590
|
+
{ url = "https://files.pythonhosted.org/packages/ad/bd/8b0d01c756203fbab65d265859749860682ccd2a59594609aeec3a144efa/msgpack-1.1.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:70a0dff9d1f8da25179ffcf880e10cf1aad55fdb63cd59c9a49a1b82290062aa", size = 81939, upload-time = "2025-10-08T09:15:01.472Z" },
|
|
591
|
+
{ url = "https://files.pythonhosted.org/packages/34/68/ba4f155f793a74c1483d4bdef136e1023f7bcba557f0db4ef3db3c665cf1/msgpack-1.1.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:446abdd8b94b55c800ac34b102dffd2f6aa0ce643c55dfc017ad89347db3dbdb", size = 85064, upload-time = "2025-10-08T09:15:03.764Z" },
|
|
592
|
+
{ url = "https://files.pythonhosted.org/packages/f2/60/a064b0345fc36c4c3d2c743c82d9100c40388d77f0b48b2f04d6041dbec1/msgpack-1.1.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c63eea553c69ab05b6747901b97d620bb2a690633c77f23feb0c6a947a8a7b8f", size = 417131, upload-time = "2025-10-08T09:15:05.136Z" },
|
|
593
|
+
{ url = "https://files.pythonhosted.org/packages/65/92/a5100f7185a800a5d29f8d14041f61475b9de465ffcc0f3b9fba606e4505/msgpack-1.1.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:372839311ccf6bdaf39b00b61288e0557916c3729529b301c52c2d88842add42", size = 427556, upload-time = "2025-10-08T09:15:06.837Z" },
|
|
594
|
+
{ url = "https://files.pythonhosted.org/packages/f5/87/ffe21d1bf7d9991354ad93949286f643b2bb6ddbeab66373922b44c3b8cc/msgpack-1.1.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2929af52106ca73fcb28576218476ffbb531a036c2adbcf54a3664de124303e9", size = 404920, upload-time = "2025-10-08T09:15:08.179Z" },
|
|
595
|
+
{ url = "https://files.pythonhosted.org/packages/ff/41/8543ed2b8604f7c0d89ce066f42007faac1eaa7d79a81555f206a5cdb889/msgpack-1.1.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:be52a8fc79e45b0364210eef5234a7cf8d330836d0a64dfbb878efa903d84620", size = 415013, upload-time = "2025-10-08T09:15:09.83Z" },
|
|
596
|
+
{ url = "https://files.pythonhosted.org/packages/41/0d/2ddfaa8b7e1cee6c490d46cb0a39742b19e2481600a7a0e96537e9c22f43/msgpack-1.1.2-cp312-cp312-win32.whl", hash = "sha256:1fff3d825d7859ac888b0fbda39a42d59193543920eda9d9bea44d958a878029", size = 65096, upload-time = "2025-10-08T09:15:11.11Z" },
|
|
597
|
+
{ url = "https://files.pythonhosted.org/packages/8c/ec/d431eb7941fb55a31dd6ca3404d41fbb52d99172df2e7707754488390910/msgpack-1.1.2-cp312-cp312-win_amd64.whl", hash = "sha256:1de460f0403172cff81169a30b9a92b260cb809c4cb7e2fc79ae8d0510c78b6b", size = 72708, upload-time = "2025-10-08T09:15:12.554Z" },
|
|
598
|
+
{ url = "https://files.pythonhosted.org/packages/c5/31/5b1a1f70eb0e87d1678e9624908f86317787b536060641d6798e3cf70ace/msgpack-1.1.2-cp312-cp312-win_arm64.whl", hash = "sha256:be5980f3ee0e6bd44f3a9e9dea01054f175b50c3e6cdb692bc9424c0bbb8bf69", size = 64119, upload-time = "2025-10-08T09:15:13.589Z" },
|
|
599
|
+
{ url = "https://files.pythonhosted.org/packages/6b/31/b46518ecc604d7edf3a4f94cb3bf021fc62aa301f0cb849936968164ef23/msgpack-1.1.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4efd7b5979ccb539c221a4c4e16aac1a533efc97f3b759bb5a5ac9f6d10383bf", size = 81212, upload-time = "2025-10-08T09:15:14.552Z" },
|
|
600
|
+
{ url = "https://files.pythonhosted.org/packages/92/dc/c385f38f2c2433333345a82926c6bfa5ecfff3ef787201614317b58dd8be/msgpack-1.1.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:42eefe2c3e2af97ed470eec850facbe1b5ad1d6eacdbadc42ec98e7dcf68b4b7", size = 84315, upload-time = "2025-10-08T09:15:15.543Z" },
|
|
601
|
+
{ url = "https://files.pythonhosted.org/packages/d3/68/93180dce57f684a61a88a45ed13047558ded2be46f03acb8dec6d7c513af/msgpack-1.1.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1fdf7d83102bf09e7ce3357de96c59b627395352a4024f6e2458501f158bf999", size = 412721, upload-time = "2025-10-08T09:15:16.567Z" },
|
|
602
|
+
{ url = "https://files.pythonhosted.org/packages/5d/ba/459f18c16f2b3fc1a1ca871f72f07d70c07bf768ad0a507a698b8052ac58/msgpack-1.1.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fac4be746328f90caa3cd4bc67e6fe36ca2bf61d5c6eb6d895b6527e3f05071e", size = 424657, upload-time = "2025-10-08T09:15:17.825Z" },
|
|
603
|
+
{ url = "https://files.pythonhosted.org/packages/38/f8/4398c46863b093252fe67368b44edc6c13b17f4e6b0e4929dbf0bdb13f23/msgpack-1.1.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:fffee09044073e69f2bad787071aeec727183e7580443dfeb8556cbf1978d162", size = 402668, upload-time = "2025-10-08T09:15:19.003Z" },
|
|
604
|
+
{ url = "https://files.pythonhosted.org/packages/28/ce/698c1eff75626e4124b4d78e21cca0b4cc90043afb80a507626ea354ab52/msgpack-1.1.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5928604de9b032bc17f5099496417f113c45bc6bc21b5c6920caf34b3c428794", size = 419040, upload-time = "2025-10-08T09:15:20.183Z" },
|
|
605
|
+
{ url = "https://files.pythonhosted.org/packages/67/32/f3cd1667028424fa7001d82e10ee35386eea1408b93d399b09fb0aa7875f/msgpack-1.1.2-cp313-cp313-win32.whl", hash = "sha256:a7787d353595c7c7e145e2331abf8b7ff1e6673a6b974ded96e6d4ec09f00c8c", size = 65037, upload-time = "2025-10-08T09:15:21.416Z" },
|
|
606
|
+
{ url = "https://files.pythonhosted.org/packages/74/07/1ed8277f8653c40ebc65985180b007879f6a836c525b3885dcc6448ae6cb/msgpack-1.1.2-cp313-cp313-win_amd64.whl", hash = "sha256:a465f0dceb8e13a487e54c07d04ae3ba131c7c5b95e2612596eafde1dccf64a9", size = 72631, upload-time = "2025-10-08T09:15:22.431Z" },
|
|
607
|
+
{ url = "https://files.pythonhosted.org/packages/e5/db/0314e4e2db56ebcf450f277904ffd84a7988b9e5da8d0d61ab2d057df2b6/msgpack-1.1.2-cp313-cp313-win_arm64.whl", hash = "sha256:e69b39f8c0aa5ec24b57737ebee40be647035158f14ed4b40e6f150077e21a84", size = 64118, upload-time = "2025-10-08T09:15:23.402Z" },
|
|
608
|
+
{ url = "https://files.pythonhosted.org/packages/22/71/201105712d0a2ff07b7873ed3c220292fb2ea5120603c00c4b634bcdafb3/msgpack-1.1.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e23ce8d5f7aa6ea6d2a2b326b4ba46c985dbb204523759984430db7114f8aa00", size = 81127, upload-time = "2025-10-08T09:15:24.408Z" },
|
|
609
|
+
{ url = "https://files.pythonhosted.org/packages/1b/9f/38ff9e57a2eade7bf9dfee5eae17f39fc0e998658050279cbb14d97d36d9/msgpack-1.1.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:6c15b7d74c939ebe620dd8e559384be806204d73b4f9356320632d783d1f7939", size = 84981, upload-time = "2025-10-08T09:15:25.812Z" },
|
|
610
|
+
{ url = "https://files.pythonhosted.org/packages/8e/a9/3536e385167b88c2cc8f4424c49e28d49a6fc35206d4a8060f136e71f94c/msgpack-1.1.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:99e2cb7b9031568a2a5c73aa077180f93dd2e95b4f8d3b8e14a73ae94a9e667e", size = 411885, upload-time = "2025-10-08T09:15:27.22Z" },
|
|
611
|
+
{ url = "https://files.pythonhosted.org/packages/2f/40/dc34d1a8d5f1e51fc64640b62b191684da52ca469da9cd74e84936ffa4a6/msgpack-1.1.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:180759d89a057eab503cf62eeec0aa61c4ea1200dee709f3a8e9397dbb3b6931", size = 419658, upload-time = "2025-10-08T09:15:28.4Z" },
|
|
612
|
+
{ url = "https://files.pythonhosted.org/packages/3b/ef/2b92e286366500a09a67e03496ee8b8ba00562797a52f3c117aa2b29514b/msgpack-1.1.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:04fb995247a6e83830b62f0b07bf36540c213f6eac8e851166d8d86d83cbd014", size = 403290, upload-time = "2025-10-08T09:15:29.764Z" },
|
|
613
|
+
{ url = "https://files.pythonhosted.org/packages/78/90/e0ea7990abea5764e4655b8177aa7c63cdfa89945b6e7641055800f6c16b/msgpack-1.1.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:8e22ab046fa7ede9e36eeb4cfad44d46450f37bb05d5ec482b02868f451c95e2", size = 415234, upload-time = "2025-10-08T09:15:31.022Z" },
|
|
614
|
+
{ url = "https://files.pythonhosted.org/packages/72/4e/9390aed5db983a2310818cd7d3ec0aecad45e1f7007e0cda79c79507bb0d/msgpack-1.1.2-cp314-cp314-win32.whl", hash = "sha256:80a0ff7d4abf5fecb995fcf235d4064b9a9a8a40a3ab80999e6ac1e30b702717", size = 66391, upload-time = "2025-10-08T09:15:32.265Z" },
|
|
615
|
+
{ url = "https://files.pythonhosted.org/packages/6e/f1/abd09c2ae91228c5f3998dbd7f41353def9eac64253de3c8105efa2082f7/msgpack-1.1.2-cp314-cp314-win_amd64.whl", hash = "sha256:9ade919fac6a3e7260b7f64cea89df6bec59104987cbea34d34a2fa15d74310b", size = 73787, upload-time = "2025-10-08T09:15:33.219Z" },
|
|
616
|
+
{ url = "https://files.pythonhosted.org/packages/6a/b0/9d9f667ab48b16ad4115c1935d94023b82b3198064cb84a123e97f7466c1/msgpack-1.1.2-cp314-cp314-win_arm64.whl", hash = "sha256:59415c6076b1e30e563eb732e23b994a61c159cec44deaf584e5cc1dd662f2af", size = 66453, upload-time = "2025-10-08T09:15:34.225Z" },
|
|
617
|
+
{ url = "https://files.pythonhosted.org/packages/16/67/93f80545eb1792b61a217fa7f06d5e5cb9e0055bed867f43e2b8e012e137/msgpack-1.1.2-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:897c478140877e5307760b0ea66e0932738879e7aa68144d9b78ea4c8302a84a", size = 85264, upload-time = "2025-10-08T09:15:35.61Z" },
|
|
618
|
+
{ url = "https://files.pythonhosted.org/packages/87/1c/33c8a24959cf193966ef11a6f6a2995a65eb066bd681fd085afd519a57ce/msgpack-1.1.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a668204fa43e6d02f89dbe79a30b0d67238d9ec4c5bd8a940fc3a004a47b721b", size = 89076, upload-time = "2025-10-08T09:15:36.619Z" },
|
|
619
|
+
{ url = "https://files.pythonhosted.org/packages/fc/6b/62e85ff7193663fbea5c0254ef32f0c77134b4059f8da89b958beb7696f3/msgpack-1.1.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5559d03930d3aa0f3aacb4c42c776af1a2ace2611871c84a75afe436695e6245", size = 435242, upload-time = "2025-10-08T09:15:37.647Z" },
|
|
620
|
+
{ url = "https://files.pythonhosted.org/packages/c1/47/5c74ecb4cc277cf09f64e913947871682ffa82b3b93c8dad68083112f412/msgpack-1.1.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:70c5a7a9fea7f036b716191c29047374c10721c389c21e9ffafad04df8c52c90", size = 432509, upload-time = "2025-10-08T09:15:38.794Z" },
|
|
621
|
+
{ url = "https://files.pythonhosted.org/packages/24/a4/e98ccdb56dc4e98c929a3f150de1799831c0a800583cde9fa022fa90602d/msgpack-1.1.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:f2cb069d8b981abc72b41aea1c580ce92d57c673ec61af4c500153a626cb9e20", size = 415957, upload-time = "2025-10-08T09:15:40.238Z" },
|
|
622
|
+
{ url = "https://files.pythonhosted.org/packages/da/28/6951f7fb67bc0a4e184a6b38ab71a92d9ba58080b27a77d3e2fb0be5998f/msgpack-1.1.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:d62ce1f483f355f61adb5433ebfd8868c5f078d1a52d042b0a998682b4fa8c27", size = 422910, upload-time = "2025-10-08T09:15:41.505Z" },
|
|
623
|
+
{ url = "https://files.pythonhosted.org/packages/f0/03/42106dcded51f0a0b5284d3ce30a671e7bd3f7318d122b2ead66ad289fed/msgpack-1.1.2-cp314-cp314t-win32.whl", hash = "sha256:1d1418482b1ee984625d88aa9585db570180c286d942da463533b238b98b812b", size = 75197, upload-time = "2025-10-08T09:15:42.954Z" },
|
|
624
|
+
{ url = "https://files.pythonhosted.org/packages/15/86/d0071e94987f8db59d4eeb386ddc64d0bb9b10820a8d82bcd3e53eeb2da6/msgpack-1.1.2-cp314-cp314t-win_amd64.whl", hash = "sha256:5a46bf7e831d09470ad92dff02b8b1ac92175ca36b087f904a0519857c6be3ff", size = 85772, upload-time = "2025-10-08T09:15:43.954Z" },
|
|
625
|
+
{ url = "https://files.pythonhosted.org/packages/81/f2/08ace4142eb281c12701fc3b93a10795e4d4dc7f753911d836675050f886/msgpack-1.1.2-cp314-cp314t-win_arm64.whl", hash = "sha256:d99ef64f349d5ec3293688e91486c5fdb925ed03807f64d98d205d2713c60b46", size = 70868, upload-time = "2025-10-08T09:15:44.959Z" },
|
|
626
|
+
]
|
|
627
|
+
|
|
628
|
+
[[package]]
|
|
629
|
+
name = "msgpack-numpy"
|
|
630
|
+
version = "0.4.8"
|
|
631
|
+
source = { registry = "https://pypi.org/simple" }
|
|
632
|
+
dependencies = [
|
|
633
|
+
{ name = "msgpack" },
|
|
634
|
+
{ name = "numpy" },
|
|
635
|
+
]
|
|
636
|
+
sdist = { url = "https://files.pythonhosted.org/packages/08/94/61e8aee142733ebfdc400a05bdac6e1763c4514bba3b42743d223f388450/msgpack-numpy-0.4.8.tar.gz", hash = "sha256:c667d3180513422f9c7545be5eec5d296dcbb357e06f72ed39cc683797556e69", size = 10923, upload-time = "2022-06-09T03:43:08.739Z" }
|
|
637
|
+
wheels = [
|
|
638
|
+
{ url = "https://files.pythonhosted.org/packages/9b/5d/f25ac7d4fb77cbd53ddc6d05d833c6bf52b12770a44fa9a447eed470ca9a/msgpack_numpy-0.4.8-py2.py3-none-any.whl", hash = "sha256:773c19d4dfbae1b3c7b791083e2caf66983bb19b40901646f61d8731554ae3da", size = 6919, upload-time = "2022-06-09T03:43:06.82Z" },
|
|
639
|
+
]
|
|
640
|
+
|
|
563
641
|
[[package]]
|
|
564
642
|
name = "numpy"
|
|
565
643
|
version = "2.2.6"
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import numpy as np
|
|
2
|
-
from .strutil import arry_to_line, matrix_to_lines, arry_to_fwl
|
|
3
|
-
from ..emdata import EHFieldFF
|
|
4
|
-
|
|
5
|
-
def export_ffdata(filename: str,
|
|
6
|
-
thetas: np.ndarray,
|
|
7
|
-
phis: np.ndarray,
|
|
8
|
-
frequencies: np.ndarray,
|
|
9
|
-
fields: list[EHFieldFF],
|
|
10
|
-
precision: int = 4) -> None:
|
|
11
|
-
|
|
12
|
-
lines = []
|
|
13
|
-
lines.append('% Theta (deg)')
|
|
14
|
-
lines.append(arry_to_line(thetas, precision=precision))
|
|
15
|
-
lines.append('% Phi (deg)')
|
|
16
|
-
lines.append(arry_to_line(phis, precision=precision))
|
|
17
|
-
lines.append('')
|
|
18
|
-
nF = frequencies.shape[0]
|
|
19
|
-
|
|
20
|
-
actual_frequencies = []
|
|
21
|
-
blocks = []
|
|
22
|
-
|
|
23
|
-
T, P = np.meshgrid(thetas, phis, indexing='ij')
|
|
24
|
-
|
|
25
|
-
thetal = T.flatten()
|
|
26
|
-
phil = P.flatten()
|
|
27
|
-
|
|
28
|
-
for iF in range(nF):
|
|
29
|
-
freq = frequencies[iF]
|
|
30
|
-
actual_frequencies.append(freq)
|
|
31
|
-
|
|
32
|
-
farfield = fields[iF]._E
|
|
33
|
-
Fx = farfield[0,:,:].squeeze().flatten()
|
|
34
|
-
Fy = farfield[1,:,:].squeeze().flatten()
|
|
35
|
-
Fz = farfield[2,:,:].squeeze().flatten()
|
|
36
|
-
|
|
37
|
-
block_lines = []
|
|
38
|
-
|
|
39
|
-
block_lines.append(f'# {freq} (Hz)')
|
|
40
|
-
block_lines.append('$ Theta(deg); Phi(deg); E_x[re](V/m); E_x[im](V/m); E_y[re](V/m); E_y[im](V/m); E_z[re](V/m); E_z[im](V/m)')
|
|
41
|
-
positions = (0, 14, 24, 38, 52, 66, 80, 94)
|
|
42
|
-
for th, ph, ex, ey, ez in zip(thetal, phil, Fx, Fy, Fz):
|
|
43
|
-
re_ex = np.real(ex)
|
|
44
|
-
im_ex = np.imag(ex)
|
|
45
|
-
re_ey = np.real(ey)
|
|
46
|
-
im_ey = np.imag(ey)
|
|
47
|
-
re_ez = np.real(ez)
|
|
48
|
-
im_ez = np.imag(ez)
|
|
49
|
-
line_values = np.array([th, ph, re_ex, im_ex, re_ey, im_ey, re_ez, im_ez])
|
|
50
|
-
line = arry_to_fwl(line_values, positions, precision=precision)
|
|
51
|
-
block_lines.append(line)
|
|
52
|
-
|
|
53
|
-
lines.extend(block_lines)
|
|
54
|
-
lines.extend('')
|
|
55
|
-
|
|
56
|
-
text = '\n'.join(lines)
|
|
57
|
-
with open(filename, 'w') as f:
|
|
58
|
-
f.write(text)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|