diff --git a/examples/legacy_standalone/10_GDS_workflow.py b/examples/legacy_standalone/10_GDS_workflow.py
index 13525ee49b..5ba2c5f900 100644
--- a/examples/legacy_standalone/10_GDS_workflow.py
+++ b/examples/legacy_standalone/10_GDS_workflow.py
@@ -54,7 +54,7 @@
#
# This code sets up a simulation with HFSS and adds a frequency sweep.
-setup = c.setups.add_setup("Setup1", "1GHz")
+setup = c.setups.add_setup("Setup1", "1GHz", 0.02, 10)
setup.add_sweep("Sweep1", "0.01GHz", "5GHz", "0.1GHz")
# ## Provide additional stackup settings
diff --git a/src/pyedb/dotnet/edb.py b/src/pyedb/dotnet/edb.py
index eb7b7ac4f4..53277d6a63 100644
--- a/src/pyedb/dotnet/edb.py
+++ b/src/pyedb/dotnet/edb.py
@@ -603,7 +603,7 @@ def create_edb(self):
def import_layout_pcb(
self,
input_file,
- working_dir,
+ working_dir="",
anstranslator_full_path="",
use_ppe=False,
control_file=None,
@@ -616,7 +616,7 @@ def import_layout_pcb(
----------
input_file : str
Full path to the board file.
- working_dir : str
+ working_dir : str, optional
Directory in which to create the ``aedb`` folder. The name given to the AEDB file
is the same as the name of the board file.
anstranslator_full_path : str, optional
@@ -1510,6 +1510,13 @@ def import_gds_file(
else:
return False
else:
+ if anstranslator_full_path and os.path.exists(anstranslator_full_path):
+ path = anstranslator_full_path
+ else:
+ path = os.path.join(self.base_path, "anstranslator")
+ if is_windows:
+ path += ".exe"
+
temp_map_file = os.path.splitext(inputGDS)[0] + ".map"
temp_layermap_file = os.path.splitext(inputGDS)[0] + ".layermap"
@@ -1529,10 +1536,10 @@ def import_gds_file(
else:
self.logger.error("Unable to define control file.")
- command = [anstranslator_full_path, inputGDS, f'-g="{map_file}"', f'-c="{control_file}"']
+ command = [path, inputGDS, f'-g="{map_file}"', f'-c="{control_file}"']
else:
command = [
- anstranslator_full_path,
+ path,
inputGDS,
f'-o="{control_file_temp}"' f'-t="{tech_file}"',
f'-g="{map_file}"',
diff --git a/src/pyedb/dotnet/edb_core/edb_data/control_file.py b/src/pyedb/dotnet/edb_core/edb_data/control_file.py
index 076b85f575..3d3e191aec 100644
--- a/src/pyedb/dotnet/edb_core/edb_data/control_file.py
+++ b/src/pyedb/dotnet/edb_core/edb_data/control_file.py
@@ -25,6 +25,7 @@
import re
import subprocess
import sys
+import xml
from pyedb.edb_logger import pyedb_logger
from pyedb.generic.general_methods import ET, env_path, env_value, is_linux
@@ -964,14 +965,14 @@ def _write_xml(self, root):
class ControlFileSetup:
"""Setup Class."""
- def __init__(self, name):
+ def __init__(self, name, adapt_freq="1GHz", maxdelta=0.02, maxpasses=10):
self.name = name
self.enabled = True
self.save_fields = False
self.save_rad_fields = False
- self.frequency = "1GHz"
- self.maxpasses = 10
- self.max_delta = 0.02
+ self.frequency = adapt_freq
+ self.maxpasses = maxpasses
+ self.max_delta = maxdelta
self.union_polygons = True
self.small_voids_area = 0
self.mode_type = "IC"
@@ -1082,22 +1083,25 @@ class ControlFileSetups:
def __init__(self):
self.setups = []
- def add_setup(self, name, frequency):
+ def add_setup(self, name, adapt_freq, maxdelta, maxpasses):
"""Add a new setup
Parameters
----------
name : str
- Setup name.
- frequency : str
+ Setup Name.
+ adapt_freq : str, optional
Setup Frequency.
+ maxdelta : float, optional
+ Maximum Delta.
+ maxpasses : int, optional
+ Maximum Number of Passes.
Returns
-------
:class:`pyedb.dotnet.edb_core.edb_data.control_file.ControlFileSetup`
"""
- setup = ControlFileSetup(name)
- setup.frequency = frequency
+ setup = ControlFileSetup(name, adapt_freq, maxdelta, maxpasses)
self.setups.append(setup)
return setup
@@ -1112,17 +1116,17 @@ class ControlFile:
def __init__(self, xml_input=None, tecnhology=None, layer_map=None):
self.stackup = ControlFileStackup()
+ self.boundaries = ControlFileBoundaries()
+ self.setups = ControlFileSetups()
if xml_input:
self.parse_xml(xml_input)
if tecnhology:
self.parse_technology(tecnhology)
if layer_map:
self.parse_layer_map(layer_map)
- self.boundaries = ControlFileBoundaries()
self.remove_holes = False
self.remove_holes_area_minimum = 30
self.remove_holes_units = "um"
- self.setups = ControlFileSetups()
self.components = ControlFileComponents()
self.import_options = ControlFileImportOptions()
pass
@@ -1262,6 +1266,50 @@ def parse_xml(self, xml_input):
via.remove_unconnected = (
True if i.attrib["RemoveUnconnected"] == "true" else False
)
+ if el.tag == "Boundaries":
+ for port_el in el:
+ if port_el.tag == "CircuitPortPt":
+ self.boundaries.add_port(
+ name=port_el.attrib["Name"],
+ x1=port_el.attrib["x1"],
+ y1=port_el.attrib["y1"],
+ layer1=port_el.attrib["Layer1"],
+ x2=port_el.attrib["x2"],
+ y2=port_el.attrib["y2"],
+ layer2=port_el.attrib["Layer2"],
+ z0=port_el.attrib["Z0"],
+ )
+ setups = root.find("SimulationSetups")
+ if setups:
+ hfsssetup = setups.find("HFSSSetup")
+ if hfsssetup:
+ if "Name" in hfsssetup.attrib:
+ name = hfsssetup.attrib["Name"]
+ hfsssimset = hfsssetup.find("HFSSSimulationSettings")
+ if hfsssimset:
+ hfssadaptset = hfsssimset.find("HFSSAdaptiveSettings")
+ if hfssadaptset:
+ adaptset = hfssadaptset.find("AdaptiveSettings")
+ if adaptset:
+ singlefreqdatalist = adaptset.find("SingleFrequencyDataList")
+ if singlefreqdatalist:
+ adaptfreqdata = singlefreqdatalist.find("AdaptiveFrequencyData")
+ if adaptfreqdata:
+ if isinstance(
+ adaptfreqdata.find("AdaptiveFrequency"), xml.etree.ElementTree.Element
+ ):
+ adapt_freq = adaptfreqdata.find("AdaptiveFrequency").text
+ else:
+ adapt_freq = "1GHz"
+ if isinstance(adaptfreqdata.find("MaxDelta"), xml.etree.ElementTree.Element):
+ maxdelta = adaptfreqdata.find("MaxDelta").text
+ else:
+ maxdelta = 0.02
+ if isinstance(adaptfreqdata.find("MaxPasses"), xml.etree.ElementTree.Element):
+ maxpasses = adaptfreqdata.find("MaxPasses").text
+ else:
+ maxpasses = 10
+ self.setups.add_setup(name, adapt_freq, maxdelta, maxpasses)
return True
def write_xml(self, xml_output):
@@ -1278,8 +1326,7 @@ def write_xml(self, xml_output):
"""
control = ET.Element("{http://www.ansys.com/control}Control", attrib={"schemaVersion": "1.0"})
self.stackup._write_xml(control)
- if self.boundaries.ports or self.boundaries.extents:
- self.boundaries._write_xml(control)
+ self.boundaries._write_xml(control)
if self.remove_holes:
hole = ET.SubElement(control, "RemoveHoles")
hole.set("HoleAreaMinimum", str(self.remove_holes_area_minimum))
diff --git a/tests/example_models/cad/GDS/sky130_fictitious_dtc_example_control_no_map.xml b/tests/example_models/cad/GDS/sky130_fictitious_dtc_example_control_no_map.xml
index a27a8562d5..51abc4db35 100644
--- a/tests/example_models/cad/GDS/sky130_fictitious_dtc_example_control_no_map.xml
+++ b/tests/example_models/cad/GDS/sky130_fictitious_dtc_example_control_no_map.xml
@@ -106,4 +106,25 @@
+
+
+
+
+
+
+
+
+
+
+
+ 0.5GHz
+ 0.01
+ 10
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/legacy/system/test_edb.py b/tests/legacy/system/test_edb.py
index 211440484d..8c604cdf7c 100644
--- a/tests/legacy/system/test_edb.py
+++ b/tests/legacy/system/test_edb.py
@@ -1341,7 +1341,7 @@ def test_import_gds_from_tech(self):
self.local_scratch.copyfile(gds_in, gds_out)
c = ControlFile(c_file_in, layer_map=c_map)
- setup = c.setups.add_setup("Setup1", "1GHz")
+ setup = c.setups.add_setup("Setup1", "1GHz", 0.02, 10)
setup.add_sweep("Sweep1", "0.01GHz", "5GHz", "0.1GHz")
c.boundaries.units = "um"
c.stackup.units = "um"
@@ -1364,8 +1364,8 @@ def test_import_gds_from_tech(self):
)
assert edb
- assert "P1" in edb.excitations
- assert "Setup1" in edb.setups
+ assert "P1" and "P2" in edb.excitations
+ assert "Setup1" and "Setup Test" in edb.setups
assert "B1" in edb.components.instances
edb.close()
@@ -1495,7 +1495,7 @@ def test_add_layer_api_with_control_file(self):
)
assert ctrl.boundaries.ports
# setup using q3D for DC point
- setup = ctrl.setups.add_setup("test_setup", "10GHz")
+ setup = ctrl.setups.add_setup("test_setup", "10GHz", 0.02, 10)
assert setup
setup.add_sweep(
name="test_sweep",