emerge 0.4.7__py3-none-any.whl → 0.4.9__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.

Potentially problematic release.


This version of emerge might be problematic. Click here for more details.

Files changed (78) hide show
  1. emerge/__init__.py +14 -14
  2. emerge/_emerge/__init__.py +42 -0
  3. emerge/_emerge/bc.py +197 -0
  4. emerge/_emerge/coord.py +119 -0
  5. emerge/_emerge/cs.py +523 -0
  6. emerge/_emerge/dataset.py +36 -0
  7. emerge/_emerge/elements/__init__.py +19 -0
  8. emerge/_emerge/elements/femdata.py +212 -0
  9. emerge/_emerge/elements/index_interp.py +64 -0
  10. emerge/_emerge/elements/legrange2.py +172 -0
  11. emerge/_emerge/elements/ned2_interp.py +645 -0
  12. emerge/_emerge/elements/nedelec2.py +140 -0
  13. emerge/_emerge/elements/nedleg2.py +217 -0
  14. emerge/_emerge/geo/__init__.py +24 -0
  15. emerge/_emerge/geo/horn.py +107 -0
  16. emerge/_emerge/geo/modeler.py +449 -0
  17. emerge/_emerge/geo/operations.py +254 -0
  18. emerge/_emerge/geo/pcb.py +1244 -0
  19. emerge/_emerge/geo/pcb_tools/calculator.py +28 -0
  20. emerge/_emerge/geo/pcb_tools/macro.py +79 -0
  21. emerge/_emerge/geo/pmlbox.py +204 -0
  22. emerge/_emerge/geo/polybased.py +529 -0
  23. emerge/_emerge/geo/shapes.py +427 -0
  24. emerge/_emerge/geo/step.py +77 -0
  25. emerge/_emerge/geo2d.py +86 -0
  26. emerge/_emerge/geometry.py +510 -0
  27. emerge/_emerge/howto.py +214 -0
  28. emerge/_emerge/logsettings.py +5 -0
  29. emerge/_emerge/material.py +118 -0
  30. emerge/_emerge/mesh3d.py +730 -0
  31. emerge/_emerge/mesher.py +339 -0
  32. emerge/_emerge/mth/common_functions.py +33 -0
  33. emerge/_emerge/mth/integrals.py +71 -0
  34. emerge/_emerge/mth/optimized.py +357 -0
  35. emerge/_emerge/periodic.py +263 -0
  36. emerge/_emerge/physics/__init__.py +0 -0
  37. emerge/_emerge/physics/microwave/__init__.py +1 -0
  38. emerge/_emerge/physics/microwave/adaptive_freq.py +279 -0
  39. emerge/_emerge/physics/microwave/assembly/assembler.py +569 -0
  40. emerge/_emerge/physics/microwave/assembly/curlcurl.py +448 -0
  41. emerge/_emerge/physics/microwave/assembly/generalized_eigen.py +426 -0
  42. emerge/_emerge/physics/microwave/assembly/robinbc.py +433 -0
  43. emerge/_emerge/physics/microwave/microwave_3d.py +1150 -0
  44. emerge/_emerge/physics/microwave/microwave_bc.py +915 -0
  45. emerge/_emerge/physics/microwave/microwave_data.py +1148 -0
  46. emerge/_emerge/physics/microwave/periodic.py +82 -0
  47. emerge/_emerge/physics/microwave/port_functions.py +53 -0
  48. emerge/_emerge/physics/microwave/sc.py +175 -0
  49. emerge/_emerge/physics/microwave/simjob.py +147 -0
  50. emerge/_emerge/physics/microwave/sparam.py +138 -0
  51. emerge/_emerge/physics/microwave/touchstone.py +140 -0
  52. emerge/_emerge/plot/__init__.py +0 -0
  53. emerge/_emerge/plot/display.py +394 -0
  54. emerge/_emerge/plot/grapher.py +93 -0
  55. emerge/_emerge/plot/matplotlib/mpldisplay.py +264 -0
  56. emerge/_emerge/plot/pyvista/__init__.py +1 -0
  57. emerge/_emerge/plot/pyvista/display.py +931 -0
  58. emerge/_emerge/plot/pyvista/display_settings.py +24 -0
  59. emerge/_emerge/plot/simple_plots.py +551 -0
  60. emerge/_emerge/plot.py +225 -0
  61. emerge/_emerge/projects/__init__.py +0 -0
  62. emerge/_emerge/projects/_gen_base.txt +32 -0
  63. emerge/_emerge/projects/_load_base.txt +24 -0
  64. emerge/_emerge/projects/generate_project.py +40 -0
  65. emerge/_emerge/selection.py +596 -0
  66. emerge/_emerge/simmodel.py +444 -0
  67. emerge/_emerge/simulation_data.py +411 -0
  68. emerge/_emerge/solver.py +993 -0
  69. emerge/_emerge/system.py +54 -0
  70. emerge/cli.py +19 -0
  71. emerge/lib.py +1 -1
  72. emerge/plot.py +1 -1
  73. {emerge-0.4.7.dist-info → emerge-0.4.9.dist-info}/METADATA +7 -6
  74. emerge-0.4.9.dist-info/RECORD +78 -0
  75. emerge-0.4.9.dist-info/entry_points.txt +2 -0
  76. emerge-0.4.7.dist-info/RECORD +0 -9
  77. emerge-0.4.7.dist-info/entry_points.txt +0 -2
  78. {emerge-0.4.7.dist-info → emerge-0.4.9.dist-info}/WHEEL +0 -0
@@ -0,0 +1,140 @@
1
+ # EMerge is an open source Python based FEM EM simulation module.
2
+ # Copyright (C) 2025 Andrés Martínez Mera.
3
+ # Copyright (C) 2025 Robert Fennis.
4
+
5
+ # This program is free software; you can redistribute it and/or
6
+ # modify it under the terms of the GNU General Public License
7
+ # as published by the Free Software Foundation; either version 2
8
+ # of the License, or (at your option) any later version.
9
+
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program; if not, see
17
+ # <https://www.gnu.org/licenses/>.
18
+
19
+ import os
20
+ from typing import Literal
21
+ import numpy as np
22
+ from datetime import datetime
23
+ from loguru import logger
24
+
25
+ _F_UNIT = {'HZ': 1,
26
+ 'KHZ': 1e3,
27
+ 'MHZ': 1e6,
28
+ 'GHZ': 1e9,
29
+ }
30
+ def generate_touchstone(filename: str,
31
+ freq: np.ndarray,
32
+ Smat: np.ndarray,
33
+ data_format: Literal['RI','MA','DB'],
34
+ custom_comments: list[str] = None,
35
+ funit: Literal['HZ','KHZ','MHZ','GHZ'] = 'GHZ') -> None:
36
+ """
37
+ Export S-parameter data to a Touchstone file
38
+
39
+ Parameters
40
+ ----------
41
+ filename : str
42
+ Base filename (may include path). If no extension is given, one
43
+ will be added of the form '.sNp' where N = number of ports.
44
+ freq : np.ndarray
45
+ 1-D array of M frequency points, in Hz.
46
+ Smat : np.ndarray
47
+ 3-D array of S-parameters with shape (M, N, N).
48
+ data_format : {'RI','MA','DB'}
49
+ RI = real&imag, MA = magnitude&angle (deg), DB = dB&angle (deg).
50
+ custom_comments : list[str], optional
51
+ List of custom comment strings to add to the touchstone file header.
52
+ Each string will be prefixed with "! " automatically.
53
+ """
54
+ # --- validations ---
55
+ if Smat.ndim != 3:
56
+ raise ValueError(f"Smat must be 3-D with shape (M,N,N), got ndim={Smat.ndim}")
57
+ M, Nports, N2 = Smat.shape
58
+ if Nports != N2:
59
+ raise ValueError(f"Smat must be square in its last two dims, got {Nports}×{N2}")
60
+ if freq.ndim != 1 or freq.size != M:
61
+ raise ValueError(f"freq must be 1-D of length {M}, got shape {freq.shape}")
62
+
63
+ # --- determine output filename & extension ---
64
+ base, ext = os.path.splitext(filename)
65
+ if ext == '':
66
+ ext = f".s{Nports}p"
67
+ filename_out = base + ext
68
+
69
+ # --- write the Touchstone file ---
70
+ with open(filename_out, 'w') as f:
71
+ # Write header
72
+ f.write(f"! Generated by EMerge - {Nports}-port S-parameters\n")
73
+ timestamp_str = datetime.now().strftime("%Y-%m-%d %H:%M %Z")
74
+ f.write(f"! File generated: {timestamp_str}\n")
75
+
76
+ # Write custom comments if provided
77
+ if custom_comments:
78
+ for comment in custom_comments:
79
+ # Ensure comment starts with "! " if not already present
80
+ if not comment.startswith("! "):
81
+ comment = "! " + comment
82
+ f.write(f"{comment}\n")
83
+
84
+ f.write(f"# {funit} S {data_format.upper()} R 50\n")
85
+
86
+ # Write data
87
+ for i in range(M):
88
+ freq_ghz = freq[i] / _F_UNIT[funit] # Convert Hz to GHz
89
+
90
+ # Build all S-parameter values for this frequency
91
+ s_values = []
92
+ for row in range(Nports):
93
+ for col in range(Nports):
94
+ s_val = Smat[i, row, col]
95
+
96
+ if data_format == 'RI':
97
+ # Real and Imaginary
98
+ val1 = s_val.real
99
+ val2 = s_val.imag
100
+ elif data_format == 'MA':
101
+ # Magnitude and Angle (degrees)
102
+ val1 = np.abs(s_val)
103
+ val2 = np.angle(s_val) * 180.0 / np.pi
104
+ elif data_format == 'DB':
105
+ # dB and Angle (degrees)
106
+ val1 = 20.0 * np.log10(np.abs(s_val)) if np.abs(s_val) > 0 else -300.0
107
+ val2 = np.angle(s_val) * 180.0 / np.pi
108
+
109
+ s_values.extend([val1, val2])
110
+
111
+ # Write frequency and S-parameters according to touchstone format
112
+ if Nports == 1:
113
+ # s1p: freq S11_real S11_imag (3 values per line)
114
+ f.write(f"{freq_ghz:12.6e} {s_values[0]:12.6e} {s_values[1]:12.6e}\n")
115
+
116
+ elif Nports == 2:
117
+ # s2p: freq S11_r S11_i S21_r S21_i S12_r S12_i S22_r S22_i (9 values per line)
118
+ f.write(f"{freq_ghz:12.6e}")
119
+ for val in s_values:
120
+ f.write(f" {val:12.6e}")
121
+ f.write("\n")
122
+
123
+ else:
124
+ # s3p, s4p, etc: freq on first line, then S-parameters on subsequent lines
125
+ f.write(f"{freq_ghz:12.6e}")
126
+
127
+ if (Nports == 3):
128
+ values_per_line = 6 # 3-port S-parameter
129
+ else:
130
+ values_per_line = 8 # 4-port S-parameter pairs
131
+ for j in range(0, len(s_values), values_per_line):
132
+ f.write(" ") # Indent continuation lines
133
+ if (j != 0):
134
+ f.write(' ' * 12) # Extra indentation
135
+ end_idx = min(j + values_per_line, len(s_values))
136
+ for val in s_values[j:end_idx]:
137
+ f.write(f" {val:12.6e}")
138
+ f.write("\n")
139
+
140
+ logger.info(f"Touchstone file written to '{filename_out}'")
File without changes
@@ -0,0 +1,394 @@
1
+ from ..mesh3d import Mesh3D
2
+ from ..geometry import GeoObject
3
+ from ..selection import Selection
4
+ from ..physics.microwave.microwave_bc import PortBC
5
+ from typing import Iterable, Literal
6
+ import numpy as np
7
+
8
+ cmap_names = Literal['bgy','bgyw','kbc','blues','bmw','bmy','kgy','gray','dimgray','fire','kb','kg','kr',
9
+ 'bkr','bky','coolwarm','gwv','bjy','bwy','cwr','colorwheel','isolum','rainbow','fire',
10
+ 'cet_fire','gouldian','kbgyw','cwr','CET_CBL1','CET_CBL3','CET_D1A']
11
+
12
+
13
+ class BaseDisplay:
14
+
15
+ def __init__(self, mesh: Mesh3D):
16
+ self._mesh: Mesh3D = mesh
17
+
18
+ def show(self):
19
+ raise NotImplementedError('This method is not implemented')
20
+
21
+ def add_object(self, obj: GeoObject | Selection | Iterable,*args, **kwargs):
22
+ """ Adds an object to the display
23
+
24
+ Keyword arguments
25
+ ----------
26
+ color : ColorLike, optional
27
+ Use to make the entire mesh have a single solid color.
28
+ Either a string, RGB list, or hex color string. For example:
29
+ ``color='white'``, ``color='w'``, ``color=[1.0, 1.0, 1.0]``, or
30
+ ``color='#FFFFFF'``. Color will be overridden if scalars are
31
+ specified.
32
+
33
+ Defaults to :attr:`pyvista.global_theme.color
34
+ <pyvista.plotting.themes.Theme.color>`.
35
+
36
+ style : str, optional
37
+ Visualization style of the mesh. One of the following:
38
+ ``style='surface'``, ``style='wireframe'``, ``style='points'``,
39
+ ``style='points_gaussian'``. Defaults to ``'surface'``. Note that
40
+ ``'wireframe'`` only shows a wireframe of the outer geometry.
41
+ ``'points_gaussian'`` can be modified with the ``emissive``,
42
+ ``render_points_as_spheres`` options.
43
+
44
+ scalars : str | numpy.ndarray, optional
45
+ Scalars used to "color" the mesh. Accepts a string name
46
+ of an array that is present on the mesh or an array equal
47
+ to the number of cells or the number of points in the
48
+ mesh. Array should be sized as a single vector. If both
49
+ ``color`` and ``scalars`` are ``None``, then the active
50
+ scalars are used.
51
+
52
+ clim : sequence[float], optional
53
+ Two item color bar range for scalars. Defaults to minimum and
54
+ maximum of scalars array. Example: ``[-1, 2]``. ``rng`` is
55
+ also an accepted alias for this.
56
+
57
+ show_edges : bool, optional
58
+ Shows the edges of a mesh. Does not apply to a wireframe
59
+ representation.
60
+
61
+ edge_color : ColorLike, optional
62
+ The solid color to give the edges when ``show_edges=True``.
63
+ Either a string, RGB list, or hex color string.
64
+
65
+ Defaults to :attr:`pyvista.global_theme.edge_color
66
+ <pyvista.plotting.themes.Theme.edge_color>`.
67
+
68
+ point_size : float, optional
69
+ Point size of any nodes in the dataset plotted. Also
70
+ applicable when style='points'. Default ``5.0``.
71
+
72
+ line_width : float, optional
73
+ Thickness of lines. Only valid for wireframe and surface
74
+ representations. Default ``None``.
75
+
76
+ opacity : float | str| array_like
77
+ Opacity of the mesh. If a single float value is given, it
78
+ will be the global opacity of the mesh and uniformly
79
+ applied everywhere - should be between 0 and 1. A string
80
+ can also be specified to map the scalars range to a
81
+ predefined opacity transfer function (options include:
82
+ ``'linear'``, ``'linear_r'``, ``'geom'``, ``'geom_r'``).
83
+ A string could also be used to map a scalars array from
84
+ the mesh to the opacity (must have same number of elements
85
+ as the ``scalars`` argument). Or you can pass a custom
86
+ made transfer function that is an array either
87
+ ``n_colors`` in length or shorter.
88
+
89
+ flip_scalars : bool, default: False
90
+ Flip direction of cmap. Most colormaps allow ``*_r``
91
+ suffix to do this as well.
92
+
93
+ lighting : bool, optional
94
+ Enable or disable view direction lighting. Default ``False``.
95
+
96
+ n_colors : int, optional
97
+ Number of colors to use when displaying scalars. Defaults to 256.
98
+ The scalar bar will also have this many colors.
99
+
100
+ interpolate_before_map : bool, optional
101
+ Enabling makes for a smoother scalars display. Default is
102
+ ``True``. When ``False``, OpenGL will interpolate the
103
+ mapped colors which can result is showing colors that are
104
+ not present in the color map.
105
+
106
+ cmap : str | list | pyvista.LookupTable, default: :attr:`pyvista.plotting.themes.Theme.cmap`
107
+ If a string, this is the name of the ``matplotlib`` colormap to use
108
+ when mapping the ``scalars``. See available Matplotlib colormaps.
109
+ Only applicable for when displaying ``scalars``.
110
+ ``colormap`` is also an accepted alias
111
+ for this. If ``colorcet`` or ``cmocean`` are installed, their
112
+ colormaps can be specified by name.
113
+
114
+ You can also specify a list of colors to override an existing
115
+ colormap with a custom one. For example, to create a three color
116
+ colormap you might specify ``['green', 'red', 'blue']``.
117
+
118
+ This parameter also accepts a :class:`pyvista.LookupTable`. If this
119
+ is set, all parameters controlling the color map like ``n_colors``
120
+ will be ignored.
121
+
122
+ label : str, optional
123
+ String label to use when adding a legend to the scene with
124
+ :func:`pyvista.Plotter.add_legend`.
125
+
126
+ reset_camera : bool, optional
127
+ Reset the camera after adding this mesh to the scene. The default
128
+ setting is ``None``, where the camera is only reset if this plotter
129
+ has already been shown. If ``False``, the camera is not reset
130
+ regardless of the state of the ``Plotter``. When ``True``, the
131
+ camera is always reset.
132
+
133
+ scalar_bar_args : dict, optional
134
+ Dictionary of keyword arguments to pass when adding the
135
+ scalar bar to the scene. For options, see
136
+ :func:`pyvista.Plotter.add_scalar_bar`.
137
+
138
+ show_scalar_bar : bool, optional
139
+ If ``False``, a scalar bar will not be added to the
140
+ scene.
141
+
142
+ multi_colors : bool | str | cycler.Cycler | sequence[ColorLike], default: False
143
+ If a :class:`pyvista.MultiBlock` dataset is given this will color
144
+ each block by a solid color using a custom cycler.
145
+
146
+ If ``True``, the default 'matplotlib' color cycler is used.
147
+
148
+ See :func:`set_color_cycler<Plotter.set_color_cycler>` for usage of
149
+ custom color cycles.
150
+
151
+ name : str, optional
152
+ The name for the added mesh/actor so that it can be easily
153
+ updated. If an actor of this name already exists in the
154
+ rendering window, it will be replaced by the new actor.
155
+
156
+ texture : pyvista.Texture or np.ndarray, optional
157
+ A texture to apply if the input mesh has texture
158
+ coordinates. This will not work with MultiBlock
159
+ datasets.
160
+
161
+ render_points_as_spheres : bool, optional
162
+ Render points as spheres rather than dots.
163
+
164
+ render_lines_as_tubes : bool, optional
165
+ Show lines as thick tubes rather than flat lines. Control
166
+ the width with ``line_width``.
167
+
168
+ smooth_shading : bool, optional
169
+ Enable smooth shading when ``True`` using the Phong
170
+ shading algorithm. When ``False``, use flat shading.
171
+ Automatically enabled when ``pbr=True``. See
172
+ :ref:`shading_example`.
173
+
174
+ split_sharp_edges : bool, optional
175
+ Split sharp edges exceeding 30 degrees when plotting with smooth
176
+ shading. Control the angle with the optional keyword argument
177
+ ``feature_angle``. By default this is ``False`` unless overridden
178
+ by the global or plotter theme. Note that enabling this will
179
+ create a copy of the input mesh within the plotter. See
180
+ :ref:`shading_example`.
181
+
182
+ ambient : float, optional
183
+ When lighting is enabled, this is the amount of light in
184
+ the range of 0 to 1 (default 0.0) that reaches the actor
185
+ when not directed at the light source emitted from the
186
+ viewer.
187
+
188
+ diffuse : float, optional
189
+ The diffuse lighting coefficient. Default 1.0.
190
+
191
+ specular : float, optional
192
+ The specular lighting coefficient. Default 0.0.
193
+
194
+ specular_power : float, optional
195
+ The specular power. Between 0.0 and 128.0.
196
+
197
+ nan_color : ColorLike, optional
198
+ The color to use for all ``NaN`` values in the plotted
199
+ scalar array.
200
+
201
+ nan_opacity : float, optional
202
+ Opacity of ``NaN`` values. Should be between 0 and 1.
203
+ Default 1.0.
204
+
205
+ culling : str, optional
206
+ Does not render faces that are culled. Options are
207
+ ``'front'`` or ``'back'``. This can be helpful for dense
208
+ surface meshes, especially when edges are visible, but can
209
+ cause flat meshes to be partially displayed. Defaults to
210
+ ``False``.
211
+
212
+ rgb : bool, optional
213
+ If an 2 dimensional array is passed as the scalars, plot
214
+ those values as RGB(A) colors. ``rgba`` is also an
215
+ accepted alias for this. Opacity (the A) is optional. If
216
+ a scalars array ending with ``"_rgba"`` is passed, the default
217
+ becomes ``True``. This can be overridden by setting this
218
+ parameter to ``False``.
219
+
220
+ categories : bool, optional
221
+ If set to ``True``, then the number of unique values in
222
+ the scalar array will be used as the ``n_colors``
223
+ argument.
224
+
225
+ silhouette : dict, bool, optional
226
+ If set to ``True``, plot a silhouette highlight for the
227
+ mesh. This feature is only available for a triangulated
228
+ ``PolyData``. As a ``dict``, it contains the properties
229
+ of the silhouette to display:
230
+
231
+ * ``color``: ``ColorLike``, color of the silhouette
232
+ * ``line_width``: ``float``, edge width
233
+ * ``opacity``: ``float`` between 0 and 1, edge transparency
234
+ * ``feature_angle``: If a ``float``, display sharp edges
235
+ exceeding that angle in degrees.
236
+ * ``decimate``: ``float`` between 0 and 1, level of decimation
237
+
238
+ use_transparency : bool, optional
239
+ Invert the opacity mappings and make the values correspond
240
+ to transparency.
241
+
242
+ below_color : ColorLike, optional
243
+ Solid color for values below the scalars range
244
+ (``clim``). This will automatically set the scalar bar
245
+ ``below_label`` to ``'below'``.
246
+
247
+ above_color : ColorLike, optional
248
+ Solid color for values below the scalars range
249
+ (``clim``). This will automatically set the scalar bar
250
+ ``above_label`` to ``'above'``.
251
+
252
+ annotations : dict, optional
253
+ Pass a dictionary of annotations. Keys are the float
254
+ values in the scalars range to annotate on the scalar bar
255
+ and the values are the string annotations.
256
+
257
+ pickable : bool, optional
258
+ Set whether this actor is pickable.
259
+
260
+ preference : str, default: "point"
261
+ When ``mesh.n_points == mesh.n_cells`` and setting
262
+ scalars, this parameter sets how the scalars will be
263
+ mapped to the mesh. Default ``'point'``, causes the
264
+ scalars will be associated with the mesh points. Can be
265
+ either ``'point'`` or ``'cell'``.
266
+
267
+ log_scale : bool, default: False
268
+ Use log scale when mapping data to colors. Scalars less
269
+ than zero are mapped to the smallest representable
270
+ positive float.
271
+
272
+ pbr : bool, optional
273
+ Enable physics based rendering (PBR) if the mesh is
274
+ ``PolyData``. Use the ``color`` argument to set the base
275
+ color.
276
+
277
+ metallic : float, optional
278
+ Usually this value is either 0 or 1 for a real material
279
+ but any value in between is valid. This parameter is only
280
+ used by PBR interpolation.
281
+
282
+ roughness : float, optional
283
+ This value has to be between 0 (glossy) and 1 (rough). A
284
+ glossy material has reflections and a high specular
285
+ part. This parameter is only used by PBR
286
+ interpolation.
287
+
288
+ render : bool, default: True
289
+ Force a render when ``True``.
290
+
291
+ user_matrix : np.ndarray | vtk.vtkMatrix4x4, default: np.eye(4)
292
+ Matrix passed to the Actor class before rendering. This affects the
293
+ actor/rendering only, not the input volume itself. The user matrix is the
294
+ last transformation applied to the actor before rendering. Defaults to the
295
+ identity matrix.
296
+
297
+ component : int, optional
298
+ Set component of vector valued scalars to plot. Must be
299
+ nonnegative, if supplied. If ``None``, the magnitude of
300
+ the vector is plotted.
301
+
302
+ emissive : bool, optional
303
+ Treat the points/splats as emissive light sources. Only valid for
304
+ ``style='points_gaussian'`` representation.
305
+
306
+ copy_mesh : bool, default: False
307
+ If ``True``, a copy of the mesh will be made before adding it to
308
+ the plotter. This is useful if you would like to add the same
309
+ mesh to a plotter multiple times and display different
310
+ scalars. Setting ``copy_mesh`` to ``False`` is necessary if you
311
+ would like to update the mesh after adding it to the plotter and
312
+ have these updates rendered, e.g. by changing the active scalars or
313
+ through an interactive widget. This should only be set to ``True``
314
+ with caution. Defaults to ``False``. This is ignored if the input
315
+ is a ``vtkAlgorithm`` subclass.
316
+
317
+ backface_params : dict | pyvista.Property, optional
318
+ A :class:`pyvista.Property` or a dict of parameters to use for
319
+ backface rendering. This is useful for instance when the inside of
320
+ oriented surfaces has a different color than the outside. When a
321
+ :class:`pyvista.Property`, this is directly used for backface
322
+ rendering. When a dict, valid keys are :class:`pyvista.Property`
323
+ attributes, and values are corresponding values to use for the
324
+ given property. Omitted keys (or the default of
325
+ ``backface_params=None``) default to the corresponding frontface
326
+ properties.
327
+
328
+ show_vertices : bool, optional
329
+ When ``style`` is not ``'points'``, render the external surface
330
+ vertices. The following optional keyword arguments may be used to
331
+ control the style of the vertices:
332
+
333
+ * ``vertex_color`` - The color of the vertices
334
+ * ``vertex_style`` - Change style to ``'points_gaussian'``
335
+ * ``vertex_opacity`` - Control the opacity of the vertices
336
+
337
+ edge_opacity : float, optional
338
+ Edge opacity of the mesh. A single float value that will be applied globally
339
+ edge opacity of the mesh and uniformly applied everywhere - should be
340
+ between 0 and 1.
341
+
342
+ .. note::
343
+ `edge_opacity` uses ``SetEdgeOpacity`` as the underlying method which
344
+ requires VTK version 9.3 or higher. If ``SetEdgeOpacity`` is not
345
+ available, `edge_opacity` is set to 1.
346
+
347
+ **kwargs : dict, optional
348
+ Optional keyword arguments.
349
+ """
350
+ raise NotImplementedError('This method is not implemented')
351
+
352
+ def add_scatter(self, xs: np.ndarray, ys: np.ndarray, zs: np.ndarray):
353
+ raise NotImplementedError('This method is not implemented')
354
+
355
+ def add_portmode(self, port: PortBC, k0: float, Npoints: int = 10, dv=(0,0,0), XYZ=None,
356
+ field: Literal['E','H'] = 'E'):
357
+ raise NotImplementedError('This method is not implemented')
358
+
359
+ def add_quiver(self, x: np.ndarray, y: np.ndarray, z: np.ndarray,
360
+ dx: np.ndarray, dy: np.ndarray, dz: np.ndarray,
361
+ scale: float = 1,
362
+ scalemode: Literal['lin','log'] = 'lin'):
363
+
364
+ raise NotImplementedError('This method is not implemented')
365
+
366
+ def add_surf(self,
367
+ x: np.ndarray,
368
+ y: np.ndarray,
369
+ z: np.ndarray,
370
+ field: np.ndarray,
371
+ scale: Literal['lin','log','symlog'] = 'lin',
372
+ cmap: cmap_names = 'coolwarm',
373
+ clim: tuple[float, float] = None,
374
+ opacity: float = 1.0,
375
+ symmetrize: bool = True,
376
+ animate: bool = False,
377
+ **kwargs,):
378
+ """Add a surface plot to the display
379
+ The X,Y,Z coordinates must be a 2D grid of data points. The field must be a real field with the same size.
380
+
381
+ Args:
382
+ x (np.ndarray): The X-grid array
383
+ y (np.ndarray): The Y-grid array
384
+ z (np.ndarray): The Z-grid array
385
+ field (np.ndarray): The scalar field to display
386
+ scale (Literal["lin","log","symlog"], optional): The colormap scaling¹. Defaults to 'lin'.
387
+ cmap (cmap_names, optional): The colormap. Defaults to 'coolwarm'.
388
+ clim (tuple[float, float], optional): Specific color limits (min, max). Defaults to None.
389
+ opacity (float, optional): The opacity of the surface. Defaults to 1.0.
390
+ symmetrize (bool, optional): Wether to force a symmetrical color limit (-A,A). Defaults to True.
391
+
392
+ (¹): lin: f(x)=x, log: f(x)=log₁₀(|x|), symlog: f(x)=sgn(x)·log₁₀(1+|x·ln(10)|)
393
+ """
394
+ raise NotImplementedError('This method is not implemented')
@@ -0,0 +1,93 @@
1
+ from __future__ import annotations
2
+ import matplotlib.pyplot as plt
3
+ import numpy as np
4
+ from typing import Any
5
+ from itertools import cycle
6
+
7
+ class Style:
8
+
9
+ def __init__(self):
10
+ self.linecolors: list = ['k']
11
+ self.linestyles: list[str] = ['-']
12
+ self.markers: list[str] = [None]
13
+
14
+ self.color_cycler = cycle(self.linecolors)
15
+ self.style_cycler = cycle(self.linestyles)
16
+ self.marker_cycle = cycle(self.markers)
17
+
18
+ def get_line_properties(self, color: str, linestyle: str, marker: str) -> dict[str, Any]:
19
+ if color is None:
20
+ color = next(self.color_cycler)
21
+ if linestyle is None:
22
+ linestyle = next(self.style_cycler)
23
+ if marker is None:
24
+ marker = next(self.marker_cycle)
25
+ return dict(color=color, linestyle=linestyle, marker=marker)
26
+
27
+ def dress(self, axis):
28
+ axis.grid(True, axis='both', color='k')
29
+
30
+ class Grapher:
31
+
32
+ def __init__(self):
33
+ self.axes: list = []
34
+ self.axes_grid: list[list] = []
35
+ self.fig = None
36
+ self.style: Style = Style()
37
+ self.nrows: int = 0
38
+ self.ncols: int = 0
39
+ self.current: int = 0
40
+
41
+ self.cur_axis = None
42
+
43
+ @property
44
+ def ax(self) -> plt.axis:
45
+ if self.cur_axis is None:
46
+ return self.axes[0]
47
+ return self.cur_axis
48
+
49
+ def reset(self):
50
+ self.cur_axis = None
51
+
52
+ def __call__(self, i: int = None, j: int = None) -> Grapher:
53
+ if i is None:
54
+ self.cur_axis = self.axes[0]
55
+ return self
56
+ if j is None and i is not None:
57
+ self.cur_axis = self.axes[i]
58
+ else:
59
+ self.cur_axis = self.axes[i,j]
60
+ return self
61
+
62
+ def new(self, rows: int = 1, cols: int = 1) -> Grapher:
63
+ self.fig, self.axes = plt.subplots(rows, cols)
64
+ if rows==1 and cols==1:
65
+ self.axes = [self.axes]
66
+ self.nrows = rows
67
+ self.ncols = cols
68
+ return self
69
+
70
+ def line(self,
71
+ xs: np.ndarray,
72
+ ys: np.ndarray,
73
+ color: str = None,
74
+ linestyle: str = None,
75
+ marker: str = None,
76
+ dB: bool = False) -> Grapher:
77
+ if dB:
78
+ ys = 20*np.log10(np.abs(ys))
79
+ self.ax.plot(xs, ys, **self.style.get_line_properties(color=color, linestyle=linestyle, marker=marker))
80
+ self.reset()
81
+ return self
82
+
83
+ def show(self):
84
+ for ax in self.axes:
85
+ self.style.dress(ax)
86
+ plt.show()
87
+
88
+ gr = Grapher().new()
89
+
90
+ xs = np.linspace(1e9, 2e9, 1001)
91
+ ys = np.sin(xs/1e8)
92
+
93
+ gr().line(xs, ys).show()