Skip to content

Commit a8503d2

Browse files
Expose Maxwell Circuit Editor (#789)
* Add MaxwellCircuit Class * Add AnalysisMaxwellCircuit class * Add Modeler for MaxwellCircuit * Add Primitives class for MaxwellCircuit * Add Unit tests for Maxwell Circuit * Fix up issues with insert design * Fix up unit tests * Fix flake8 errors * fix up black style issues * Apply suggestions from code review Co-authored-by: Maxime Rey <[email protected]> * Update Documentation comments Co-authored-by: Maxime Rey <[email protected]>
1 parent 4e2a978 commit a8503d2

File tree

12 files changed

+585
-5
lines changed

12 files changed

+585
-5
lines changed

_unittest/test_35_MaxwellCircuit.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Setup paths for module imports
2+
from _unittest.conftest import scratch_path
3+
4+
# Import required modules
5+
from pyaedt import MaxwellCircuit
6+
from pyaedt.generic.filesystem import Scratch
7+
import gc
8+
9+
10+
class TestClass:
11+
def setup_class(self):
12+
project_name = "MaxwellCircuitProject"
13+
design_name = "MaxwellCircuitDesign1"
14+
# set a scratch directory and the environment / test data
15+
with Scratch(scratch_path) as self.local_scratch:
16+
self.aedtapp = MaxwellCircuit(project_name, design_name)
17+
18+
def teardown_class(self):
19+
self.aedtapp._desktop.ClearMessages("", "", 3)
20+
assert self.aedtapp.close_project(self.aedtapp.project_name, saveproject=False)
21+
self.local_scratch.remove()
22+
gc.collect()
23+
24+
def test_01_create_resistor(self):
25+
id = self.aedtapp.modeler.schematic.create_resistor("Resistor1", 10, [0, 0])
26+
assert id.parameters["R"] == "10"
27+
28+
def test_02_create_inductor(self):
29+
id = self.aedtapp.modeler.schematic.create_inductor("Inductor1", 1.5, [0.25, 0])
30+
assert id.parameters["L"] == "1.5"
31+
32+
def test_03_create_capacitor(self):
33+
id = self.aedtapp.modeler.schematic.create_capacitor("Capacitor1", 7.5, [0.5, 0])
34+
assert id.parameters["C"] == "7.5"
35+
36+
def test_04_create_diode(self):
37+
assert self.aedtapp.modeler.schematic.create_diode("Diode1")
38+
39+
def test_05_create_winding(self):
40+
assert self.aedtapp.modeler.schematic.create_winding("mywinding")
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
from conf_unittest import test_generator, PytestMockup
2+
import os
3+
4+
if os.name != "posix":
5+
test_filter = "test_"
6+
7+
test_name = os.path.basename(__file__).replace(".py", "")
8+
mymodule = __import__("_unittest." + test_name, fromlist=["TestClass"])
9+
test_obj = mymodule.TestClass()
10+
11+
class TestSequenceFunctionsGenerate(PytestMockup):
12+
@classmethod
13+
def setUpClass(cls):
14+
test_obj.setup_class()
15+
16+
@classmethod
17+
def tearDownClass(cls):
18+
test_obj.teardown_class()
19+
20+
test_names = [name for name in dir(test_obj) if name.startswith(test_filter)]
21+
for test_name in test_names:
22+
test_fn = test_generator(test_obj, test_name)
23+
setattr(TestSequenceFunctionsGenerate, test_name, test_fn)

pyaedt/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
Edb,
3333
Maxwell3d,
3434
Maxwell2d,
35+
MaxwellCircuit,
3536
Mechanical,
3637
Rmxprt,
3738
TwinBuilder,

pyaedt/application/Analysis.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,7 @@ def __init__(
103103
close_on_exit,
104104
student_version,
105105
)
106-
self._ooptimetrics = self._odesign.GetModule("Optimetrics")
107-
self._ooutput_variable = self._odesign.GetModule("OutputVariable")
106+
108107
self.logger.info("Design Loaded")
109108
self._setup = None
110109
if setup_name:
@@ -115,13 +114,18 @@ def __init__(
115114
self._available_variations = self.AvailableVariations(self)
116115
if "HFSS 3D Layout Design" in self.design_type:
117116
self._oanalysis = self._odesign.GetModule("SolveSetups")
118-
elif "EMIT" in self.design_type:
117+
elif "EMIT" in self.design_type or "Maxwell Circuit" in self.design_type:
119118
self._oanalysis = None
120119
elif "Circuit Design" in self.design_type or "Twin Builder" in self.design_type:
121120
self._oanalysis = self._odesign.GetModule("SimSetup")
122121
else:
123122
self._oanalysis = self._odesign.GetModule("AnalysisSetup")
124-
self.setups = [self.get_setup(setup_name) for setup_name in self.setup_names]
123+
124+
if self.design_type != "Maxwell Circuit":
125+
self._ooptimetrics = self._odesign.GetModule("Optimetrics")
126+
self._ooutput_variable = self._odesign.GetModule("OutputVariable")
127+
self.setups = [self.get_setup(setup_name) for setup_name in self.setup_names]
128+
125129
self.opti_parametric = ParametericsSetups(self)
126130
self.opti_optimization = OptimizationSetups(self)
127131
self.opti_doe = DOESetups(self)
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
from pyaedt.modeler.Circuit import ModelerMaxwellCircuit
2+
from pyaedt.application.Analysis import Analysis
3+
4+
5+
class AnalysisMaxwellCircuit(Analysis):
6+
"""Class for Maxwell Circuit (MaxwellCircuit)
7+
8+
Maxwell Circuit Editor has no setup, solution, analysis or postprocessor
9+
It is automatically initialized by Application call (Maxwell Circuit).
10+
Refer to Application function for inputs definition
11+
12+
Parameters
13+
----------
14+
projectname : str, optional
15+
Name of the project to select or the full path to the project
16+
or AEDTZ archive to open. The default is ``None``, in which
17+
case an attempt is made to get an active project. If no
18+
projects are present, an empty project is created.
19+
designname : str, optional
20+
Name of the design to select. The default is ``None``, in
21+
which case an attempt is made to get an active design. If no
22+
designs are present, an empty design is created.
23+
specified_version : str, optional
24+
Version of AEDT to use. The default is ``None``. If ``None``,
25+
the active setup is used or the latest installed version is
26+
used.
27+
NG : bool, optional
28+
Whether to launch AEDT in the non-graphical mode. The default
29+
is ``False``, in which case AEDT is launched in the graphical mode.
30+
new_desktop_session : bool, optional
31+
Whether to launch an instance of AEDT in a new thread, even if
32+
another instance of the ``specified_version`` is active on the
33+
machine. The default is ``True``.
34+
close_on_exit : bool, optional
35+
Whether to release AEDT on exit. The default is ``True``.
36+
student_version : bool, optional
37+
Whether open AEDT Student Version. The default is ``False``.
38+
39+
"""
40+
41+
def __init__(
42+
self,
43+
application,
44+
projectname,
45+
designname,
46+
specified_version=None,
47+
non_graphical=False,
48+
new_desktop_session=False,
49+
close_on_exit=False,
50+
student_version=False,
51+
):
52+
53+
Analysis.__init__(
54+
self,
55+
application,
56+
projectname,
57+
designname,
58+
None,
59+
None,
60+
specified_version,
61+
non_graphical,
62+
new_desktop_session,
63+
close_on_exit,
64+
student_version,
65+
)
66+
self.solution_type = None
67+
self._modeler = ModelerMaxwellCircuit(self)
68+
69+
@property
70+
def modeler(self):
71+
"""Design oModeler."""
72+
return self._modeler

pyaedt/application/Design.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -606,7 +606,7 @@ def design_type(self):
606606
607607
Options are ``"Circuit Design"``, ``"Emit"``, ``"HFSS"``,
608608
``"HFSS 3D Layout Design"``, ``"Icepak"``, ``"Maxwell 2D"``,
609-
``"Maxwell 3D"``, ``"Mechanical"``, ``"ModelCreation"``,
609+
``"Maxwell 3D"``, ``"Maxwell Circuit"``, ``"Mechanical"``, ``"ModelCreation"``,
610610
``"Q2D Extractor"``, ``"Q3D Extractor"``, ``"RMxprtSolution"``,
611611
and ``"Twin Builder"``.
612612

pyaedt/application/design_solutions.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"Maxwell 3D": "Magnetostatic",
77
"Twin Builder": "TR",
88
"Circuit Design": "NexximLNA",
9+
"Maxwell Circuit": "",
910
"2D Extractor": "Open",
1011
"Q3D Extractor": "Q3D Extractor",
1112
"HFSS": "HFSS Modal Network",
@@ -433,13 +434,16 @@
433434
"EMIT": {
434435
"EMIT": {"name": None, "options": None, "report_type": None, "default_setup": None, "default_adaptive": None}
435436
},
437+
# Maxwell Circuit has no solution type
438+
"Maxwell Circuit": {},
436439
}
437440

438441
model_names = {
439442
"Maxwell 2D": "Maxwell2DModel",
440443
"Maxwell 3D": "Maxwell3DModel",
441444
"Twin Builder": "SimplorerCircuit",
442445
"Circuit Design": "NexximCircuit",
446+
"Maxwell Circuit": "MaxCirCircuit",
443447
"2D Extractor": "2DExtractorModel",
444448
"Q3D Extractor": "Q3DModel",
445449
"HFSS": "HFSSModel",

pyaedt/generic/design_types.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from pyaedt.rmxprt import Rmxprt
1414
from pyaedt.twinbuilder import TwinBuilder
1515
from pyaedt.twinbuilder import TwinBuilder as Simplorer # noqa: F401 # namespace only for backward compatibility
16+
from pyaedt.maxwellcircuit import MaxwellCircuit
1617
from pyaedt.emit import Emit
1718
from pyaedt.desktop import Desktop
1819
except ImportError:
@@ -28,13 +29,15 @@
2829
from pyaedt.rmxprt import Rmxprt
2930
from pyaedt.twinbuilder import TwinBuilder
3031
from pyaedt.twinbuilder import TwinBuilder as Simplorer # noqa: F401 # namespace only for backward compatibility
32+
from pyaedt.maxwellcircuit import MaxwellCircuit
3133
from pyaedt.emit import Emit
3234
from pyaedt.desktop import Desktop
3335

3436

3537
app_map = {
3638
"Maxwell 2D": Maxwell2d,
3739
"Maxwell 3D": Maxwell3d,
40+
"Maxwell Circuit": MaxwellCircuit,
3841
"Twin Builder": TwinBuilder,
3942
"Circuit Design": Circuit,
4043
"2D Extractor": Q2d,

pyaedt/maxwellcircuit.py

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
"""This module contains the `MaxwellCircuit` class."""
2+
3+
from __future__ import absolute_import
4+
import math
5+
6+
from pyaedt.application.AnalysisMaxwellCircuit import AnalysisMaxwellCircuit
7+
from pyaedt.generic.general_methods import aedt_exception_handler
8+
9+
10+
class MaxwellCircuit(AnalysisMaxwellCircuit, object):
11+
"""Provides the Maxwell Circuit Editor application interface.
12+
13+
Parameters
14+
----------
15+
projectname : str, optional
16+
Name of the project to select or the full path to the project
17+
or AEDTZ archive to open. The default is ``None``, in which
18+
case an attempt is made to get an active project. If no
19+
projects are present, an empty project is created.
20+
designname : str, optional
21+
Name of the design to select. The default is ``None``, in
22+
which case an attempt is made to get an active design. If no
23+
designs are present, an empty design is created.
24+
specified_version : str, optional
25+
Version of AEDT to use. The default is ``None``. If ``None``,
26+
the active setup is used or the latest installed version is
27+
used.
28+
NG : bool, optional
29+
Whether to launch AEDT in the non-graphical mode. The default
30+
is ``False``, in which case AEDT is launched in the graphical mode.
31+
new_desktop_session : bool, optional
32+
Whether to launch an instance of AEDT in a new thread, even if
33+
another instance of the ``specified_version`` is active on the
34+
machine. The default is ``True``.
35+
close_on_exit : bool, optional
36+
Whether to release AEDT on exit. The default is ``True``.
37+
student_version : bool, optional
38+
Whether open AEDT Student Version. The default is ``False``.
39+
40+
Examples
41+
--------
42+
Create an instance of Maxwell Circuit Editor and connect to an existing
43+
Maxwell ckt design or create a new Maxwell ckt design if one does not
44+
exist.
45+
46+
>>> from pyaedt import MaxwellCircuit
47+
>>> app = MaxwellCircuit()
48+
49+
Create an instance of Maxwell Circuit and link to a project named
50+
``"projectname"``. If this project does not exist, create one with
51+
this name.
52+
53+
>>> app = MaxwellCircuit(projectname)
54+
55+
Create an instance of Maxwell Circuit and link to a design named
56+
``"designname"`` in a project named ``"projectname"``.
57+
58+
>>> app = MaxwellCircuit(projectname, designame)
59+
60+
Create an instance of Maxwell Circuit and open the specified
61+
project, which is named ``"myfile.aedt"``.
62+
63+
>>> app = MaxwellCircuit("myfile.aedt")
64+
"""
65+
66+
def __init__(
67+
self,
68+
projectname=None,
69+
designname=None,
70+
specified_version=None,
71+
non_graphical=False,
72+
new_desktop_session=False,
73+
close_on_exit=False,
74+
student_version=False,
75+
):
76+
"""Constructor."""
77+
AnalysisMaxwellCircuit.__init__(
78+
self,
79+
"Maxwell Circuit",
80+
projectname,
81+
designname,
82+
specified_version,
83+
non_graphical,
84+
new_desktop_session,
85+
close_on_exit,
86+
student_version,
87+
)
88+
89+
@aedt_exception_handler
90+
def create_schematic_from_netlist(self, file_to_import):
91+
"""Create a circuit schematic from an HSpice net list.
92+
93+
Supported currently are:
94+
95+
* R
96+
* L
97+
* C
98+
* Diodes
99+
100+
Parameters
101+
----------
102+
file_to_import : str
103+
Full path to the HSpice file.
104+
105+
Returns
106+
-------
107+
bool
108+
``True`` when successful, ``False`` when failed.
109+
110+
"""
111+
xpos = 0
112+
ypos = 0
113+
delta = 0.0508
114+
use_instance = True
115+
with open(file_to_import, "r") as f:
116+
for line in f:
117+
mycomp = None
118+
fields = line.split(" ")
119+
name = fields[0]
120+
if fields[0][0] == "R":
121+
value = fields[3][fields[3].find("=") + 1 :].strip()
122+
mycomp = self.modeler.schematic.create_resistor(
123+
name, value, [xpos, ypos], use_instance_id_netlist=use_instance
124+
)
125+
elif fields[0][0] == "L":
126+
value = fields[3][fields[3].find("=") + 1 :].strip()
127+
mycomp = self.modeler.schematic.create_inductor(
128+
name, value, [xpos, ypos], use_instance_id_netlist=use_instance
129+
)
130+
elif fields[0][0] == "C":
131+
value = fields[3][fields[3].find("=") + 1 :].strip()
132+
mycomp = self.modeler.schematic.create_capacitor(
133+
name, value, [xpos, ypos], use_instance_id_netlist=use_instance
134+
)
135+
elif fields[0][0] == "D":
136+
value = fields[3][fields[3].find("=") + 1 :].strip()
137+
mycomp = self.modeler.schematic.create_diode(
138+
name, value, [xpos, ypos], use_instance_id_netlist=use_instance
139+
)
140+
if mycomp:
141+
id = 1
142+
for pin in mycomp.pins:
143+
if pin.name == "CH" or pin.name == fields[0][0]:
144+
continue
145+
pos = pin.location
146+
if pos[0] < xpos:
147+
angle = 0.0
148+
else:
149+
angle = math.pi
150+
self.modeler.schematic.create_page_port(fields[id], [pos[0], pos[1]], angle)
151+
id += 1
152+
ypos += delta
153+
if ypos > 0.254:
154+
xpos += delta
155+
ypos = 0
156+
return True
157+
158+
def __enter__(self):
159+
return self

0 commit comments

Comments
 (0)