Skip to content

Commit a34a538

Browse files
Added Spiral Inductor Example (#640)
* Added Spiral Example * Added Spiral Example * Update examples/02-HFSS/HFSS_Spiral.py * Update pyaedt/modeler/Primitives3D.py * Update pyaedt/modeler/Primitives3D.py * Update pyaedt/modeler/Primitives3D.py * Update pyaedt/modeler/Primitives3D.py * Update pyaedt/modeler/Primitives3D.py * Update pyaedt/modeler/Primitives3D.py * Update pyaedt/modeler/Primitives3D.py * Apply suggestions from code review Co-authored-by: Maxime Rey <[email protected]> * Apply suggestions from code review Co-authored-by: Maxime Rey <[email protected]> Co-authored-by: Maxime Rey <[email protected]>
1 parent c58d9ac commit a34a538

File tree

4 files changed

+234
-1
lines changed

4 files changed

+234
-1
lines changed

_unittest/test_07_Object3D.py

+6
Original file line numberDiff line numberDiff line change
@@ -337,3 +337,9 @@ def test_16_duplicate_around_axis_and_unite(self):
337337
def test_17_section_object(self):
338338
o = self.aedtapp.modeler.primitives.create_box([-10, 0, 0], [10, 10, 5], "SectionBox", "Copper")
339339
o.section(plane="YZ", create_new=True, section_cross_object=False)
340+
341+
def test_18_create_spiral(self):
342+
sp1 = self.aedtapp.modeler.create_spiral(name="ind")
343+
assert sp1
344+
assert sp1.name == "ind"
345+
assert len(sp1.points) == 78

examples/02-HFSS/HFSS_Spiral.py

+162
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
"""
2+
Spiral Inductor Example
3+
-----------------------
4+
This example shows how you can use PyAEDT to create a spiral inductor, solve it and plot results.
5+
"""
6+
# sphinx_gallery_thumbnail_path = 'Resources/spiral.png'
7+
8+
#############################################################
9+
# Import packages
10+
#
11+
12+
import os
13+
import matplotlib.pyplot as plt
14+
from pyaedt import Hfss, constants
15+
16+
17+
#############################################################
18+
# Launch Hfss 2021.2 in non graphical mode.
19+
# Change units to micron
20+
hfss = Hfss(specified_version="2021.2", non_graphical=False, designname='A1')
21+
hfss.modeler.model_units = "um"
22+
p = hfss.modeler.primitives
23+
24+
#############################################################
25+
# Input Variables. Edit it to change your inductor.
26+
#
27+
rin = 10
28+
width = 2
29+
spacing = 1
30+
thickness = 1
31+
Np = 8
32+
Nr = 10
33+
gap = 3
34+
Tsub = 6
35+
36+
37+
#############################################################
38+
# This method standardizes the usage of polyline in this
39+
# example by fixing the width, thickness and material.
40+
#
41+
def create_line(pts):
42+
p.create_polyline(pts,
43+
xsection_type='Rectangle',
44+
xsection_width=width,
45+
xsection_height=thickness, matname='copper')
46+
47+
48+
################################################################
49+
# Here a spiral inductor is created.
50+
# Spiral is not parameteric but could be parametrized later on.
51+
#
52+
53+
ind = hfss.modeler.create_spiral(internal_radius=rin, width=width, spacing=spacing, turns=Nr, faces=Np,
54+
thickness=thickness, material="copper", name="Inductor1")
55+
56+
57+
################################################################
58+
# Center Return Path.
59+
#
60+
61+
x0, y0, z0 = ind.points[0]
62+
x1, y1, z1 = ind.points[-1]
63+
create_line([(x0 - width / 2, y0, -gap), (abs(x1) + 5, y0, -gap)])
64+
p.create_box((x0 - width / 2, y0 - width / 2, -gap - thickness / 2),
65+
(width, width, gap + thickness),
66+
matname='copper')
67+
68+
################################################################
69+
# Port 1 creation.
70+
#
71+
p.create_rectangle(constants.PLANE.YZ,
72+
(abs(x1) + 5, y0 - width / 2, -gap - thickness / 2),
73+
(width, -Tsub + gap),
74+
name='port1')
75+
hfss.create_lumped_port_to_sheet(sheet_name='port1', axisdir=constants.AXIS.Z)
76+
77+
78+
################################################################
79+
# Port 2 creation.
80+
#
81+
create_line([(x1 + width / 2, y1, 0), (x1 - 5, y1, 0)])
82+
p.create_rectangle(constants.PLANE.YZ,
83+
(x1 - 5, y1 - width / 2, -thickness / 2),
84+
(width, -Tsub),
85+
name='port2')
86+
hfss.create_lumped_port_to_sheet(sheet_name='port2', axisdir=constants.AXIS.Z)
87+
88+
89+
################################################################
90+
# Silicon Substrate and Ground plane.
91+
#
92+
p.create_box([x1 - 20, x1 - 20, -Tsub - thickness / 2],
93+
[-2 * x1 + 40, -2 * x1 + 40, Tsub],
94+
matname='silicon')
95+
96+
p.create_box([x1 - 20, x1 - 20, -Tsub - thickness / 2],
97+
[-2 * x1 + 40, -2 * x1 + 40, -0.1],
98+
matname='PEC')
99+
100+
################################################################
101+
# Airbox and radiation assignment.
102+
#
103+
box = p.create_box([x1 - 20, x1 - 20, -Tsub - thickness / 2 - 0.1],
104+
[-2 * x1 + 40, -2 * x1 + 40, 100],
105+
name='airbox',
106+
matname='air')
107+
108+
hfss.assign_radiation_boundary_to_objects('airbox')
109+
110+
################################################################
111+
# Material override is needed to avoid validation check to fail.
112+
#
113+
hfss.change_material_override()
114+
115+
################################################################
116+
# Create a setup and define a frequency sweep.
117+
# Project will be solved after that.
118+
119+
setup1 = hfss.create_setup(setupname='setup1')
120+
setup1.props['Frequency'] = '10GHz'
121+
hfss.create_linear_count_sweep('setup1', 'GHz', 1e-3, 50, 451, sweep_type="Interpolating")
122+
setup1.update()
123+
hfss.save_project()
124+
hfss.analyze_all()
125+
126+
################################################################
127+
# Get Report Data and plot it on matplotlib.
128+
# Inductance.
129+
L_formula = '1e9*im(1/Y(1,1))/(2*pi*freq)'
130+
x = hfss.post.get_report_data(L_formula)
131+
132+
plt.plot(x.sweeps["Freq"], x.data_real(L_formula))
133+
134+
plt.grid()
135+
plt.xlabel('Freq')
136+
plt.ylabel('L(nH)')
137+
plt.show()
138+
139+
plt.clf()
140+
141+
142+
################################################################
143+
# Get Report Data and plot it on matplotlib.
144+
# Quality Factor.
145+
146+
Q_formula = 'im(Y(1,1))/re(Y(1,1))'
147+
x = hfss.post.get_report_data(Q_formula)
148+
hfss.save_project()
149+
150+
plt.plot(x.sweeps["Freq"], x.data_real(Q_formula))
151+
plt.grid()
152+
plt.xlabel('Freq')
153+
plt.ylabel('Q')
154+
plt.show()
155+
156+
################################################################
157+
# Get Report Data and plot it on matplotlib.
158+
# Save and close the project.
159+
160+
hfss.save_project()
161+
if os.name != "posix":
162+
hfss.release_desktop()

pyaedt/modeler/Primitives.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,6 @@ def __init__(
192192
num_seg=xsection_num_seg,
193193
bend_type=xsection_bend_type,
194194
)
195-
#self._positions = copy(position_list)
196195
self._positions = [i for i in position_list]
197196
# When close surface or cover_surface are set to True, ensure the start point and end point are coincident,
198197
# and insert a line segment to achieve this if necessary
@@ -256,6 +255,11 @@ def end_point(self):
256255
end_vertex_id = self._primitives.get_object_vertices(partID=self.id)[-1]
257256
return self._primitives.get_vertex_position(end_vertex_id)
258257

258+
@property
259+
def points(self):
260+
"""Polyline Points."""
261+
return self._positions
262+
259263
@property
260264
def vertex_positions(self):
261265
"""List of the ``[x, y, z]`` coordinates for all vertex positions in the

pyaedt/modeler/Primitives3D.py

+61
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import os
2+
from math import pi, cos, sin, tan
3+
24
from pyaedt.generic.general_methods import aedt_exception_handler
35
from pyaedt.modeler.Primitives import Primitives
46
from pyaedt.modeler.GeometryOperators import GeometryOperators
@@ -814,6 +816,65 @@ def create_udm(self, udmfullname, udm_params_list, udm_library='syslib'):
814816
else:
815817
return False
816818

819+
@aedt_exception_handler
820+
def create_spiral(self, internal_radius=10, spacing=1, faces=8, turns=10, width=2, thickness=1, elevation=0,
821+
material="copper", name=None):
822+
"""Create a spiral inductor from a polyline.
823+
824+
Parameters
825+
----------
826+
internal_radius : float, optional
827+
Internal starting point of spiral. Default is `10`.
828+
spacing : float, optional
829+
Internal pitch between two turns. Default is `1`.
830+
faces : int, optional
831+
Number of faces per turn. Default is `8` as an octagon.
832+
turns : int, optional
833+
Number of turns. Default is `10`.
834+
width : float, optional
835+
Spiral width. Default is `2`.
836+
thickness : float, optional
837+
Spiral thickness. Default is `1`.
838+
elevation : float, optional
839+
Spiral elevation. Default is`0`.
840+
material : str, optional
841+
Spiral material. Default is `"copper"`.
842+
name : str, optional
843+
Spiral name. Default is `None`.
844+
845+
Returns
846+
-------
847+
:class:`pyaedt.modeler.Object3d.Polyline`
848+
Polyline object.
849+
"""
850+
assert internal_radius > 0, "Internal Radius must be greater than 0."
851+
assert faces > 0, "Faces must be greater than 0."
852+
dtheta = 2 * pi / faces
853+
theta = pi / 2
854+
pts = [(internal_radius, 0, elevation), (internal_radius, internal_radius * tan(dtheta / 2), elevation)]
855+
rin = internal_radius * tan(dtheta / 2) * 2
856+
x = rin
857+
r = rin
858+
for i in range(faces):
859+
r += 1
860+
theta += dtheta
861+
x = x + r * cos(theta)
862+
dr = (width + spacing) / (x - rin)
863+
864+
for i in range(turns * faces - int(faces / 2) - 1):
865+
rin += dr
866+
theta += dtheta
867+
x0, y0 = pts[-1][:2]
868+
x1, y1 = x0 + rin * cos(theta), y0 + rin * sin(theta)
869+
pts.append((x1, y1, elevation))
870+
871+
pts.append((x1, 0, elevation))
872+
p1 = self.create_polyline(pts, xsection_type='Rectangle', xsection_width=width, xsection_height=thickness,
873+
matname=material)
874+
if name:
875+
p1.name = name
876+
return p1
877+
817878
@aedt_exception_handler
818879
def insert_3d_component(self, compFile, geoParams=None, szMatParams='', szDesignParams='', targetCS='Global'):
819880
"""Insert a new 3D component.

0 commit comments

Comments
 (0)