Skip to content

Added Spiral Inductor Example #640

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Dec 14, 2021
6 changes: 6 additions & 0 deletions _unittest/test_07_Object3D.py
Original file line number Diff line number Diff line change
Expand Up @@ -337,3 +337,9 @@ def test_16_duplicate_around_axis_and_unite(self):
def test_17_section_object(self):
o = self.aedtapp.modeler.primitives.create_box([-10, 0, 0], [10, 10, 5], "SectionBox", "Copper")
o.section(plane="YZ", create_new=True, section_cross_object=False)

def test_18_create_spiral(self):
sp1 = self.aedtapp.modeler.create_spiral(name="ind")
assert sp1
assert sp1.name == "ind"
assert len(sp1.points) == 78
163 changes: 163 additions & 0 deletions examples/02-HFSS/HFSS_Spiral.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
"""
Spiral Inductor Example
-----------------------
This example shows how you can use PyAEDT to create a spiral inductor, solve it and plot results.
"""
# sphinx_gallery_thumbnail_path = 'Resources/spiral.png'

#############################################################
# Import packages
#

import os
import matplotlib.pyplot as plt
from pyaedt import Hfss, constants


#############################################################
# Launch Hfss 2021.2 in non graphical mode.
# Change units to micron
hfss = Hfss(specified_version="2021.2", non_graphical=False, designname='A1')
hfss.modeler.model_units = "um"
p = hfss.modeler.primitives

#############################################################
# Input Variables. Edit it to change your inductor.
#
rin = 10
width = 2
spacing = 1
thickness = 1
Np = 8
Nr = 10
gap = 3
Tsub = 6


#############################################################
# This method standardizes the usage of polyline in this
# example by fixing the width, thickness and material.
#
def create_line(pts):
p.create_polyline(pts,
xsection_type='Rectangle',
xsection_width=width,
xsection_height=thickness, matname='copper')


################################################################
# Here a spiral inductor is created.
# Spiral is not parameteric but could be parametrized later on.
#

ind = hfss.modeler.create_spiral(internal_radius=rin, width=width, spacing=spacing, turns=Nr, faces=Np,
thickness=thickness, material="copper", name="Inductor1")


################################################################
# Center Return Path.
#

x0, y0, z0 = ind.points[0]
x1, y1, z1 = ind.points[-1]
create_line([(x0 - width / 2, y0, -gap), (abs(x1) + 5, y0, -gap)])
p.create_box((x0 - width / 2, y0 - width / 2, -gap - thickness / 2),
(width, width, gap + thickness),
matname='copper')

################################################################
# Port 1 creation.
#
p.create_rectangle(constants.PLANE.YZ,
(abs(x1) + 5, y0 - width / 2, -gap - thickness / 2),
(width, -Tsub + gap),
name='port1')
hfss.create_lumped_port_to_sheet(sheet_name='port1', axisdir=constants.AXIS.Z)


################################################################
# Port 2 creation.
#
create_line([(x1 + width / 2, y1, 0), (x1 - 5, y1, 0)])
p.create_rectangle(constants.PLANE.YZ,
(x1 - 5, y1 - width / 2, -thickness / 2),
(width, -Tsub),
name='port2')
hfss.create_lumped_port_to_sheet(sheet_name='port2', axisdir=constants.AXIS.Z)


################################################################
# Silicon Substrate and Ground plane.
#
p.create_box([x1 - 20, x1 - 20, -Tsub - thickness / 2],
[-2 * x1 + 40, -2 * x1 + 40, Tsub],
matname='silicon')

p.create_box([x1 - 20, x1 - 20, -Tsub - thickness / 2],
[-2 * x1 + 40, -2 * x1 + 40, -0.1],
matname='PEC')

################################################################
# Airbox and radiation assignment.
#

box = p.create_box([x1 - 20, x1 - 20, -Tsub - thickness / 2 - 0.1],
[-2 * x1 + 40, -2 * x1 + 40, 100],
name='airbox',
matname='air')

hfss.assign_radiation_boundary_to_objects('airbox')

################################################################
# Material override is needed to avoid validation check to fail.
#
hfss.change_material_override()

################################################################
# Create a setup and define a frequency sweep.
# Project will be solved after that.

setup1 = hfss.create_setup(setupname='setup1')
setup1.props['Frequency'] = '10GHz'
hfss.create_linear_count_sweep('setup1', 'GHz', 1e-3, 50, 451, sweep_type="Interpolating")
setup1.update()
hfss.save_project()
hfss.analyze_all()

################################################################
# Get Report Data and plot it on matplotlib.
# Inductance.
L_formula = '1e9*im(1/Y(1,1))/(2*pi*freq)'
x = hfss.post.get_report_data(L_formula)

plt.plot(x.sweeps["Freq"], x.data_real(L_formula))

plt.grid()
plt.xlabel('Freq')
plt.ylabel('L(nH)')
plt.show()

plt.clf()


################################################################
# Get Report Data and plot it on matplotlib.
# Quality Factor.

L_formula = 'im(Y(1,1))/re(Y(1,1))'
x = hfss.post.get_report_data(L_formula)
hfss.save_project()

plt.plot(x.sweeps["Freq"], x.data_real(L_formula))
plt.grid()
plt.xlabel('Freq')
plt.ylabel('Q')
plt.show()

################################################################
# Get Report Data and plot it on matplotlib.
# Save and close the project.

hfss.save_project()
if os.name != "posix":
hfss.release_desktop()
6 changes: 5 additions & 1 deletion pyaedt/modeler/Primitives.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,6 @@ def __init__(
num_seg=xsection_num_seg,
bend_type=xsection_bend_type,
)
#self._positions = copy(position_list)
self._positions = [i for i in position_list]
# When close surface or cover_surface are set to True, ensure the start point and end point are coincident,
# and insert a line segment to achieve this if necessary
Expand Down Expand Up @@ -256,6 +255,11 @@ def end_point(self):
end_vertex_id = self._primitives.get_object_vertices(partID=self.id)[-1]
return self._primitives.get_vertex_position(end_vertex_id)

@property
def points(self):
"""Polyline Points."""
return self._positions

@property
def vertex_positions(self):
"""List of the ``[x, y, z]`` coordinates for all vertex positions in the
Expand Down
60 changes: 60 additions & 0 deletions pyaedt/modeler/Primitives3D.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import os
from math import pi, cos, sin, tan

from pyaedt.generic.general_methods import aedt_exception_handler
from pyaedt.modeler.Primitives import Primitives
from pyaedt.modeler.GeometryOperators import GeometryOperators
Expand Down Expand Up @@ -814,6 +816,64 @@ def create_udm(self, udmfullname, udm_params_list, udm_library='syslib'):
else:
return False

@aedt_exception_handler
def create_spiral(self, internal_radius=10, spacing=1, faces=8, turns=10, width=2, thickness=1, elevation=0,
material="copper", name=None):
"""Create a spiral inductor from a polyne.

Parameters
----------
internal_radius : float, optional
internal starting point of spiral. Default is `10`.
spacing : float, optional
internal ptich between two turns. Default is `1`.
faces : int, optional
Number of faces per turn. Default is `8` as an octagon.
turns : int, optional
Number of turns. Default is `10`.
width : float, optional
spiral width. Default is `2`.
thickness : float, optional
Spiral thickness. Default is `1`.
elevation : float, optional
Spiral elevation. Default is`0`.
material : str, optional
Spiral material. Default is `"copper"`.
name : str, optional
Spiral name.Default is `None`.

Returns
-------
:class:`pyaedt.modeler.Object3d.Polyline`
Polyline object.
"""
assert internal_radius > 0, "Internal Radius must be greater than 0"
dtheta = 2 * pi / faces
theta = pi / 2
pts = [(internal_radius, 0, elevation), (internal_radius, internal_radius * tan(dtheta / 2), elevation)]
rin = internal_radius * tan(dtheta / 2) * 2
x = rin
r = rin
for i in range(faces):
r += 1
theta += dtheta
x = x + r * cos(theta)
dr = (width + spacing) / (x - rin)

for i in range(turns * faces - int(faces / 2) - 1):
rin += dr
theta += dtheta
x0, y0 = pts[-1][:2]
x1, y1 = x0 + rin * cos(theta), y0 + rin * sin(theta)
pts.append((x1, y1, elevation))

pts.append((x1, 0, elevation))
p1 = self.create_polyline(pts, xsection_type='Rectangle', xsection_width=width, xsection_height=thickness,
matname=material)
if name:
p1.name = name
return p1

@aedt_exception_handler
def insert_3d_component(self, compFile, geoParams=None, szMatParams='', szDesignParams='', targetCS='Global'):
"""Insert a new 3D component.
Expand Down