# -*- coding: utf-8 -*-
#
# Copyright (C) 2023 - 2026 ANSYS, Inc. and/or its affiliates.
# SPDX-License-Identifier: MIT
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from collections import OrderedDict
import math
import ansys.aedt.core.generic.constants as constants
from ansys.aedt.core.generic.constants import Axis
from ansys.aedt.core.generic.constants import Plane
from ansys.aedt.core.generic.general_methods import pyaedt_function_handler
from ansys.aedt.toolkits.common.backend.logger_handler import logger
from ansys.aedt.toolkits.antenna.backend.antenna_models.common import CommonAntenna
class CommonHelix(CommonAntenna):
"""Provides base methods common to horn antenna."""
def __init__(self, _default_input_parameters, *args, **kwargs):
CommonAntenna.antenna_type = "Helix"
CommonAntenna.__init__(self, _default_input_parameters, *args, **kwargs)
@property
def material(self):
"""Helix material.
Returns
-------
str
"""
return self._input_parameters.material
@material.setter
def material(self, value):
if self._app:
if (
value
and value not in self._app.materials.mat_names_aedt
and value not in self._app.materials.mat_names_aedt_lower
):
logger.debug("Material not defined")
else:
if value != self.material and self.object_list:
for antenna_obj in self.object_list:
if (
self.object_list[antenna_obj].material_name == self.material.lower()
and "coax" not in antenna_obj
):
self.object_list[antenna_obj].material_name = value
self._input_parameters.material = value
parameters = self.synthesis()
self.update_synthesis_parameters(parameters)
self.set_variables_in_hfss()
@property
def gain(self):
"""Helix expected gain.
Returns
-------
float
"""
return self._input_parameters.gain
@gain.setter
def gain(self, value):
self._input_parameters.gain = value
if value != self.gain and self.object_list:
parameters = self.synthesis()
self.update_synthesis_parameters(parameters)
self.set_variables_in_hfss()
@property
def direction(self):
"""Helix direction. ``0`` for left, and ``1`` for right.
Returns
-------
int
"""
return self._input_parameters.direction
@direction.setter
def direction(self, value):
if isinstance(value, str) and value.lower() == "left":
value = 0
elif isinstance(value, str) and value.lower() == "right":
value = 1
self._input_parameters.direction = value
if self.object_list:
parameters = self.synthesis()
self.update_synthesis_parameters(parameters)
self.set_variables_in_hfss()
@property
def feeder_length(self):
"""Helix feeder length.
Returns
-------
float
"""
return self._input_parameters.feeder_length
@feeder_length.setter
def feeder_length(self, value):
self._input_parameters.feeder_length = value
if value != self.feeder_length and self.object_list:
parameters = self.synthesis()
self.update_synthesis_parameters(parameters)
self.set_variables_in_hfss()
@staticmethod
def _ordered_parameters(parameters):
my_keys = list(parameters.keys())
my_keys.sort()
return OrderedDict([(i, parameters[i]) for i in my_keys])
def _validate_material(self):
if (
self.material not in self._app.materials.mat_names_aedt
and self.material not in self._app.materials.mat_names_aedt_lower
):
self._app.logger.warning("Material not found. Create the material before assigning it.")
return False
return True
def _set_unitless_parameter(self, parameter_name):
parameter = getattr(self.synthesis_parameters, parameter_name, None)
if parameter:
self._app[parameter.hfss_variable] = str(parameter.value)
def _single_feed_model(self, radius_change="0"):
if self.object_list:
logger.debug("This antenna is already defined")
return False
if not self._validate_material():
return False
self.set_variables_in_hfss()
groundx = self.synthesis_parameters.groundx.hfss_variable
groundy = self.synthesis_parameters.groundy.hfss_variable
diameter = self.synthesis_parameters.diameter.hfss_variable
wire_diameter = self.synthesis_parameters.wire_diameter.hfss_variable
spacing = self.synthesis_parameters.spacing.hfss_variable
coax_inner_radius = self.synthesis_parameters.coax_inner_radius.hfss_variable
coax_outer_radius = self.synthesis_parameters.coax_outer_radius.hfss_variable
feed_pinl = self.synthesis_parameters.feed_pinL.hfss_variable
feed_pind = self.synthesis_parameters.feed_pinD.hfss_variable
feeder_length = self.synthesis_parameters.feeder_length.hfss_variable
number_of_turns = self.synthesis_parameters.number_of_turns.hfss_variable
self._set_unitless_parameter("number_of_turns")
pos_x = self.synthesis_parameters.pos_x.hfss_variable
pos_y = self.synthesis_parameters.pos_y.hfss_variable
pos_z = self.synthesis_parameters.pos_z.hfss_variable
antenna_name = self.name
coordinate_system = self.coordinate_system
my_udm_pairs = [
["PolygonSegments", "8"],
["PolygonRadius", "{}/2".format(wire_diameter)],
["StartHelixRadius", "{}/2".format(diameter)],
["RadiusChange", radius_change],
["Pitch", spacing],
["Turns", str(number_of_turns)],
["SegmentsPerTurn", "16"],
["RightHanded", self.direction],
]
udm = self._app.modeler.create_udp(
dll="SegmentedHelix/PolygonHelix.dll",
parameters=my_udm_pairs,
library="syslib",
name="helix",
)
udm_obj = self._app.get_oo_object(self._app.oeditor, udm.name)
# Set direction
self._app.set_oo_property_value(
aedt_object=udm_obj, object_name="CreateUserDefinedPart:1", prop_name="RightHanded", value=self.direction
)
# Set coordinate system of udm
self._app.set_oo_property_value(
aedt_object=udm_obj,
object_name="CreateUserDefinedPart:1",
prop_name="Coordinate System",
value=coordinate_system,
)
udm.material_name = "pec"
self._app.modeler.split(udm, "XY", "PositiveOnly")
gnd = self._app.modeler.create_rectangle(
Plane.XY,
[
"-{}/2".format(groundx),
"-{}/2".format(groundy),
"-{}-{}/2".format(feed_pinl, wire_diameter),
],
[groundx, groundy],
name="gnd_" + antenna_name,
new_properties={"Coordinate System": coordinate_system},
)
cutout = self._app.modeler.create_circle(
orientation=2,
origin=[
"{}/2".format(diameter),
"-{}/2".format(feed_pind),
"-{}-{}/2".format(feed_pinl, wire_diameter),
],
radius=coax_outer_radius,
new_properties={"Coordinate System": coordinate_system},
)
gnd.subtract(cutout, keep_originals=False)
feed_pin = self._app.modeler.create_cylinder(
orientation=2,
origin=[
"{}/2".format(diameter),
"-{}/2".format(feed_pind),
"-{}-{}/2".format(feed_pinl, wire_diameter),
],
radius=feed_pind + "/2",
height=feed_pinl + "+" + wire_diameter + "/2",
name="Feed_{}".format(antenna_name),
material="pec",
new_properties={"Coordinate System": coordinate_system},
)
feed_coax = self._app.modeler.create_cylinder(
orientation=2,
origin=[
"{}/2".format(diameter),
"-{}/2".format(feed_pind),
"-{}-{}/2".format(feed_pinl, wire_diameter),
],
radius=coax_inner_radius,
height="-{}".format(feeder_length),
name="Feed1_{}".format(antenna_name),
material="pec",
new_properties={"Coordinate System": coordinate_system},
)
coax = self._app.modeler.create_cylinder(
orientation=2,
origin=[
"{}/2".format(diameter),
"-{}/2".format(feed_pind),
"-{}-{}/2".format(feed_pinl, wire_diameter),
],
radius=coax_outer_radius,
height="-{}".format(feeder_length),
name="coax_{}".format(antenna_name),
material="Teflon (tm)",
new_properties={"Coordinate System": coordinate_system},
)
cap = self._app.modeler.create_cylinder(
orientation=2,
origin=[
"{}/2".format(diameter),
"-{}/2".format(feed_pind),
"-{}-{}/2-{}".format(feed_pinl, wire_diameter, feeder_length),
],
radius=coax_outer_radius,
height="-{}/2".format(feed_pinl),
name="port_cap_" + antenna_name,
material="pec",
new_properties={"Coordinate System": coordinate_system},
)
p1 = self._app.modeler.create_circle(
orientation=2,
origin=[
"{}/2".format(diameter),
"-{}/2".format(feed_pind),
"-{}-{}/2-{}".format(feed_pinl, wire_diameter, feeder_length),
],
radius=coax_outer_radius,
name="port_" + antenna_name,
new_properties={"Coordinate System": coordinate_system},
)
p1.color = (128, 0, 0)
objects = [udm, feed_coax, feed_pin, coax, cap, gnd, p1]
for obj in objects:
obj.group_name = antenna_name
self._app.modeler.move(objects, [pos_x, pos_y, pos_z])
for obj in objects:
self.object_list[obj.name] = obj
self._app.modeler.fit_all()
return True
def _quadrifilar_model(self, shorted=False):
if self.object_list:
logger.debug("This antenna is already defined")
return False
if not self._validate_material():
return False
self.set_variables_in_hfss()
groundx = self.synthesis_parameters.groundx.hfss_variable
groundy = self.synthesis_parameters.groundy.hfss_variable
diameter = self.synthesis_parameters.diameter.hfss_variable
wire_diameter = self.synthesis_parameters.wire_diameter.hfss_variable
spacing = self.synthesis_parameters.spacing.hfss_variable
port_height = self.synthesis_parameters.port_height.hfss_variable
number_of_turns = self.synthesis_parameters.number_of_turns.hfss_variable
self._set_unitless_parameter("number_of_turns")
pos_x = self.synthesis_parameters.pos_x.hfss_variable
pos_y = self.synthesis_parameters.pos_y.hfss_variable
pos_z = self.synthesis_parameters.pos_z.hfss_variable
antenna_name = self.name
coordinate_system = self.coordinate_system
objects = []
gnd = self._app.modeler.create_rectangle(
Plane.XY,
[
"-{}/2".format(groundx),
"-{}/2".format(groundy),
"-2*{}-{}/2".format(port_height, wire_diameter),
],
[groundx, groundy],
name="gnd_" + antenna_name,
new_properties={"Coordinate System": coordinate_system},
)
objects.append(gnd)
udp_pairs = [
["PolygonSegments", "8"],
["PolygonRadius", "{}/2".format(wire_diameter)],
["StartHelixRadius", "{}/2".format(diameter)],
["RadiusChange", "0"],
["Pitch", spacing],
["Turns", str(number_of_turns)],
["SegmentsPerTurn", "16"],
["RightHanded", self.direction],
]
helix = self._app.modeler.create_udp(
dll="SegmentedHelix/PolygonHelix.dll",
parameters=udp_pairs,
library="syslib",
name="helix_1",
)
# Set direction
helix_obj = self._app.get_oo_object(self._app.oeditor, helix.name)
# Set direction
self._app.set_oo_property_value(
aedt_object=helix_obj, object_name="CreateUserDefinedPart:1", prop_name="RightHanded", value=self.direction
)
# Set coordinate system of udm
self._app.set_oo_property_value(
aedt_object=helix_obj,
object_name="CreateUserDefinedPart:1",
prop_name="Coordinate System",
value=coordinate_system,
)
helix.material_name = "pec"
self._app.modeler.split(helix, "XY", "PositiveOnly")
helix_names = [helix.name] + helix.duplicate_around_axis(Axis.Z, 90, 4)
feed_pin = self._app.modeler.create_cylinder(
orientation=2,
origin=["{}/2".format(diameter), "0", "-{}-{}/2".format(port_height, wire_diameter)],
radius="{}/2".format(wire_diameter),
height="{}+{}/2".format(port_height, wire_diameter),
name="feed_1",
material="pec",
new_properties={"Coordinate System": coordinate_system},
)
feed_names = [feed_pin.name] + feed_pin.duplicate_around_axis(Axis.Z, 90, 4)
port = self._app.modeler.create_rectangle(
orientation=Plane.ZX,
origin=[
"{}/2-{}/2".format(diameter, wire_diameter),
"0",
"-{}-{}/2".format(port_height, wire_diameter),
],
sizes=["-{}".format(port_height), wire_diameter],
name="port_lump_{}_1".format(antenna_name),
new_properties={"Coordinate System": coordinate_system},
)
port.color = (128, 0, 0)
port_names = [port.name] + port.duplicate_around_axis(Axis.Z, 90, 4)
for index, object_name in enumerate(helix_names, start=1):
obj = self._app.modeler[object_name]
if index > 1:
obj.name = "helix_{}".format(index)
obj = self._app.modeler[obj.name]
helix_names[index - 1] = obj.name
objects.append(obj)
for index, object_name in enumerate(feed_names, start=1):
obj = self._app.modeler[object_name]
if index > 1:
obj.name = "feed_{}".format(index)
obj = self._app.modeler[obj.name]
objects.append(obj)
for index, object_name in enumerate(port_names, start=1):
obj = self._app.modeler[object_name]
if index > 1:
obj.name = "port_lump_{}_{}".format(antenna_name, index)
obj = self._app.modeler[obj.name]
obj.color = (128, 0, 0)
objects.append(obj)
if shorted:
short_1 = self._app.modeler.create_cylinder(
orientation=0,
origin=["{}/2".format(diameter), 0.0, "{}*{}".format(number_of_turns, spacing)],
radius="{}/2".format(wire_diameter),
height="-{}".format(diameter),
name="short_1",
material="pec",
new_properties={"Coordinate System": coordinate_system},
)
short_2 = self._app.modeler.create_cylinder(
orientation=1,
origin=[0.0, "{}/2".format(diameter), "{}*{}".format(number_of_turns, spacing)],
radius="-{}/2".format(wire_diameter),
height="-{}".format(diameter),
name="short_2",
material="pec",
new_properties={"Coordinate System": coordinate_system},
)
objects.extend([short_1, short_2])
# Unite all helix and short objects
helix_and_short_objs = [self._app.modeler[name] for name in helix_names] + [short_1, short_2]
self._app.modeler.unite(helix_and_short_objs)
# _ = helix_and_short_objs[0]
# Remove united objects from objects list and add the united one
for obj in helix_and_short_objs[1:]:
if obj in objects:
objects.remove(obj)
for obj in objects:
obj.group_name = antenna_name
self._app.modeler.move(objects, [pos_x, pos_y, pos_z])
for obj in objects:
self.object_list[obj.name] = obj
self._app.modeler.fit_all()
return True
@pyaedt_function_handler()
def synthesis(self):
pass
[docs]
class AxialMode(CommonHelix):
"""Manages an axial mode helix antenna.
This class is accessible through the ``Hfss`` object [1]_.
Parameters
----------
frequency : float, optional
Center frequency. The default is ``10.0``.
frequency_unit : str, optional
Frequency units. The default is ``"GHz"``.
material : str, optional
Helix material. If the material is not defined, a new material,
``parametrized``, is defined. The default is ``"pec"``.
outer_boundary : str, optional
Boundary type to use. The default is ``None``. Options are ``"FEBI"``, ``"PML"``,
``"Radiation"``, and ``None``.
length_unit : str, optional
Length units. The default is ``"mm"``.
parametrized : bool, optional
Whether to create a parametrized antenna. The default is ``True``.
Returns
-------
:class:`aedt.toolkits.antenna.AxialMode`
Antenna object.
Notes
-----
.. [1] C. Balanis, "Wideband and Travelling-Wave Antennas,"
*Modern Antenna Handbook*, New York, 2008.
Examples
--------
>>> from ansys.aedt.toolkits.antenna.backend.antenna_models.helix import AxialMode
>>> import ansys.aedt.core
>>> app = ansys.aedt.core.Hfss()
>>> oantenna1 = AxialMode(app)
>>> oantenna1.frequency = 12.0
>>> oantenna1.model_hfss()
>>> oantenna1.setup_hfss()
>>> oantenna2 = AxialMode(app, origin=[200, 50, 0])
>>> oantenna2.model_hfss()
>>> oantenna2.setup_hfss()
>>> app.release_desktop(False, False)
"""
_default_input_parameters = {
"name": "",
"origin": [0, 0, 0],
"length_unit": "mm",
"coordinate_system": "Global",
"frequency": 10.0,
"frequency_unit": "GHz",
"gain": 10,
"direction": 0,
"feeder_length": 10,
"outer_boundary": "",
"material": "pec",
}
def __init__(self, *args, **kwargs):
CommonHelix.__init__(self, self._default_input_parameters, *args, **kwargs)
self._parameters = self.synthesis()
self.update_synthesis_parameters(self._parameters)
self.antenna_type = "AxialMode"
[docs]
@pyaedt_function_handler()
def synthesis(self):
"""Antenna synthesis.
Returns
-------
dict
Analytical parameters.
"""
parameters = {}
light_speed = constants.SpeedOfLight # m/s
freq_hz = constants.unit_converter(self.frequency, "Freq", self.frequency_unit, "Hz")
freq_ghz = constants.unit_converter(self.frequency, "Freq", self.frequency_unit, "GHz")
wl_meters = light_speed / freq_hz
gain_value_db = self.gain
gain_value_mag = math.pow(10.0, gain_value_db / 10.0)
groundx = constants.unit_converter(4.0 * (3.33 / freq_ghz), "Length", "in", "mm")
groundy = constants.unit_converter(4.0 * (3.33 / freq_ghz), "Length", "in", "mm")
helix_diameter = constants.unit_converter(1.128 * (3.33 / freq_ghz), "Length", "in", "mm")
helix_spacing = constants.unit_converter(0.786 * (3.33 / freq_ghz), "Length", "in", "mm")
helix_wiredia = constants.unit_converter(0.2 * (3.33 / freq_ghz), "Length", "in", "mm")
helix_coax_inner_radius = constants.unit_converter(0.082 * (3.33 / freq_ghz) / 2, "Length", "in", "mm")
helix_coax_outer_radius = constants.unit_converter(0.275 * (3.33 / freq_ghz) / 2, "Length", "in", "mm")
helix_feed_pinl = constants.unit_converter(0.05 * (3.33 / freq_ghz), "Length", "in", "mm")
helix_feed_pind = constants.unit_converter(0.082 * (3.33 / freq_ghz), "Length", "in", "mm")
helix_diameter_syn = wl_meters / math.pi * 0.9
helix_spacing_syn = math.pi * helix_diameter_syn * math.tan(math.radians(12.5))
helix_turns_syn = gain_value_mag * wl_meters / 15.0 / helix_spacing_syn
parameters["groundx"] = groundx
parameters["groundy"] = groundy
parameters["diameter"] = helix_diameter
parameters["spacing"] = helix_spacing
parameters["wire_diameter"] = helix_wiredia
parameters["coax_inner_radius"] = helix_coax_inner_radius
parameters["coax_outer_radius"] = helix_coax_outer_radius
parameters["feed_pinL"] = helix_feed_pinl
parameters["feed_pinD"] = helix_feed_pind
parameters["feed_pinL"] = helix_feed_pinl
parameters["feed_pinD"] = helix_feed_pind
parameters["number_of_turns"] = helix_turns_syn
parameters["feeder_length"] = self.feeder_length
parameters["pos_x"] = self.origin[0]
parameters["pos_y"] = self.origin[1]
parameters["pos_z"] = self.origin[2]
my_keys = list(parameters.keys())
my_keys.sort()
parameters_out = OrderedDict([(i, parameters[i]) for i in my_keys])
return parameters_out
[docs]
@pyaedt_function_handler()
def model_hfss(self):
"""Draw an axial mode antenna.
Once the antenna is created, this method is not used anymore.
"""
if self.object_list:
logger.debug("This antenna is already defined")
return False
if (
self.material not in self._app.materials.mat_names_aedt
and self.material not in self._app.materials.mat_names_aedt_lower
):
self._app.logger.warning("Material not found. Create the material before assigning it.")
return False
self.set_variables_in_hfss()
# Map parameters
groundx = self.synthesis_parameters.groundx.hfss_variable
groundy = self.synthesis_parameters.groundy.hfss_variable
diameter = self.synthesis_parameters.diameter.hfss_variable
wire_diameter = self.synthesis_parameters.wire_diameter.hfss_variable
spacing = self.synthesis_parameters.spacing.hfss_variable
coax_inner_radius = self.synthesis_parameters.coax_inner_radius.hfss_variable
coax_outer_radius = self.synthesis_parameters.coax_outer_radius.hfss_variable
feed_pinl = self.synthesis_parameters.feed_pinL.hfss_variable
feed_pind = self.synthesis_parameters.feed_pinD.hfss_variable
feeder_length = self.synthesis_parameters.feeder_length.hfss_variable
number_of_turns = self.synthesis_parameters.number_of_turns.hfss_variable
self._app[number_of_turns] = str(self.synthesis_parameters.number_of_turns.value)
pos_x = self.synthesis_parameters.pos_x.hfss_variable
pos_y = self.synthesis_parameters.pos_y.hfss_variable
pos_z = self.synthesis_parameters.pos_z.hfss_variable
antenna_name = self.name
coordinate_system = self.coordinate_system
my_udm_pairs = []
mypair = ["PolygonSegments", "8"]
my_udm_pairs.append(mypair)
mypair = ["PolygonRadius", "{}/2".format(wire_diameter)]
my_udm_pairs.append(mypair)
mypair = ["StartHelixRadius", "{}/2".format(diameter)]
my_udm_pairs.append(mypair)
mypair = ["RadiusChange", "0"]
my_udm_pairs.append(mypair)
mypair = ["Pitch", spacing]
my_udm_pairs.append(mypair)
mypair = ["Turns", str(number_of_turns)]
my_udm_pairs.append(mypair)
mypair = ["SegmentsPerTurn", "16"]
my_udm_pairs.append(mypair)
# Parameter not working in native API call, it is modified later
mypair = ["RightHanded", self.direction]
my_udm_pairs.append(mypair)
udm = self._app.modeler.create_udp(
dll="SegmentedHelix/PolygonHelix.dll",
parameters=my_udm_pairs,
library="syslib",
name="helix",
)
udm_obj = self._app.get_oo_object(self._app.oeditor, udm.name)
# Set direction
self._app.set_oo_property_value(
aedt_object=udm_obj, object_name="CreateUserDefinedPart:1", prop_name="RightHanded", value=self.direction
)
# Set coordinate system of udm
self._app.set_oo_property_value(
aedt_object=udm_obj,
object_name="CreateUserDefinedPart:1",
prop_name="Coordinate System",
value=coordinate_system,
)
udm.material_name = "pec"
self._app.modeler.split(udm, "XY", "PositiveOnly")
gnd = self._app.modeler.create_rectangle(
Plane.XY,
[
"-{}/2".format(groundx),
"-{}/2".format(groundy),
"-{}-{}/2".format(feed_pinl, wire_diameter),
],
[groundx, groundy],
name="gnd_" + antenna_name,
new_properties={"Coordinate System": coordinate_system},
)
cutout = self._app.modeler.create_circle(
orientation=2,
origin=[
"{}/2".format(diameter),
"-{}/2".format(feed_pind),
"-{}-{}/2".format(feed_pinl, wire_diameter),
],
radius=coax_outer_radius,
new_properties={"Coordinate System": coordinate_system},
)
gnd.subtract(cutout, keep_originals=False)
# Negative air
feed_pin = self._app.modeler.create_cylinder(
orientation=2,
origin=[
"{}/2".format(diameter),
"-{}/2".format(feed_pind),
"-{}-{}/2".format(feed_pinl, wire_diameter),
],
radius=feed_pind + "/2",
height=feed_pinl + "+" + wire_diameter + "/2",
name="Feed_{}".format(antenna_name),
material="pec",
new_properties={"Coordinate System": coordinate_system},
)
feed_coax = self._app.modeler.create_cylinder(
orientation=2,
origin=[
"{}/2".format(diameter),
"-{}/2".format(feed_pind),
"-{}-{}/2".format(feed_pinl, wire_diameter),
],
radius=coax_inner_radius,
height="-{}".format(feeder_length),
name="Feed1_{}".format(antenna_name),
material="pec",
new_properties={"Coordinate System": coordinate_system},
)
coax = self._app.modeler.create_cylinder(
orientation=2,
origin=[
"{}/2".format(diameter),
"-{}/2".format(feed_pind),
"-{}-{}/2".format(feed_pinl, wire_diameter),
],
radius=coax_outer_radius,
height="-{}".format(feeder_length),
name="coax_{}".format(antenna_name),
material="Teflon (tm)",
new_properties={"Coordinate System": coordinate_system},
)
# Cap
cap = self._app.modeler.create_cylinder(
orientation=2,
origin=[
"{}/2".format(diameter),
"-{}/2".format(feed_pind),
"-{}-{}/2-{}".format(feed_pinl, wire_diameter, feeder_length),
],
radius=coax_outer_radius,
height="-{}/2".format(feed_pinl),
name="port_cap_" + antenna_name,
material="pec",
new_properties={"Coordinate System": coordinate_system},
)
# P1
p1 = self._app.modeler.create_circle(
orientation=2,
origin=[
"{}/2".format(diameter),
"-{}/2".format(feed_pind),
"-{}-{}/2-{}".format(feed_pinl, wire_diameter, feeder_length),
],
radius=coax_outer_radius,
name="port_" + antenna_name,
new_properties={"Coordinate System": coordinate_system},
)
p1.color = (128, 0, 0)
udm.group_name = antenna_name
feed_coax.group_name = antenna_name
feed_pin.group_name = antenna_name
cap.group_name = antenna_name
gnd.group_name = antenna_name
p1.group_name = antenna_name
self._app.modeler.move([udm, feed_coax, feed_pin, coax, cap, gnd, p1], [pos_x, pos_y, pos_z])
self.object_list[udm.name] = udm
self.object_list[feed_coax.name] = feed_coax
self.object_list[feed_pin.name] = feed_pin
self.object_list[cap.name] = cap
self.object_list[gnd.name] = gnd
self.object_list[p1.name] = p1
self._app.modeler.fit_all()
return True
[docs]
@pyaedt_function_handler()
def model_disco(self):
"""Model in PyDiscovery. To be implemented."""
pass
[docs]
@pyaedt_function_handler()
def setup_disco(self):
"""Set up model in PyDiscovery. To be implemented."""
pass
[docs]
class AxialModeTaper(CommonHelix):
"""Manages a tapered axial mode helix antenna.
Notes
-----
.. [1] C. Balanis, "Wideband and Travelling-Wave Antennas,"
*Modern Antenna Handbook*, New York, 2008.
"""
_default_input_parameters = {
"name": "",
"origin": [0, 0, 0],
"length_unit": "mm",
"coordinate_system": "Global",
"frequency": 10.0,
"frequency_unit": "GHz",
"gain": 10,
"direction": 0,
"feeder_length": None,
"outer_boundary": "",
"material": "pec",
}
def __init__(self, *args, **kwargs):
CommonHelix.__init__(self, self._default_input_parameters, *args, **kwargs)
self._parameters = self.synthesis()
self.update_synthesis_parameters(self._parameters)
self.antenna_type = "AxialModeTaper"
[docs]
@pyaedt_function_handler()
def synthesis(self):
parameters = {}
light_speed = constants.SpeedOfLight
freq_hz = constants.unit_converter(self.frequency, "Freq", self.frequency_unit, "Hz")
freq_ghz = constants.unit_converter(self.frequency, "Freq", self.frequency_unit, "GHz")
wl_meters = light_speed / freq_hz
gain_value_mag = math.pow(10.0, self.gain / 10.0)
groundx = constants.unit_converter(4.0 * (3.33 / freq_ghz), "Length", "in", self.length_unit)
groundy = constants.unit_converter(4.0 * (3.33 / freq_ghz), "Length", "in", self.length_unit)
helix_diameter = constants.unit_converter(1.128 * (3.33 / freq_ghz), "Length", "in", self.length_unit)
helix_spacing = constants.unit_converter(0.786 * (3.33 / freq_ghz), "Length", "in", self.length_unit)
helix_wiredia = constants.unit_converter(0.2 * (3.33 / freq_ghz), "Length", "in", self.length_unit)
helix_coax_inner_radius = constants.unit_converter(
0.082 * (3.33 / freq_ghz) / 2.0, "Length", "in", self.length_unit
)
helix_coax_outer_radius = constants.unit_converter(
0.275 * (3.33 / freq_ghz) / 2.0, "Length", "in", self.length_unit
)
helix_feed_pinl = constants.unit_converter(0.05 * (3.33 / freq_ghz), "Length", "in", self.length_unit)
helix_feed_pind = constants.unit_converter(0.082 * (3.33 / freq_ghz), "Length", "in", self.length_unit)
default_feeder_length = constants.unit_converter(1.005 * (3.33 / freq_ghz), "Length", "in", self.length_unit)
helix_diameter_syn = wl_meters / math.pi * 0.9
helix_spacing_syn = math.pi * helix_diameter_syn * math.tan(math.radians(12.5))
helix_turns_syn = gain_value_mag * wl_meters / 15.0 / helix_spacing_syn
helix_radius_change_syn = helix_diameter_syn * 0.4 / 2.0 / helix_turns_syn
parameters["groundx"] = groundx
parameters["groundy"] = groundy
parameters["diameter"] = helix_diameter
parameters["spacing"] = helix_spacing
parameters["wire_diameter"] = helix_wiredia
parameters["coax_inner_radius"] = helix_coax_inner_radius
parameters["coax_outer_radius"] = helix_coax_outer_radius
parameters["feed_pinL"] = helix_feed_pinl
parameters["feed_pinD"] = helix_feed_pind
parameters["number_of_turns"] = helix_turns_syn
parameters["radius_change"] = constants.unit_converter(
helix_radius_change_syn, "Length", "meter", self.length_unit
)
parameters["feeder_length"] = self.feeder_length or default_feeder_length
parameters["pos_x"] = self.origin[0]
parameters["pos_y"] = self.origin[1]
parameters["pos_z"] = self.origin[2]
return self._ordered_parameters(parameters)
[docs]
@pyaedt_function_handler()
def model_hfss(self):
return self._single_feed_model("-{}".format(self.synthesis_parameters.radius_change.hfss_variable))
[docs]
@pyaedt_function_handler()
def model_disco(self):
"""Model in PyDiscovery. To be implemented."""
pass
[docs]
@pyaedt_function_handler()
def setup_disco(self):
"""Set up model in PyDiscovery. To be implemented."""
pass
[docs]
class NormalMode(CommonHelix):
"""Manages a normal mode helix antenna.
Notes
-----
.. [1] C. Balanis, "Wideband and Travelling-Wave Antennas,"
*Modern Antenna Handbook*, New York, 2008.
"""
_default_input_parameters = {
"name": "",
"origin": [0, 0, 0],
"length_unit": "mm",
"coordinate_system": "Global",
"frequency": 10.0,
"frequency_unit": "GHz",
"gain": 1,
"direction": 0,
"feeder_length": None,
"outer_boundary": "",
"material": "pec",
}
def __init__(self, *args, **kwargs):
CommonHelix.__init__(self, self._default_input_parameters, *args, **kwargs)
self._parameters = self.synthesis()
self.update_synthesis_parameters(self._parameters)
self.antenna_type = "NormalMode"
[docs]
@pyaedt_function_handler()
def synthesis(self):
parameters = {}
light_speed = constants.SpeedOfLight
freq_hz = constants.unit_converter(self.frequency, "Freq", self.frequency_unit, "Hz")
wl_meters = light_speed / freq_hz
groundx_syn = wl_meters
groundy_syn = wl_meters
helix_diameter_syn = wl_meters / 20.0
helix_spacing_syn = wl_meters / 20.0
helix_turns_syn = wl_meters / 8.0 / helix_spacing_syn / 1.375
zin = 2.0 * math.pow(25.3 * helix_turns_syn * helix_spacing_syn / wl_meters, 2)
helix_wiredia_syn = wl_meters * 0.01
helix_coax_inner_radius_syn = helix_wiredia_syn / 2.0
helix_coax_outer_radius_syn = helix_coax_inner_radius_syn * math.exp(zin * math.sqrt(2.2) / 60.0)
while helix_coax_outer_radius_syn >= wl_meters / 8.0 / math.sqrt(2.2):
helix_coax_inner_radius_syn *= 0.9
helix_coax_outer_radius_syn = helix_coax_inner_radius_syn * math.exp(zin * math.sqrt(2.2) / 60.0)
helix_feed_pinl_syn = wl_meters / 64.0
helix_feed_pind_syn = helix_coax_inner_radius_syn * 2.0
default_feeder_length = 5 * helix_feed_pind_syn
parameters["groundx"] = constants.unit_converter(groundx_syn, "Length", "meter", self.length_unit)
parameters["groundy"] = constants.unit_converter(groundy_syn, "Length", "meter", self.length_unit)
parameters["diameter"] = constants.unit_converter(helix_diameter_syn, "Length", "meter", self.length_unit)
parameters["spacing"] = constants.unit_converter(helix_spacing_syn, "Length", "meter", self.length_unit)
parameters["wire_diameter"] = constants.unit_converter(helix_wiredia_syn, "Length", "meter", self.length_unit)
parameters["coax_inner_radius"] = constants.unit_converter(
helix_coax_inner_radius_syn, "Length", "meter", self.length_unit
)
parameters["coax_outer_radius"] = constants.unit_converter(
helix_coax_outer_radius_syn, "Length", "meter", self.length_unit
)
parameters["feed_pinL"] = constants.unit_converter(helix_feed_pinl_syn, "Length", "meter", self.length_unit)
parameters["feed_pinD"] = constants.unit_converter(helix_feed_pind_syn, "Length", "meter", self.length_unit)
parameters["number_of_turns"] = helix_turns_syn
parameters["feeder_length"] = self.feeder_length or constants.unit_converter(
default_feeder_length, "Length", "meter", self.length_unit
)
parameters["pos_x"] = self.origin[0]
parameters["pos_y"] = self.origin[1]
parameters["pos_z"] = self.origin[2]
return self._ordered_parameters(parameters)
[docs]
@pyaedt_function_handler()
def model_hfss(self):
return self._single_feed_model()
[docs]
@pyaedt_function_handler()
def model_disco(self):
"""Model in PyDiscovery. To be implemented."""
pass
[docs]
@pyaedt_function_handler()
def setup_disco(self):
"""Set up model in PyDiscovery. To be implemented."""
pass
[docs]
class QuadrifilarOpen(CommonHelix):
"""Manages an open quadrifilar helix antenna.
Notes
-----
.. [1] C. Balanis, "Wideband and Travelling-Wave Antennas,"
*Modern Antenna Handbook*, New York, 2008.
"""
_default_input_parameters = {
"name": "",
"origin": [0, 0, 0],
"length_unit": "mm",
"coordinate_system": "Global",
"frequency": 1.0,
"frequency_unit": "GHz",
"gain": 10,
"direction": 0,
"feeder_length": 0.0,
"outer_boundary": "",
"material": "pec",
}
def __init__(self, *args, **kwargs):
CommonHelix.__init__(self, self._default_input_parameters, *args, **kwargs)
self._parameters = self.synthesis()
self.update_synthesis_parameters(self._parameters)
self.antenna_type = "QuadrifilarOpen"
[docs]
@pyaedt_function_handler()
def synthesis(self):
parameters = {}
freq_ghz = constants.unit_converter(self.frequency, "Freq", self.frequency_unit, "GHz")
parameters["groundx"] = constants.unit_converter(60 * (1 / freq_ghz), "Length", "mm", self.length_unit)
parameters["groundy"] = constants.unit_converter(60 * (1 / freq_ghz), "Length", "mm", self.length_unit)
parameters["diameter"] = constants.unit_converter(43.2 * (1 / freq_ghz), "Length", "mm", self.length_unit)
parameters["spacing"] = constants.unit_converter(139 * (1 / freq_ghz), "Length", "mm", self.length_unit)
parameters["wire_diameter"] = constants.unit_converter(1.6 * (1 / freq_ghz), "Length", "mm", self.length_unit)
parameters["port_height"] = constants.unit_converter(3.2 * (1 / freq_ghz), "Length", "mm", self.length_unit)
parameters["number_of_turns"] = 1.1
parameters["pos_x"] = self.origin[0]
parameters["pos_y"] = self.origin[1]
parameters["pos_z"] = self.origin[2]
return self._ordered_parameters(parameters)
[docs]
@pyaedt_function_handler()
def model_hfss(self):
return self._quadrifilar_model()
[docs]
@pyaedt_function_handler()
def model_disco(self):
"""Model in PyDiscovery. To be implemented."""
pass
[docs]
@pyaedt_function_handler()
def setup_disco(self):
"""Set up model in PyDiscovery. To be implemented."""
pass
[docs]
class QuadrifilarShort(CommonHelix):
"""Manages a shorted quadrifilar helix antenna.
Notes
-----
.. [1] C. Balanis, "Wideband and Travelling-Wave Antennas,"
*Modern Antenna Handbook*, New York, 2008.
"""
_default_input_parameters = {
"name": "",
"origin": [0, 0, 0],
"length_unit": "mm",
"coordinate_system": "Global",
"frequency": 1.0,
"frequency_unit": "GHz",
"gain": 10,
"direction": 0,
"feeder_length": 0.0,
"outer_boundary": "",
"material": "pec",
}
def __init__(self, *args, **kwargs):
CommonHelix.__init__(self, self._default_input_parameters, *args, **kwargs)
self._parameters = self.synthesis()
self.update_synthesis_parameters(self._parameters)
self.antenna_type = "QuadrifilarShort"
[docs]
@pyaedt_function_handler()
def synthesis(self):
parameters = {}
freq_ghz = constants.unit_converter(self.frequency, "Freq", self.frequency_unit, "GHz")
syn_resonant_freq = 0.9322
scaling = syn_resonant_freq / freq_ghz
parameters["groundx"] = constants.unit_converter(100 * scaling, "Length", "mm", self.length_unit)
parameters["groundy"] = constants.unit_converter(100 * scaling, "Length", "mm", self.length_unit)
parameters["diameter"] = constants.unit_converter(52.2 * scaling, "Length", "mm", self.length_unit)
parameters["spacing"] = constants.unit_converter(255 * scaling, "Length", "mm", self.length_unit)
parameters["wire_diameter"] = constants.unit_converter(15 * scaling, "Length", "mm", self.length_unit)
parameters["port_height"] = constants.unit_converter(3.2 * scaling, "Length", "mm", self.length_unit)
parameters["number_of_turns"] = 0.5
parameters["pos_x"] = self.origin[0]
parameters["pos_y"] = self.origin[1]
parameters["pos_z"] = self.origin[2]
return self._ordered_parameters(parameters)
[docs]
@pyaedt_function_handler()
def model_hfss(self):
return self._quadrifilar_model(shorted=True)
[docs]
@pyaedt_function_handler()
def model_disco(self):
"""Model in PyDiscovery. To be implemented."""
pass
[docs]
@pyaedt_function_handler()
@pyaedt_function_handler()
def setup_disco(self):
"""Set up model in PyDiscovery. To be implemented."""
pass