Skip to content

Commit eac02f8

Browse files
maxcapodi78SMoraisAnsyspre-commit-ci[bot]hui-zhou-a
authored
Added ERL computation in Circuit through SPISIM launched in non-graphical mode (#4219)
* Added ERL computation in Circuit through SPISIM launched in non-graphical mode * Added ERL computation in Circuit through SPISIM launched in non-graphical mode * Added ERL computation in Circuit through SPISIM launched in non-graphical mode * Update pyaedt/circuit.py Co-authored-by: SMoraisAnsys <[email protected]> * Update pyaedt/circuit.py Co-authored-by: SMoraisAnsys <[email protected]> * Update pyaedt/circuit.py Co-authored-by: SMoraisAnsys <[email protected]> * minor fix * minor fix * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * minor fix --------- Co-authored-by: maxcapodi78 <Shark78> Co-authored-by: SMoraisAnsys <[email protected]> Co-authored-by: ring630 <@gmail.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Hui Zhou <[email protected]>
1 parent 3460217 commit eac02f8

File tree

4 files changed

+249
-5
lines changed

4 files changed

+249
-5
lines changed
4.26 MB
Binary file not shown.

Diff for: _unittest_solvers/test_00_analyze.py

+14
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
component = "Circ_Patch_5GHz.a3dcomp"
2727

2828
test_subfolder = "T00"
29+
erl_project_name = "erl_unit_test"
2930

3031

3132
@pytest.fixture()
@@ -74,6 +75,12 @@ def circuit_app(add_app):
7475
app.modeler.schematic_units = "mil"
7576
return app
7677

78+
@pytest.fixture(scope="class")
79+
def circuit_erl(add_app):
80+
app = add_app(erl_project_name, design_name="2ports", application=Circuit, subfolder=test_subfolder)
81+
return app
82+
83+
7784

7885
@pytest.fixture(scope="class")
7986
def m3dtransient(add_app):
@@ -426,3 +433,10 @@ def test_07_export_maxwell_fields(self, m3dtransient):
426433
setuptype=setup.setuptype)
427434
new_setup.props = setup.props
428435
new_setup.update()
436+
437+
def test_08_compute_erl(self, circuit_erl):
438+
erl_data2 = circuit_erl.compute_erl(port_order="EvenOdd",bandwidth="40p")
439+
assert erl_data2
440+
circuit_erl.set_active_design("4_ports")
441+
erl_data_3 = circuit_erl.compute_erl(specify_through_ports=[1, 2, 3, 4])
442+
assert erl_data_3

Diff for: pyaedt/application/Analysis.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -2088,8 +2088,8 @@ def _export_touchstone(
20882088
20892089
Returns
20902090
-------
2091-
bool
2092-
``True`` when successful, ``False`` when failed.
2091+
str
2092+
file name when successful, ``False`` when failed.
20932093
"""
20942094
if variations is None:
20952095
variations = list(self.available_variations.nominal_w_values_dict.keys())
@@ -2180,7 +2180,7 @@ def _export_touchstone(
21802180
NonStandardExtensions,
21812181
)
21822182
self.logger.info("Touchstone correctly exported to %s", filename)
2183-
return True
2183+
return OutFile
21842184

21852185
@pyaedt_function_handler()
21862186
def value_with_units(

Diff for: pyaedt/circuit.py

+232-2
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,16 @@
88
import os
99
import re
1010
import shutil
11+
import subprocess # nosec B404
1112

1213
from pyaedt import Hfss3dLayout
14+
from pyaedt import settings
1315
from pyaedt.application.AnalysisNexxim import FieldAnalysisCircuit
1416
from pyaedt.generic import ibis_reader
1517
from pyaedt.generic.DataHandlers import from_rkm_to_aedt
1618
from pyaedt.generic.filesystem import search_files
1719
from pyaedt.generic.general_methods import generate_unique_name
20+
from pyaedt.generic.general_methods import is_linux
1821
from pyaedt.generic.general_methods import open_file
1922
from pyaedt.generic.general_methods import pyaedt_function_handler
2023
from pyaedt.modules.Boundary import CurrentSinSource
@@ -828,8 +831,8 @@ def export_touchstone(
828831
829832
Returns
830833
-------
831-
bool
832-
``True`` when successful, ``False`` when failed.
834+
str
835+
File name when successful, ``False`` when failed.
833836
834837
References
835838
----------
@@ -1684,3 +1687,230 @@ def import_edb_in_circuit(self, edb_path):
16841687
hfss_3d_layout_model = self.modeler.schematic.add_subcircuit_3dlayout(hfss.design_name)
16851688
hfss.close_project(save_project=False)
16861689
return hfss_3d_layout_model
1690+
1691+
@pyaedt_function_handler()
1692+
def compute_erl_from_touchstone(
1693+
self,
1694+
touchstone_file,
1695+
port_order="EvenOdd",
1696+
specify_through_ports=None,
1697+
bandwidth=30e9,
1698+
tdr_duration=5,
1699+
z_terminations=50,
1700+
transition_time="10p",
1701+
fixture_delay=500e-12,
1702+
input_amplitude=1.0,
1703+
ber=1e-4,
1704+
pdf_bin_size=1e-5,
1705+
signal_loss_factor=1.7e9,
1706+
permitted_reflection=0.18,
1707+
reflections_lenght=1000,
1708+
modulation_type="NRZ",
1709+
):
1710+
"""Compute effective return loss (erl) using Ansys SPISIM from a S-parameter file.
1711+
1712+
Parameters
1713+
----------
1714+
touchstone_file : str
1715+
Path to the touchstone file.
1716+
port_order : str, optional
1717+
Whether to use "``EvenOdd``" or "``Incremental``" numbering for ``s4p`` files.
1718+
Ignored if the ports are greater than 4.
1719+
specify_through_ports : list, optional
1720+
Input and Output ports on which compute the erl. Those are ordered like ``[inp, inneg, outp, outneg]``.
1721+
bandwidth : float, str, optional
1722+
Application bandwidth: inverse of one UI (unit interval). Can be a float or str with unit ("m", "g").
1723+
Default is ``30e9``.
1724+
tdr_duration : float, optional
1725+
TDR duration (in second): How long the TDR tailed data should be applied. Default is ``5``.
1726+
z_terminations : float, optional
1727+
Z-Terminations: termination (Z11 and Z22) when TDR is calculated. Default is ``50``.
1728+
transition_time : float, str, optional
1729+
Transition time: how fast (slew rate) input pulse transit from 0 to Vcc volt. Default is "``10p``".
1730+
fixture_delay : float, optional
1731+
Fixture delay: delay when input starts transition from 0 to Vcc. Default is ``500e-12``.
1732+
input_amplitude : float, optional
1733+
Input amplitude: Vcc volt of step input. Default is ``1.0``.
1734+
ber : float, optional
1735+
Specified BER: At what threshold ERL is calculated. Default is ``1e-4``.
1736+
pdf_bin_size : float, optional
1737+
PDF bin size: how to quantize the superimposed value. Default is ``1e-5``.
1738+
signal_loss_factor : float, optional
1739+
Signal loss factor (Beta). See SPISIM Help for info. Default is ``1.7e9``.
1740+
permitted_reflection : float, optional
1741+
Permitted reflection (Rho). See SPISIM Help for info. Default is ``0.18``.
1742+
reflections_lenght : float, optional
1743+
Length of the reflections: how many UI will be used to calculate ERL. Default is ``1000``.
1744+
modulation_type : str, optional
1745+
Modulations type: signal modulation type "``NRZ``" or "``PAM4``". Default is "``NRZ``".
1746+
1747+
Returns
1748+
-------
1749+
bool or dict
1750+
A dictionary with the result from the spisimExe command, ``False`` when failed.
1751+
"""
1752+
exec_name = "SPISimJNI_LX64.exe" if is_linux else "SPISimJNI_WIN64.exe"
1753+
spisimExe = os.path.join(self.desktop_install_dir, "spisim", "SPISim", "modules", "ext", exec_name)
1754+
1755+
cfg_file = os.path.join(self.working_directory, "spisim_erl.cfg")
1756+
with open(cfg_file, "w") as fp:
1757+
fp.write("# INPARRY: INPARRY\n")
1758+
fp.write("INPARRY = {}\n".format(touchstone_file))
1759+
fp.write("# MIXMODE: MIXMODE\n")
1760+
if port_order and touchstone_file.lower().endswith(".s4p"):
1761+
fp.write("MIXMODE = {}\n".format(port_order))
1762+
else:
1763+
fp.write("MIXMODE = \n")
1764+
fp.write("# THRUS4P: THRUS4P\n")
1765+
if not touchstone_file.lower().endswith(".s4p"):
1766+
if isinstance(specify_through_ports[0], int):
1767+
thrus4p = ",".join([str(i) for i in specify_through_ports])
1768+
else:
1769+
try:
1770+
ports = self.excitations.keys()
1771+
thrus4p = ",".join([ports.index(i) for i in specify_through_ports])
1772+
except IndexError:
1773+
self.logger.error("Port not found.")
1774+
return False
1775+
fp.write("THRUS4P = {}\n".format(thrus4p))
1776+
else:
1777+
fp.write("THRUS4P = \n")
1778+
fp.write("# BANDWID: BANDWID\n")
1779+
fp.write("BANDWID = {}\n".format(bandwidth))
1780+
fp.write("# TDR_DUR: TDR_DUR\n")
1781+
fp.write("TDR_DUR = {}\n".format(tdr_duration))
1782+
fp.write("# REFIMPD: REFIMPD\n")
1783+
fp.write("REFIMPD = {}\n".format(z_terminations))
1784+
fp.write("# BINSIZE: BINSIZE\n")
1785+
fp.write("BINSIZE = {}\n".format(pdf_bin_size))
1786+
fp.write("# SPECBER: SPECBER\n")
1787+
fp.write("SPECBER = {}\n".format(ber))
1788+
fp.write("# MODTYPE: MODTYPE\n")
1789+
fp.write("MODTYPE = {}\n".format(modulation_type))
1790+
fp.write("# FIXDELY: FIXDELY\n")
1791+
fp.write("FIXDELY = {}\n".format(fixture_delay))
1792+
fp.write("# INPVOLT: INPVOLT\n")
1793+
fp.write("INPVOLT = {}\n".format(input_amplitude))
1794+
fp.write("# TRSTIME: TRSTIME\n")
1795+
fp.write("TRSTIME = {}\n".format(transition_time))
1796+
fp.write("# SIGBETA: SIGBETA\n")
1797+
fp.write("SIGBETA = {}\n".format(signal_loss_factor))
1798+
fp.write("# REFLRHO: REFLRHO\n")
1799+
fp.write("REFLRHO = {}\n".format(permitted_reflection))
1800+
fp.write("# NCYCLES: NCYCLES\n")
1801+
fp.write("NCYCLES = {}\n".format(reflections_lenght))
1802+
1803+
cfgCmmd = '-i %s -v CFGFILE="%s"' % (touchstone_file, cfg_file)
1804+
command = [spisimExe, "CalcERL", cfgCmmd]
1805+
# Debug('%s %s' % (cmdList[0], ' '.join(arguments)))
1806+
# try up to three times to be sure
1807+
out_processing = os.path.join(self.working_directory, "spsim_erl_out.txt")
1808+
my_env = os.environ.copy()
1809+
my_env.update(settings.aedt_environment_variables)
1810+
if is_linux: # pragma: no cover
1811+
command.append("&")
1812+
with open(out_processing, "w") as outfile:
1813+
subprocess.Popen(command, env=my_env, stdout=outfile, stderr=outfile).wait() # nosec
1814+
else:
1815+
with open(out_processing, "w") as outfile:
1816+
subprocess.Popen(" ".join(command), env=my_env, stdout=outfile, stderr=outfile).wait() # nosec
1817+
out_data = {}
1818+
try:
1819+
with open(out_processing, "r") as infile:
1820+
lines = infile.read()
1821+
parmDat = lines.split("[ParmDat]:", 1)[1]
1822+
for keyValu in parmDat.split(","):
1823+
dataAry = keyValu.split("=")
1824+
out_data[dataAry[0].strip()] = float(dataAry[1].strip().split()[0])
1825+
return out_data
1826+
except IndexError:
1827+
self.logger.error("Failed to compute ERL. Check input parameters and retry")
1828+
return False
1829+
1830+
@pyaedt_function_handler()
1831+
def compute_erl(
1832+
self,
1833+
setup_name=None,
1834+
port_order="EvenOdd",
1835+
specify_through_ports=None,
1836+
bandwidth=30e9,
1837+
tdr_duration=5,
1838+
z_terminations=50,
1839+
transition_time="10p",
1840+
fixture_delay=500e-12,
1841+
input_amplitude=1.0,
1842+
ber=1e-4,
1843+
pdf_bin_size=1e-5,
1844+
signal_loss_factor=1.7e9,
1845+
permitted_reflection=0.18,
1846+
reflections_lenght=1000,
1847+
modulation_type="NRZ",
1848+
):
1849+
"""Compute effective return loss (erl) using Ansys SPISIM.
1850+
1851+
Parameters
1852+
----------
1853+
setup_name : str, optional
1854+
Name of the setup to use as the nominal. The default is
1855+
``None``, in which case the active setup is used or
1856+
nothing is used.
1857+
port_order : str, optional
1858+
Whether to use "``EvenOdd``" or "``Incremental``" numbering for ``s4p`` files.
1859+
Ignored if the ports are greater than 4.
1860+
specify_through_ports : list, optional
1861+
Input and Output ports on which compute the erl. Those are ordered like ``[inp, inneg, outp, outneg]``.
1862+
bandwidth : float, str, optional
1863+
Application bandwidth: inverse of one UI (unit interval). Can be a float or str with unit ("m", "g").
1864+
Default is ``30e9``.
1865+
tdr_duration : float, optional
1866+
TDR duration (in second): How long the TDR tailed data should be applied. Default is ``5``.
1867+
z_terminations : float, optional
1868+
Z-Terminations: termination (Z11 and Z22) when TDR is calculated. Default is ``50``.
1869+
transition_time : float, str, optional
1870+
Transition time: how fast (slew rate) input pulse transit from 0 to Vcc volt. Default is "``10p``".
1871+
fixture_delay : float, optional
1872+
Fixture delay: delay when input starts transition from 0 to Vcc. Default is ``500e-12``.
1873+
input_amplitude : float, optional
1874+
Input amplitude: Vcc volt of step input. Default is ``1.0``.
1875+
ber : float, optional
1876+
Specified BER: At what threshold ERL is calculated. Default is ``1e-4``.
1877+
pdf_bin_size : float, optional
1878+
PDF bin size: how to quantize the superimposed value. Default is ``1e-5``.
1879+
signal_loss_factor : float, optional
1880+
Signal loss factor (Beta). See SPISIM Help for info. Default is ``1.7e9``.
1881+
permitted_reflection : float, optional
1882+
Permitted reflection (Rho). See SPISIM Help for info. Default is ``0.18``.
1883+
reflections_lenght : float, optional
1884+
Length of the reflections: how many UI will be used to calculate ERL. Default is ``1000``.
1885+
modulation_type : str, optional
1886+
Modulations type: signal modulation type "``NRZ``" or "``PAM4``". Default is "``NRZ``".
1887+
1888+
Returns
1889+
-------
1890+
bool or dict
1891+
A dictionary with the result from the spisimExe command, ``False`` when failed.
1892+
"""
1893+
1894+
if not setup_name:
1895+
setup_name = self.nominal_sweep
1896+
tc_file = self.export_touchstone(
1897+
setup_name,
1898+
)
1899+
1900+
return self.compute_erl_from_touchstone(
1901+
tc_file,
1902+
port_order,
1903+
specify_through_ports,
1904+
bandwidth,
1905+
tdr_duration,
1906+
z_terminations,
1907+
transition_time,
1908+
fixture_delay,
1909+
input_amplitude,
1910+
ber,
1911+
pdf_bin_size,
1912+
signal_loss_factor,
1913+
permitted_reflection,
1914+
reflections_lenght,
1915+
modulation_type,
1916+
)

0 commit comments

Comments
 (0)