diff --git a/_unittest/test_21_Circuit.py b/_unittest/test_21_Circuit.py index 48127cd7b72..b7a7adf1f31 100644 --- a/_unittest/test_21_Circuit.py +++ b/_unittest/test_21_Circuit.py @@ -169,6 +169,8 @@ def test_11_export_fullwave(self): def test_12_connect_components(self): myind = self.aedtapp.modeler.schematic.create_inductor("L100", 1e-9) + myind2 = self.aedtapp.modeler.schematic.create_inductor("L1001", 1e-9) + myind3 = self.aedtapp.modeler.schematic.create_inductor("L1002", 1e-9) myres = self.aedtapp.modeler.schematic.create_resistor("R100", 50) mycap = self.aedtapp.modeler.schematic.create_capacitor("C100", 1e-12) portname = self.aedtapp.modeler.schematic.create_interface_port("Port1") @@ -177,6 +179,9 @@ def test_12_connect_components(self): assert myind.pins[0].connect_to_component(portname.pins[0]) assert myind.pins[1].connect_to_component(myres.pins[1], use_wire=True) assert self.aedtapp.modeler.connect_schematic_components(myres.id, mycap.id, pin_starting=1) + assert self.aedtapp.modeler.connect_schematic_components( + myind2, myind3, pin_starting=["n1", "n2"], pin_ending=["n2", "n1"], use_wire=False + ) gnd = self.aedtapp.modeler.schematic.create_gnd() assert mycap.pins[1].connect_to_component(gnd.pins[0]) # create_interface_port @@ -888,8 +893,8 @@ def test_48_automatic_tdr(self): result, tdr_probe_name = self.aedtapp.create_tdr_schematic_from_snp( input_file=touchstone_file, - probe_pins=["A-MII-RXD1_30.SQFP28X28_208.P"], - probe_ref_pins=["A-MII-RXD1_65.SQFP20X20_144.N"], + tx_schematic_pins=["A-MII-RXD1_30.SQFP28X28_208.P"], + tx_schematic_differential_pins=["A-MII-RXD1_65.SQFP20X20_144.N"], termination_pins=["A-MII-RXD2_32.SQFP28X28_208.P", "A-MII-RXD2_66.SQFP20X20_144.N"], differential=True, rise_time=35, @@ -905,14 +910,13 @@ def test_49_automatic_ami(self): ami_file = os.path.join(local_path, "example_models", test_subfolder, "pcieg5_32gt.ibs") result, eye_curve_tx, eye_curve_rx = self.aedtapp.create_ami_schematic_from_snp( input_file=touchstone_file, - ibis_ami=ami_file, - component_name="Spec_Model", + ibis_tx_file=ami_file, tx_buffer_name="1p", rx_buffer_name="2p", - tx_pins=["A-MII-RXD1_30.SQFP28X28_208.P"], - tx_refs=["A-MII-RXD1_65.SQFP20X20_144.N"], - rx_pins=["A-MII-RXD2_32.SQFP28X28_208.P"], - rx_refs=["A-MII-RXD2_66.SQFP20X20_144.N"], + tx_schematic_pins=["A-MII-RXD1_30.SQFP28X28_208.P"], + rx_schematic_pins=["A-MII-RXD2_32.SQFP28X28_208.P"], + tx_schematic_differential_pins=["A-MII-RXD1_65.SQFP20X20_144.N"], + rx_schematic_differentialial_pins=["A-MII-RXD2_66.SQFP20X20_144.N"], use_ibis_buffer=False, differential=True, bit_pattern="random_bit_count=2.5e3 random_seed=1", @@ -923,6 +927,28 @@ def test_49_automatic_ami(self): ) assert result + @pytest.mark.skipif(config["NonGraphical"] and is_linux, reason="Method not working in Linux and Non graphical.") + def test_49_automatic_ibis(self): + touchstone_file = os.path.join(local_path, "example_models", test_subfolder, touchstone_custom) + ibis_file = os.path.join(local_path, "example_models", "T15", "u26a_800_modified.ibs") + result, eye_curve_tx, eye_curve_rx = self.aedtapp.create_ibis_schematic_from_snp( + input_file=touchstone_file, + ibis_tx_file=ibis_file, + tx_buffer_name="DQ_FULL_800", + rx_buffer_name="DQ_FULL_800", + tx_schematic_pins=["A-MII-RXD1_30.SQFP28X28_208.P"], + rx_schematic_pins=["A-MII-RXD2_32.SQFP28X28_208.P"], + ibis_rx_file=ibis_file, + use_ibis_buffer=True, + differential=False, + bit_pattern="random_bit_count=2.5e3 random_seed=1", + unit_interval="31.25ps", + use_convolution=True, + analyze=False, + design_name="AMI", + ) + assert result + def test_50_enforce_touchstone_passive(self): self.aedtapp.insert_design("Touchstone_passive") self.aedtapp.modeler.schematic_units = "mil" diff --git a/examples/07-Circuit/Virtual_Compliance.py b/examples/07-Circuit/Virtual_Compliance.py index 05fbe880dd8..3c9f444ddb7 100644 --- a/examples/07-Circuit/Virtual_Compliance.py +++ b/examples/07-Circuit/Virtual_Compliance.py @@ -92,8 +92,8 @@ # The original circuit schematic is duplicated and modified to achieve this target. result, tdr_probe_name = cir.create_tdr_schematic_from_snp(input_file=touchstone_path, - probe_pins=["X1.A2.PCIe_Gen4_RX0_P"], - probe_ref_pins=["X1.A3.PCIe_Gen4_RX0_N"], + tx_schematic_pins=["X1.A2.PCIe_Gen4_RX0_P"], + tx_schematic_differential_pins=["X1.A3.PCIe_Gen4_RX0_N"], termination_pins=["U1.AP26.PCIe_Gen4_RX0_P", "U1.AN26.PCIe_Gen4_RX0_N"], differential=True, rise_time=35, use_convolution=True, @@ -105,14 +105,18 @@ # Create an Ibis AMI project to compute an eye diagram simulation and retrieve # eye mask violations. result, eye_curve_tx, eye_curve_rx = cir.create_ami_schematic_from_snp(input_file=touchstone_path, - ibis_ami=os.path.join(projectdir, "models", - "pcieg5_32gt.ibs"), - component_name="Spec_Model", tx_buffer_name="1p", - rx_buffer_name="2p", - tx_pins=["U1.AM25.PCIe_Gen4_TX0_CAP_P"], - tx_refs=["U1.AL25.PCIe_Gen4_TX0_CAP_N"], - rx_pins=["X1.B2.PCIe_Gen4_TX0_P"], - rx_refs=["X1.B3.PCIe_Gen4_TX0_N"], + ibis_tx_file=os.path.join(projectdir, "models", + "pcieg5_32gt.ibs"), + tx_buffer_name="1p", rx_buffer_name="2p", + tx_schematic_pins=[ + "U1.AM25.PCIe_Gen4_TX0_CAP_P"], + rx_schematic_pins=[ + "X1.B2.PCIe_Gen4_TX0_P"], + tx_schematic_differential_pins=[ + "U1.AL25.PCIe_Gen4_TX0_CAP_N"], + rx_schematic_differentialial_pins=[ + "X1.B3.PCIe_Gen4_TX0_N"], + ibis_tx_component_name="Spec_Model", use_ibis_buffer=False, differential=True, bit_pattern="random_bit_count=2.5e3 random_seed=1", unit_interval="31.25ps", use_convolution=True, diff --git a/pyaedt/circuit.py b/pyaedt/circuit.py index 3b81392a75c..5b257d538af 100644 --- a/pyaedt/circuit.py +++ b/pyaedt/circuit.py @@ -1659,12 +1659,14 @@ def import_edb_in_circuit(self, input_dir): hfss.close_project(save=False) return hfss_3d_layout_model - @pyaedt_function_handler(touchstone="input_file") + @pyaedt_function_handler( + touchstone="input_file", probe_pins="tx_schematic_pins", probe_ref_pins="tx_schematic_differential_pins" + ) def create_tdr_schematic_from_snp( self, input_file, - probe_pins, - probe_ref_pins=None, + tx_schematic_pins, + tx_schematic_differential_pins=None, termination_pins=None, differential=True, rise_time=30, @@ -1678,9 +1680,9 @@ def create_tdr_schematic_from_snp( ---------- input_file : str Full path to the sNp file. - probe_pins : list + tx_schematic_pins : list List of pins to attach to the probe components. - probe_ref_pins : list, optional + tx_schematic_differential_pins : list, optional Reference pins to attach to probe components. The default is ``None``. This parameter is valid only in differential TDR probes. termination_pins : list, optional @@ -1715,13 +1717,13 @@ def create_tdr_schematic_from_snp( center_x = sub.location[0] center_y = sub.location[1] left = 0 - delta_y = -1 * sub.location[1] - 2000 - 50 * len(probe_pins) + delta_y = -1 * sub.location[1] - 2000 - 50 * len(tx_schematic_pins) if differential: tdr_probe = self.modeler.components.components_catalog["TDR_Differential_Ended"] else: tdr_probe = self.modeler.components.components_catalog["TDR_Single_Ended"] tdr_probe_names = [] - for i, probe_pin in enumerate(probe_pins): + for i, probe_pin in enumerate(tx_schematic_pins): pos_y = unit_converter(delta_y - left * 1000, input_units="mil", output_units=self.modeler.schematic_units) left += 1 new_tdr_comp = tdr_probe.place("Tdr_probe", [center_x, center_y + pos_y], angle=-90) @@ -1729,11 +1731,11 @@ def create_tdr_schematic_from_snp( if isinstance(probe_pin, int): p_pin = probe_pin if differential: - n_pin = probe_ref_pins[i] + n_pin = tx_schematic_differential_pins[i] else: p_pin = [k for k in sub.pins if k.name == probe_pin][0] if differential: - n_pin = [k for k in sub.pins if k.name == probe_ref_pins[i]][0] + n_pin = [k for k in sub.pins if k.name == tx_schematic_differential_pins[i]][0] except IndexError: self.logger.error("Failed to retrieve the pins.") return False @@ -1902,18 +1904,26 @@ def create_lna_schematic_from_snp( self.analyze() return True, diff_pairs, comm_pairs - @pyaedt_function_handler(touchstone="input_file") + @pyaedt_function_handler( + touchstone="input_file", + ibis_ami="ibis_tx_file", + tx_pins="tx_schematic_pins", + rx_pins="rx_schematic_pins", + tx_refs="tx_schematic_differential_pins", + rx_refs="rx_schematic_differentialial_pins", + ) def create_ami_schematic_from_snp( self, input_file, - ibis_ami, - component_name, + ibis_tx_file, tx_buffer_name, rx_buffer_name, - tx_pins, - tx_refs, - rx_pins, - rx_refs, + tx_schematic_pins, + rx_schematic_pins, + tx_schematic_differential_pins=None, + rx_schematic_differentialial_pins=None, + ibis_tx_component_name=None, + ibis_rx_component_name=None, use_ibis_buffer=True, differential=True, bit_pattern=None, @@ -1921,6 +1931,8 @@ def create_ami_schematic_from_snp( use_convolution=True, analyze=False, design_name="AMI", + ibis_rx_file=None, + create_setup=True, ): """Create a schematic from a Touchstone file and automatically set up an IBIS-AMI analysis. @@ -1928,22 +1940,26 @@ def create_ami_schematic_from_snp( ---------- input_file : str Full path to the sNp file. - ibis_ami : str + ibis_tx_file : str Full path to the IBIS file. - component_name : str - Component name in the IBIS file to assign to components. + ibis_tx_component_name : str, optional + IBIS component name to use for the simulation of the transmitter. + This parameter is needed only if IBIS component pins are used. + ibis_rx_component_name : str, optional + IBIS component name to use for the simulation of the receiver. + This parameter is needed only if IBIS component pins are used. tx_buffer_name : str Transmission buffer name. rx_buffer_name : str Receiver buffer name - tx_pins : list + tx_schematic_pins : list Pins to assign the transmitter IBIS. - tx_refs : list + tx_schematic_differential_pins : list Reference pins to assign the transmitter IBIS. This parameter is only used in a differential configuration. - rx_pins : list + rx_schematic_pins : list Pins to assign the receiver IBIS. - rx_refs : list + rx_schematic_differentialial_pins : list Reference pins to assign the receiver IBIS. This parameter is only used in a differential configuration. use_ibis_buffer : bool, optional @@ -1962,7 +1978,116 @@ def create_ami_schematic_from_snp( Whether to automatically assign differential pairs. The default is ``False``. design_name : str, optional New schematic name. The default is ``"LNA"``. + ibis_rx_file : str, optional + Ibis receiver file. + create_setup : bool, optional + Whether to create a transient or an ami setup. The default is ``True``. + + Returns + ------- + (bool, list, list) + First argument is ``True`` if successful. + Second and third arguments are respectively the names of the tx and rx mode probes. + """ + + return self.create_ibis_schematic_from_snp( + input_file=input_file, + ibis_tx_file=ibis_tx_file, + tx_buffer_name=tx_buffer_name, + rx_buffer_name=rx_buffer_name, + tx_schematic_pins=tx_schematic_pins, + rx_schematic_pins=rx_schematic_pins, + ibis_rx_file=ibis_rx_file, + tx_schematic_differential_pins=tx_schematic_differential_pins, + rx_schematic_differential_pins=rx_schematic_differentialial_pins, + ibis_tx_component_name=ibis_tx_component_name, + ibis_rx_component_name=ibis_rx_component_name, + use_ibis_buffer=use_ibis_buffer, + differential=differential, + bit_pattern=bit_pattern, + unit_interval=unit_interval, + use_convolution=use_convolution, + analyze=analyze, + design_name=design_name, + is_ami=True, + create_setup=create_setup, + ) + + @pyaedt_function_handler() + def create_ibis_schematic_from_snp( + self, + input_file, + ibis_tx_file, + tx_buffer_name, + rx_buffer_name, + tx_schematic_pins, + rx_schematic_pins, + ibis_rx_file=None, + tx_schematic_differential_pins=None, + rx_schematic_differential_pins=None, + ibis_tx_component_name=None, + ibis_rx_component_name=None, + use_ibis_buffer=True, + differential=True, + bit_pattern=None, + unit_interval=None, + use_convolution=True, + analyze=False, + design_name="IBIS", + is_ami=False, + create_setup=True, + ): + """Create a schematic from a Touchstone file and automatically set up an IBIS-AMI analysis. + Parameters + ---------- + input_file : str + Full path to the sNp file. + ibis_tx_file : str + Full path to the IBIS file. + tx_buffer_name : str + Transmission buffer name. It can be a buffer or an ibis pin name. + In the latter case the user has to provide also the component_name. + rx_buffer_name : str + Receiver buffer name. + tx_schematic_pins : list + Pins to assign to the transmitter IBIS. + rx_schematic_pins : list, optional + Pins to assign to the receiver IBIS. + tx_schematic_differential_pins : list, optional + Reference pins to assign to the transmitter IBIS. This parameter is only used in + a differential configuration. + rx_schematic_differential_pins : list + Reference pins to assign to the receiver IBIS. This parameter is only used + in a differential configuration. + ibis_tx_component_name : str, optional + IBIS component name to use for the simulation of the transmitter. + This parameter is needed only if IBIS component pins are used. + ibis_rx_component_name : str, optional + IBIS component name to use for the simulation of the receiver. + This parameter is needed only if IBIS component pins are used. + use_ibis_buffer : bool, optional + Whether to use the IBIS buffer. The default is ``True``. If ``False``, pins are used. + differential : bool, optional + Whether the buffers are differential. The default is ``True``. If ``False``, + the buffers are single-ended. + bit_pattern : str, optional + IBIS bit pattern. + unit_interval : str, optional + Unit interval of the bit pattern. + use_convolution : bool, optional + Whether to use convolution for the Touchstone file. The default is + ``True``. If ``False``, state-space is used. + analyze : bool + Whether to automatically assign differential pairs. The default is ``False``. + design_name : str, optional + New schematic name. The default is ``"IBIS"``. + is_ami : bool, optional + Whether the ibis is AMI. The default is ``False``. + ibis_rx_file : str, optional + Ibis receiver file. + create_setup : bool, optional + Whether to create transient or ami setup. The default is ``True``. Returns ------- @@ -1980,67 +2105,225 @@ def create_ami_schematic_from_snp( touchstone_path = input_file sub = self.modeler.components.create_touchstone_component(touchstone_path) + return self.create_ibis_schematic_from_pins( + ibis_tx_file=ibis_tx_file, + ibis_rx_file=ibis_rx_file, + tx_buffer_name=tx_buffer_name, + rx_buffer_name=rx_buffer_name, + tx_schematic_pins=tx_schematic_pins, + rx_schematic_pins=rx_schematic_pins, + tx_schematic_differential_pins=tx_schematic_differential_pins, + rx_schematic_differential_pins=rx_schematic_differential_pins, + tx_component_name=sub.name, + ibis_tx_component_name=ibis_tx_component_name, + ibis_rx_component_name=ibis_rx_component_name, + use_ibis_buffer=use_ibis_buffer, + differential=differential, + bit_pattern=bit_pattern, + unit_interval=unit_interval, + use_convolution=use_convolution, + analyze=analyze, + is_ami=is_ami, + create_setup=create_setup, + ) + + @pyaedt_function_handler() + def create_ibis_schematic_from_pins( + self, + ibis_tx_file, + ibis_rx_file=None, + tx_buffer_name="", + rx_buffer_name="", + tx_schematic_pins=None, + rx_schematic_pins=None, + tx_schematic_differential_pins=None, + rx_schematic_differential_pins=None, + tx_component_name=None, + rx_component_name=None, + ibis_tx_component_name=None, + ibis_rx_component_name=None, + use_ibis_buffer=True, + differential=True, + bit_pattern=None, + unit_interval=None, + use_convolution=True, + analyze=False, + is_ami=False, + create_setup=True, + ): + """Create a schematic from a list of pins and automatically set up an IBIS-AMI analysis. + + Parameters + ---------- + + ibis_tx_file : str + Full path to the IBIS file for transmitters. + ibis_rx_file : str + Full path to the IBIS file for receiver. + tx_buffer_name : str + Transmission buffer name. It can be a buffer or a ibis pin name. + In this last case the user has to provide also the component_name. + rx_buffer_name : str + Receiver buffer name. + tx_schematic_pins : list + Pins to assign to the transmitter IBIS. + rx_schematic_pins : list, optional + Pins to assign to the receiver IBIS. + tx_schematic_differential_pins : list, optional + Reference pins to assign to the transmitter IBIS. This parameter is only used in + a differential configuration. + rx_schematic_differential_pins : list + Reference pins to assign to the receiver IBIS. This parameter is only used + in a differential configuration. + tx_component_name : str, optional + Component name in AEDT circuit schematic to which tx_pins belongs. + rx_component_name : str, optional + Component name in AEDT circuit schematic to which rx_pins belongs. + ibis_tx_component_name : str, optional + IBIS component name to use for the simulation of the transmitter. + This parameter is needed only if IBIS component pins are used. + ibis_rx_component_name : str, optional + IBIS component name to use for the simulation of the receiver. + This parameter is needed only if IBIS component pins are used. + use_ibis_buffer : bool, optional + Whether to use the IBIS buffer. The default is ``True``. If ``False``, pins are used. + differential : bool, optional + Whether the buffers are differential. The default is ``True``. If ``False``, + the buffers are single-ended. + bit_pattern : str, optional + IBIS bit pattern. + unit_interval : str, optional + Unit interval of the bit pattern. + use_convolution : bool, optional + Whether to use convolution for the Touchstone file. The default is + ``True``. If ``False``, state-space is used. + analyze : bool + Whether to automatically assign differential pairs. The default is ``False``. + is_ami : bool, optional + Whether the ibis is AMI. The default is ``False``. + create_setup : bool, optional + Whether to create transient or ami setup. The default is ``True``. + + + Returns + ------- + (bool, list, list) + First argument is ``True`` if successful. + Second and third arguments are respectively the names of the tx and rx mode probes. + """ + + if tx_component_name is None: + try: + tx_component_name = [ + i + for i, v in self.modeler.components.components.items() + if "FileName" in v.parameters + or "ModelName" in v.parameters + and v.parameters["ModelName"] == "FieldSolver" + ][0] + except Exception: + self.logger.error("A component has to be passed or an Sparameter present.") + return False + if rx_component_name is None: + rx_component_name = tx_component_name + sub = self.modeler.components[tx_component_name] center_x = sub.location[0] center_y = sub.location[1] + subx = self.modeler.components[rx_component_name] + center_x_rx = subx.location[0] + center_y_rx = subx.location[1] left = 0 - delta_y = -1 * sub.location[1] - 2000 - 50 * len(tx_pins) - ibis = self.get_ibis_model_from_file(ibis_ami, is_ami=True) + delta_y = center_y - 0.0508 - 0.00127 * len(tx_schematic_pins) + delta_y_rx = center_y_rx - 0.0508 - 0.00127 * len(tx_schematic_pins) + for el in self.modeler.components.components.values(): + if delta_y >= el.bounding_box[1]: + delta_y = el.bounding_box[1] - 0.02032 + if delta_y_rx <= el.bounding_box[3]: + delta_y_rx = el.bounding_box[3] + 0.02032 + + ibis = self.get_ibis_model_from_file(ibis_tx_file, is_ami=is_ami) + if ibis_rx_file: + ibis_rx = self.get_ibis_model_from_file(ibis_rx_file, is_ami=is_ami) + else: + ibis_rx = ibis tx_eye_names = [] rx_eye_names = [] - for j in range(len(tx_pins)): - pos_x = unit_converter(2000, input_units="mil", output_units=self.modeler.schematic_units) - pos_y = unit_converter(delta_y - left * 800, input_units="mil", output_units=self.modeler.schematic_units) - left += 1 - p_pin1 = [i for i in sub.pins if i.name == tx_pins[j]][0] - p_pin2 = [i for i in sub.pins if i.name == rx_pins[j]][0] + for j in range(len(tx_schematic_pins)): + pos_x = center_x - unit_converter(2000, input_units="mil", output_units=self.modeler.schematic_units) + pos_y = delta_y + unit_converter( + left * 0.02032, input_units="meter", output_units=self.modeler.schematic_units + ) + pos_x_rx = center_x_rx + unit_converter(2000, input_units="mil", output_units=self.modeler.schematic_units) + pos_y_rx = delta_y_rx + unit_converter( + left * 0.02032, input_units="mil", output_units=self.modeler.schematic_units + ) + + left += 1 + p_pin1 = sub[tx_schematic_pins[j]] + p_pin2 = subx[rx_schematic_pins[j]] if differential: - n_pin1 = [i for i in sub.pins if i.name == tx_refs[j]][0] - n_pin2 = [i for i in sub.pins if i.name == rx_refs[j]][0] + n_pin1 = sub[tx_schematic_differential_pins[j]] + n_pin2 = subx[rx_schematic_differential_pins[j]] if use_ibis_buffer: - buf = [k for k in ibis.components[component_name].buffer.keys() if k.startswith(tx_buffer_name + "_")] + buf = [k for k, _ in ibis.buffers.items() if k.startswith(tx_buffer_name + "_")] if differential: buf = [k for k in buf if k.endswith("diff")] - tx = ibis.components[component_name].buffer[buf[0]].insert(center_x - pos_x, center_y + pos_y) - buf = [k for k in ibis.components[component_name].buffer.keys() if k.startswith(rx_buffer_name + "_")] + tx = ibis.buffers[buf[0]].insert(pos_x, pos_y) + if tx.location[0] > tx.pins[0].location[0]: + tx.angle = 180 + buf = [k for k, _ in ibis_rx.buffers.items() if k.startswith(rx_buffer_name + "_")] if differential: buf = [k for k in buf if k.endswith("diff")] - rx = ibis.components[component_name].buffer[buf[0]].insert(center_x + pos_x, center_y + pos_y, 180) + rx = ibis_rx.buffers[buf[0]].insert(pos_x_rx, pos_y_rx, 180) + if rx.location[0] < rx.pins[0].location[0]: + rx.angle = 0 else: - buf = [k for k in ibis.components[component_name].pins.keys() if k.startswith(tx_buffer_name + "_")] + if ibis_tx_component_name: + cmp_tx = ibis.components[ibis_tx_component_name] + else: + cmp_tx = list(ibis.components.values())[0] + if ibis_rx_component_name: + cmp_rx = ibis.components[ibis_tx_component_name] + elif not ibis_rx_file: + cmp_rx = cmp_tx + else: + cmp_rx = list(ibis_rx.components.values())[0] + buf = [k for k in cmp_tx.pins.keys() if k.startswith(tx_buffer_name + "_")] if differential: buf = [k for k in buf if k.endswith("diff")] - tx = ibis.components[component_name].pins[buf[0]].insert(center_x - pos_x, center_y + pos_y) - buf = [k for k in ibis.components[component_name].pins.keys() if k.startswith(rx_buffer_name + "_")] + tx = cmp_tx.pins[buf[0]].insert(pos_x, pos_y) + if tx.location[0] > tx.pins[0].location[0]: + tx.angle = 180 + buf = [k for k in cmp_rx.pins.keys() if k.startswith(rx_buffer_name + "_")] if differential: buf = [k for k in buf if k.endswith("diff")] - rx = ibis.components[component_name].pins[buf[0]].insert(center_x + pos_x, center_y + pos_y, 180) - - tx_eye_names.append(tx.parameters["probe_name"]) - rx_eye_names.append(rx.parameters["source_name"]) - _, first, second = tx.pins[0].connect_to_component(p_pin1, page_port_angle=180) - self.modeler.move(first, [0, 100], "mil") - if second.pins[0].location[0] > center_x: - self.modeler.move(second, [1000, 0], "mil") + rx = cmp_rx.pins[buf[0]].insert(pos_x_rx, pos_y_rx, 180) + if rx.location[0] < rx.pins[0].location[0]: + rx.angle = 0 + _, first_tx, second_tx = tx.pins[0].connect_to_component(p_pin1, page_port_angle=180) + self.modeler.move(first_tx, [0, 100], "mil") + if second_tx.pins[0].location[0] > center_x: + self.modeler.move(second_tx, [1000, 0], "mil") else: - self.modeler.move(second, [-1000, 0], "mil") - _, first, second = rx.pins[0].connect_to_component(p_pin2, page_port_angle=0) - self.modeler.move(first, [0, -100], "mil") - if second.pins[0].location[0] > center_x: - self.modeler.move(second, [1000, 0], "mil") + self.modeler.move(second_tx, [-1000, 0], "mil") + _, first_rx, second_rx = rx.pins[0].connect_to_component(p_pin2, page_port_angle=0) + self.modeler.move(first_rx, [0, -100], "mil") + if second_rx.pins[0].location[0] > center_x_rx: + self.modeler.move(second_rx, [1000, 0], "mil") else: - self.modeler.move(second, [-1000, 0], "mil") + self.modeler.move(second_rx, [-1000, 0], "mil") if differential: _, first, second = tx.pins[1].connect_to_component(n_pin1, page_port_angle=180) self.modeler.move(first, [0, -100], "mil") - if second.pins[0].location[0] > center_x: + if second.pins[0].location[0] > center_x_rx: self.modeler.move(second, [1000, 0], "mil") else: self.modeler.move(second, [-1000, 0], "mil") _, first, second = rx.pins[1].connect_to_component(n_pin2, page_port_angle=0) self.modeler.move(first, [0, 100], "mil") - if second.pins[0].location[0] > center_x: + if second.pins[0].location[0] > center_x_rx: self.modeler.move(second, [1000, 0], "mil") else: self.modeler.move(second, [-1000, 0], "mil") @@ -2048,24 +2331,33 @@ def create_ami_schematic_from_snp( tx.parameters["UIorBPSValue"] = unit_interval if bit_pattern: tx.parameters["BitPattern"] = "random_bit_count=2.5e3 random_seed=1" - - setup_ami = self.create_setup("AMI", "NexximAMI") - if use_convolution: - self.oanalysis.AddAnalysisOptions( - [ - "NAME:DataBlock", - "DataBlockID:=", - 8, - "Name:=", - "Nexxim Options", + if is_ami: + tx_eye_names.append(tx.parameters["probe_name"]) + rx_eye_names.append(rx.parameters["source_name"]) + else: + tx_eye_names.append(first_tx.name.split("@")[1]) + rx_eye_names.append(first_rx.name.split("@")[1]) + if create_setup: + setup_type = "NexximTransient" + if is_ami: + setup_type = "NexximAMI" + setup_ibis = self.create_setup("Transient", setup_type) + if use_convolution: + self.oanalysis.AddAnalysisOptions( [ - "NAME:ModifiedOptions", - "ts_convolution:=", - True, - ], - ] - ) - setup_ami.props["OptionName"] = "Nexxim Options" - if analyze: - setup_ami.analyze() + "NAME:DataBlock", + "DataBlockID:=", + 8, + "Name:=", + "Nexxim Options", + [ + "NAME:ModifiedOptions", + "ts_convolution:=", + True, + ], + ] + ) + setup_ibis.props["OptionName"] = "Nexxim Options" + if analyze: + setup_ibis.analyze() return True, tx_eye_names, rx_eye_names diff --git a/pyaedt/generic/ibis_reader.py b/pyaedt/generic/ibis_reader.py index 25bd8078a4d..cb86695cd5e 100644 --- a/pyaedt/generic/ibis_reader.py +++ b/pyaedt/generic/ibis_reader.py @@ -841,7 +841,12 @@ def parse_ibis_file(self): buffers[buffer.name] = buffer ibis.buffers = buffers + self._ibis_model = ibis + available_names = self._circuit.modeler.schematic.o_component_manager.GetNames() + already_present = [i for i in buffers.keys() if i in available_names] + if len(already_present) == len(buffers): + return ibis_info if self._circuit: args = [ "NAME:Options", @@ -875,7 +880,6 @@ def parse_ibis_file(self): self._circuit.modeler.schematic.o_component_manager.ImportModelsFromFile(self._filename, args) - self._ibis_model = ibis return ibis_info # Model diff --git a/pyaedt/modeler/circuits/PrimitivesNexxim.py b/pyaedt/modeler/circuits/PrimitivesNexxim.py index 050cff65412..f765a5df8ce 100644 --- a/pyaedt/modeler/circuits/PrimitivesNexxim.py +++ b/pyaedt/modeler/circuits/PrimitivesNexxim.py @@ -83,9 +83,17 @@ def __getitem__(self, partname): if isinstance(partname, int): return self.components[partname] for el in self.components: - if self.components[el].name == partname or self.components[el].composed_name == partname or el == partname: + cmp = self.components[el] + if ( + cmp.name == partname + or cmp.composed_name == partname + or el == partname + or cmp.refdes == partname + or cmp.parameters.get("InstanceName", "") == partname + ): return self.components[el] - + if isinstance(partname, CircuitComponent): + return partname return None @property diff --git a/pyaedt/modeler/circuits/object3dcircuit.py b/pyaedt/modeler/circuits/object3dcircuit.py index 93fe04f51df..911c78c8ab8 100644 --- a/pyaedt/modeler/circuits/object3dcircuit.py +++ b/pyaedt/modeler/circuits/object3dcircuit.py @@ -41,9 +41,10 @@ class CircuitPins(object): """Manages circuit component pins.""" - def __init__(self, circuit_comp, pinname): + def __init__(self, circuit_comp, pinname, pin_number): self._circuit_comp = circuit_comp self.name = pinname + self.pin_number = pin_number self._oeditor = circuit_comp._oeditor @property @@ -414,6 +415,14 @@ def __init__(self, component, name, props): class CircuitComponent(object): """Manages circuit components.""" + def __getitem__(self, item): + if isinstance(item, int): + return self.pins[item - 1] + for i in self.pins: + if i.name == item: + return i + raise KeyError("Pin {} not found.".format(item)) + @property def composed_name(self): """Composed names.""" @@ -607,21 +616,22 @@ def pins(self): if self._pins: return self._pins self._pins = [] - + idx = 1 try: pins = list(self._oeditor.GetComponentPins(self.composed_name)) if "Port@" in self.composed_name and pins == []: - self._pins.append(CircuitPins(self, self.composed_name)) + self._pins.append(CircuitPins(self, self.composed_name, idx)) return self._pins elif not pins: return [] for pin in pins: if self._circuit_components._app.design_type != "Twin Builder": - self._pins.append(CircuitPins(self, pin)) + self._pins.append(CircuitPins(self, pin, idx)) elif pin not in list(self.parameters.keys()): - self._pins.append(CircuitPins(self, pin)) + self._pins.append(CircuitPins(self, pin, idx)) + idx += 1 except AttributeError: - self._pins.append(CircuitPins(self, self.composed_name)) + self._pins.append(CircuitPins(self, self.composed_name, idx)) return self._pins @property diff --git a/pyaedt/modeler/schematic.py b/pyaedt/modeler/schematic.py index cc9100ad4b8..42df7fb6f52 100644 --- a/pyaedt/modeler/schematic.py +++ b/pyaedt/modeler/schematic.py @@ -23,7 +23,6 @@ # SOFTWARE. import random -import re import sys import time import warnings @@ -128,7 +127,9 @@ def zoom_to_fit(self): pinnum_first="pin_starting", pinnum_second="pin_ending", ) - def connect_schematic_components(self, starting_component, ending_component, pin_starting=2, pin_ending=1): + def connect_schematic_components( + self, starting_component, ending_component, pin_starting=2, pin_ending=1, use_wire=True + ): """Connect schematic components. Parameters @@ -137,12 +138,16 @@ def connect_schematic_components(self, starting_component, ending_component, pin Starting (right) component. ending_component : str Ending (left) component for the connection line. - pin_starting : str, optional - Number of the pin at which to terminate the connection from the right end of the + pin_starting : int, str, list optional + Number or name of the pins at which to terminate the connection from the right end of the starting component. The default is ``2``. - pin_ending : str, optional - Number of the pin at which to terminate the connection from the left end of the + pin_ending : int, str, list optional + Number or name of the pins at which to terminate the connection from the left end of the ending component. The default is ``1``. + use_wire : bool, optional + Whether to use wires or a page port to connect the pins. + The default is ``True``, in which case wires are used. Note + that if wires are not well placed, shorts can result. Returns ------- @@ -156,39 +161,21 @@ def connect_schematic_components(self, starting_component, ending_component, pin """ if self._app.design_type == "Maxwell Circuit": components = self.schematic.components - obj1 = components[starting_component] else: components = self.components - obj1 = components[starting_component] - if "Port" in obj1.composed_name: - pos1 = self.oeditor.GetPropertyValue("BaseElementTab", obj1.composed_name, "Component Location").split(", ") - pos1 = [float(i.strip()[:-3]) * 0.0000254 for i in pos1] - if "GPort" in obj1.composed_name: - pos1[1] += 0.00254 - else: - if self._app.design_type == "Maxwell Circuit": - pos1 = [float(re.sub(r"[^0-9.\-]", "", x)) * 0.0000254 for x in obj1.location] - else: - pins1 = components.get_pins(starting_component) - pos1 = components.get_pin_location(starting_component, pins1[pin_starting - 1]) - obj2 = components[ending_component] - if "Port" in obj2.composed_name: - pos2 = self.oeditor.GetPropertyValue("BaseElementTab", obj2.composed_name, "Component Location").split(", ") - pos2 = [float(i.strip()[:-3]) * 0.0000254 for i in pos2] - if "GPort" in obj2.composed_name: - pos2[1] += 0.00254 - - else: - if self._app.design_type == "Maxwell Circuit": - pos2 = [float(re.sub(r"[^0-9.\-]", "", x)) * 0.0000254 for x in obj2.location] - else: - pins2 = components.get_pins(ending_component) - pos2 = components.get_pin_location(ending_component, pins2[pin_ending - 1]) - try: - self.schematic.create_wire([pos1, pos2]) - return True - except Exception: - return False + start = components[starting_component] + end = components[ending_component] + if isinstance(pin_starting, (int, str)): + pin_starting = [pin_starting] + if isinstance(pin_ending, (int, str)): + pin_ending = [pin_ending] + for pstart, pend in zip(pin_starting, pin_ending): + try: + start[pstart].connect_to_component(end[pend], use_wire=use_wire) + except Exception: + self.logger.error("Failed to connect pin {} with {}".format(pstart, pend)) + return False + return True @pyaedt_function_handler() def create_text(