Skip to content

Commit 4f305b6

Browse files
committed
FEAT: circuit configuration
1 parent 74863ab commit 4f305b6

4 files changed

Lines changed: 245 additions & 3 deletions

File tree

src/ansys/aedt/core/application/analysis_nexxim.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import warnings
2525

2626
from ansys.aedt.core.application.analysis import Analysis
27+
from ansys.aedt.core.generic.configurations import ConfigurationsNexxim
2728
from ansys.aedt.core.generic.general_methods import pyaedt_function_handler
2829
from ansys.aedt.core.generic.settings import settings
2930
from ansys.aedt.core.modeler.circuits.object_3d_circuit import CircuitComponent
@@ -91,10 +92,21 @@ def __init__(
9192
self._post = None
9293
self._internal_excitations = None
9394
self._internal_sources = None
95+
self._configurations = ConfigurationsNexxim(self)
9496
if not settings.lazy_load:
9597
self._modeler = self.modeler
9698
self._post = self.post
9799

100+
@property
101+
def configurations(self):
102+
"""Property to import and export configuration files.
103+
104+
Returns
105+
-------
106+
:class:`ansys.aedt.core.generic.configurations.Configurations`
107+
"""
108+
return self._configurations
109+
98110
@pyaedt_function_handler(setupname="name")
99111
def delete_setup(self, name):
100112
"""Delete a setup.

src/ansys/aedt/core/generic/configurations.py

Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2323
# SOFTWARE.
2424

25+
from collections import defaultdict
2526
import copy
2627
from datetime import datetime
2728
import json
@@ -2166,3 +2167,222 @@ def apply_operations_to_native_components(obj, operation_dict, native_dict): #
21662167
native_dict["Instances"],
21672168
)
21682169
return True
2170+
2171+
2172+
class ConfigurationsNexxim(Configurations):
2173+
"""Enables export and import configuration options to be applied to a new or existing Nexxim design."""
2174+
2175+
@pyaedt_function_handler()
2176+
def export_config(self, config_file=None, overwrite=False):
2177+
"""Export current design properties to a JSON or TOML file.
2178+
2179+
Parameters
2180+
----------
2181+
config_file : str, optional
2182+
Full path to json file. If ``None``, then the config file will be saved in working directory.
2183+
overwrite : bool, optional
2184+
If ``True`` the json file will be overwritten if already existing.
2185+
If ``False`` and the version is compatible, the data in the existing file will be updated.
2186+
Default is ``False``.
2187+
2188+
Returns
2189+
-------
2190+
str
2191+
Exported config file.
2192+
"""
2193+
2194+
if not config_file:
2195+
config_file = os.path.join(
2196+
self._app.working_directory, generate_unique_name(self._app.design_name) + ".json"
2197+
)
2198+
# dict_out = {}
2199+
# self._export_general(dict_out)
2200+
pin_mapping = defaultdict(list)
2201+
data_refdes = {}
2202+
data_models = {}
2203+
pin_nets = {}
2204+
skip_list = [
2205+
"LabelID",
2206+
"ADD_NOISE",
2207+
"DTEMP",
2208+
"ModelName",
2209+
"CosimDefinition",
2210+
"CoSimulator",
2211+
"InstanceName",
2212+
"NexximNetlist",
2213+
"Name",
2214+
"COMPONENT",
2215+
"EyeMeasurementFunctions",
2216+
"ACMAG",
2217+
]
2218+
for comp in list(self._app.modeler.schematic.components.values()):
2219+
properties = {}
2220+
name = []
2221+
num_terminals = None
2222+
refdes = comp.refdes
2223+
position = comp.location
2224+
angle = comp.angle
2225+
parameters = comp.parameters
2226+
if not comp.component_info:
2227+
continue
2228+
else:
2229+
component = comp.component_info["Component"]
2230+
path = comp.component_path
2231+
if path == False:
2232+
type = "Nexxim Component"
2233+
path = ""
2234+
for param, value in parameters.items():
2235+
if param in skip_list:
2236+
continue
2237+
elif value and value[-1] == "'" and value[1] == "'":
2238+
value = value[-1:1]
2239+
properties[param] = value
2240+
elif path[-4:] == ".ibs":
2241+
if "AMI_Version" in parameters:
2242+
type = "ami"
2243+
else:
2244+
type = "ibis"
2245+
for prop, value in parameters.items():
2246+
if value and value[-1] == '"' and value[0] == '"':
2247+
value = value[1:-1]
2248+
properties[prop] = value
2249+
elif path[-4:] in [".LIB", ".lib"] or path[-3:] == ".sp":
2250+
type = "spice"
2251+
elif path[-1:] == "p" and path[-2:-1].isdigit():
2252+
type = "touchstone"
2253+
elif path[-4:] == ".sss":
2254+
type = "nexxim state space"
2255+
num_terminals = comp.model_data.props["numberofports"]
2256+
2257+
for pin in comp.pins:
2258+
if pin.net == "0":
2259+
net = "gnd"
2260+
else:
2261+
net = pin.net
2262+
temp_dict = {pin: net}
2263+
pin_nets.update(temp_dict)
2264+
2265+
temp_dict2 = {
2266+
refdes: {"component": component, "properties": properties, "position": position, "angle": angle}
2267+
}
2268+
data_refdes.update(temp_dict2)
2269+
if num_terminals:
2270+
model = {component: {"type": type, "file_path": path, "num_terminals": num_terminals}}
2271+
num_terminals = None
2272+
else:
2273+
model = {component: {"type": type, "file_path": path}}
2274+
data_models.update(model)
2275+
print(data_refdes)
2276+
2277+
for k, v in pin_nets.items():
2278+
pin_mapping[v].append(k)
2279+
2280+
if "" in pin_mapping:
2281+
del pin_mapping[""]
2282+
for k, l in pin_mapping.items():
2283+
temp_dict3 = {}
2284+
for i in l:
2285+
temp_dict3.update({i._circuit_comp.refdes: i.name})
2286+
pin_mapping[k] = temp_dict3
2287+
2288+
dict_out = {
2289+
"models": data_models,
2290+
"refdes": data_refdes,
2291+
"pin_mapping": pin_mapping,
2292+
} # Call private export method to update dict_out.
2293+
2294+
# update the json if it exists already
2295+
2296+
if os.path.exists(config_file) and not overwrite:
2297+
dict_in = read_configuration_file(config_file)
2298+
try: # TODO: Allow import of config created with other versions of pyaedt.
2299+
if dict_in["general"]["pyaedt_version"] == __version__:
2300+
for k, v in dict_in.items():
2301+
if k not in dict_out:
2302+
dict_out[k] = v
2303+
elif isinstance(v, dict):
2304+
for i, j in v.items():
2305+
if i not in dict_out[k]:
2306+
dict_out[k][i] = j
2307+
except KeyError as e:
2308+
self._app.logger.error(str(e))
2309+
2310+
# write the updated dict to file
2311+
if write_configuration_file(dict_out, config_file):
2312+
self._app.logger.info(f"Json file {config_file} created correctly.")
2313+
return config_file
2314+
self._app.logger.error(f"Error creating json file {config_file}.")
2315+
return False
2316+
2317+
@pyaedt_function_handler()
2318+
def import_config(self, config_file, *args):
2319+
"""Import configuration settings from a JSON or TOML file and apply it to the current design.
2320+
2321+
2322+
Parameters
2323+
----------
2324+
config_file : str
2325+
Full path to json file.
2326+
2327+
Returns
2328+
-------
2329+
dict, bool
2330+
Config dictionary.
2331+
"""
2332+
if len(args) > 0: # pragma: no cover
2333+
raise TypeError("import_config expected at most 1 arguments, got %d" % (len(args) + 1))
2334+
self.results._reset_results()
2335+
2336+
data = read_configuration_file(config_file)
2337+
for i, j in data["refdes"].items():
2338+
for k, l in data["models"].items():
2339+
if k == j["component"]:
2340+
type = l["type"]
2341+
if type == "Nexxim Component":
2342+
new_comp = self._app.modeler.components.create_component(
2343+
name=i,
2344+
component_library="",
2345+
component_name=j["component"],
2346+
location=j["position"],
2347+
angle=j["angle"],
2348+
)
2349+
elif type in ["ibis", "ami"]:
2350+
if type == "ami":
2351+
ami = True
2352+
else:
2353+
ami = False
2354+
comp_set = self._app.get_ibis_model_from_file(l["file_path"], ami).components.values()
2355+
for comp in comp_set:
2356+
for pin in comp.pins.values():
2357+
if pin.buffer_name == k:
2358+
new_comp = pin.insert(j["position"][0], j["position"][1])
2359+
elif type == "touchstone":
2360+
new_comp = self._app.modeler.schematic.create_touchstone_component(
2361+
l["file_path"], location=j["position"], angle=j["angle"]
2362+
)
2363+
elif type == "spice":
2364+
new_comp = self._app.modeler.schematic.create_component_from_spicemodel(
2365+
input_file=l["file_path"], location=j["position"]
2366+
)
2367+
elif type == "nexxim state space":
2368+
new_comp = self._app.modeler.schematic.create_nexxim_state_space_component(
2369+
l["file_path"], l["num_terminals"], location=j["position"], angle=j["angle"]
2370+
)
2371+
for name, parameter in j["properties"].items():
2372+
new_comp.parameters[name] = parameter
2373+
2374+
for i, j in data["pin_mapping"].items():
2375+
pins = []
2376+
for k, l in j.items():
2377+
for comp in list(self._app.modeler.schematic.components.values()):
2378+
if not comp.refdes:
2379+
continue
2380+
elif comp.refdes == k:
2381+
for pin in comp.pins:
2382+
if pin.name == l:
2383+
pins.append(pin)
2384+
if i == "gnd":
2385+
for gnd_pin in pins:
2386+
self._app.modeler.schematic.create_gnd(gnd_pin.location, gnd_pin.angle, page=i)
2387+
else:
2388+
pins[0].connect_to_component(pins[1:], page_name=i)

src/ansys/aedt/core/generic/ibis_reader.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -875,6 +875,9 @@ def parse_ibis_file(self):
875875
arg_component.append([False, False])
876876
else:
877877
arg_component.append([True, False])
878+
if hasattr(pin, "negative_pin"):
879+
arg_component.append(f"{pin.short_name}:=")
880+
arg_component.append([True, True])
878881
arg_components.append(arg_component)
879882

880883
args.append(arg_buffers)
@@ -899,7 +902,11 @@ def read_model(self, ibis, model_list):
899902
900903
"""
901904
for model_info in model_list:
902-
model_spec_info = model_info["model"].strip().split("\n")
905+
if "model" in model_info:
906+
model_spec_info = model_info["model"].strip().split("\n")
907+
elif "model selector" in model_info:
908+
model_spec_info = model_info["model selector"].strip().split("\n")
909+
model_spec_info = [i.split(" ")[0] for i in model_spec_info]
903910
for idx, model_spec in enumerate(model_spec_info):
904911
if not idx:
905912
model = Model()
@@ -1294,6 +1301,9 @@ def parse_ibis_file(self):
12941301
arg_component.append([False, flag])
12951302
else:
12961303
arg_component.append([True, flag])
1304+
if hasattr(pin, "negative_pin"):
1305+
arg_component.append(f"{pin.short_name}:=")
1306+
arg_component.append([True, True])
12971307
arg_components.append(arg_component)
12981308

12991309
args.append(arg_buffers)

src/ansys/aedt/core/modeler/circuits/primitives_circuit.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -486,7 +486,7 @@ def create_model_from_touchstone(self, input_file, model_name=None, show_bitmap=
486486
bmp_file_name = os.path.basename(image_subcircuit_path)
487487

488488
if not port_names:
489-
port_names = ["Port" + str(i + 1) for i in range(num_terminal)]
489+
port_names = [str(i + 1) for i in range(num_terminal)]
490490
arg = [
491491
"NAME:" + model_name,
492492
"Name:=",
@@ -724,7 +724,7 @@ def create_model_from_nexxim_state_space(self, input_file, num_terminal, model_n
724724
if model_name in list(self.omodel_manager.GetNames()):
725725
model_name = generate_unique_name(model_name, n=2)
726726

727-
port_names = ["Port" + str(i + 1) for i in range(num_terminal)]
727+
port_names = [str(i + 1) for i in range(num_terminal)]
728728
arg = [
729729
"NAME:" + model_name,
730730
"Name:=",

0 commit comments

Comments
 (0)