Source code for pyaedt.modules.SolveSetup

"""
This module contains these classes: `Setup`, `Setup3DLayout`, and `SetupCircuit`.

This module provides all functionalities for creating and editing setups in AEDT.
It is based on templates to allow for easy creation and modification of setup properties.

"""
from __future__ import absolute_import  # noreorder

import os.path
import warnings
from collections import OrderedDict

from pyaedt.generic.DataHandlers import _dict2arg
from pyaedt.generic.DataHandlers import _tuple2dict
from pyaedt.generic.general_methods import generate_unique_name
from pyaedt.generic.general_methods import pyaedt_function_handler
from pyaedt.modules.SetupTemplates import SetupKeys
from pyaedt.modules.SetupTemplates import SweepHFSS
from pyaedt.modules.SetupTemplates import SweepHFSS3DLayout
from pyaedt.modules.SetupTemplates import SweepQ3D
from pyaedt.modules.SetupTemplates import SetupProps


[docs]class Setup(object): """Initializes, creates, and updates a 3D setup. Parameters ---------- app : :class:`pyaedt.application.Analysis3D.FieldAnalysis3D` Inherited app object. solutiontype : int, str Type of the setup. setupname : str, optional Name of the setup. The default is ``"MySetupAuto"``. isnewsetup : bool, optional Whether to create the setup from a template. The default is ``True``. If ``False``, access is to the existing setup. """ @property def p_app(self): """Parent.""" return self._app @p_app.setter def p_app(self, value): self._app = value @property def omodule(self): """Analysis module.""" return self._app.oanalysis def __repr__(self): return "SetupName " + self.name + " with " + str(len(self.sweeps)) + " Sweeps" def __init__(self, app, solutiontype, setupname="MySetupAuto", isnewsetup=True): self.auto_update = False self._app = None self.p_app = app if solutiontype is None: self.setuptype = self.p_app.design_solutions.default_setup elif isinstance(solutiontype, int): self.setuptype = solutiontype elif solutiontype in SetupKeys.SetupNames: self.setuptype = SetupKeys.SetupNames.index(solutiontype) else: self.setuptype = self.p_app.design_solutions._solution_options[solutiontype]["default_setup"] self.name = setupname self.props = {} self.sweeps = [] if isnewsetup: setup_template = SetupKeys.SetupTemplates[self.setuptype] for t in setup_template: _tuple2dict(t, self.props) self.props = SetupProps(self, self.props) else: try: setups_data = self.p_app.design_properties["AnalysisSetup"]["SolveSetups"] if setupname in setups_data: setup_data = setups_data[setupname] if "Sweeps" in setup_data and self.setuptype not in [ 0, 7, ]: # 0 and 7 represent setup HFSSDrivenAuto if self.setuptype <= 4: app = setup_data["Sweeps"] app.pop("NextUniqueID", None) app.pop("MoveBackForward", None) app.pop("MoveBackwards", None) for el in app: if isinstance(app[el], (OrderedDict, dict)): self.sweeps.append(SweepHFSS(self.omodule, setupname, el, props=app[el])) else: app = setup_data["Sweeps"] for el in app: if isinstance(app[el], (OrderedDict, dict)): self.sweeps.append(SweepQ3D(self.omodule, setupname, el, props=app[el])) setup_data.pop("Sweeps", None) self.props = SetupProps(self, OrderedDict(setup_data)) except: self.props = SetupProps(self, OrderedDict()) self.auto_update = True
[docs] @pyaedt_function_handler() def create(self): """Add a new setup based on class settings in AEDT. Returns ------- dict Dictionary of arguments. References ---------- >>> oModule.InsertSetup """ soltype = SetupKeys.SetupNames[self.setuptype] arg = ["NAME:" + self.name] _dict2arg(self.props, arg) self.omodule.InsertSetup(soltype, arg) return arg
[docs] @pyaedt_function_handler() def update(self, update_dictionary=None): """Update the setup based on either the class argument or a dictionary. Parameters ---------- update_dictionary : optional Dictionary to use to update the setup. The default is ``None``. Returns ------- bool ``True`` when successful, ``False`` when failed. References ---------- >>> oModule.EditSetup """ legacy_update = self.auto_update self.auto_update = False if update_dictionary: for el in update_dictionary: self.props[el] = update_dictionary[el] self.auto_update = legacy_update arg = ["NAME:" + self.name] _dict2arg(self.props, arg) self.omodule.EditSetup(self.name, arg) return True
[docs] @pyaedt_function_handler() def delete(self): """Delete actual Setup. Returns ------- bool `True` if setup is deleted. `False` if it failed. """ self.omodule.DeleteSetups([self.name]) self._app.setups.remove(self) return True
@pyaedt_function_handler() def _expression_cache( self, expression_list, report_type_list, intrinsics_list, isconvergence_list, isrelativeconvergence, conv_criteria, ): """Retrieve data from the expression setup cache. Parameters ---------- expressions_list : list List of formulas to retrieve. report_type_list : list List of report types for the expressions. intrinsics_list : list List of intrinsic expressions for the expressions. isconvergence_list : list List of Boolean values indicating whether the expressions are in the convergence criteria. isrelativeconvergence : bool conv_criteria: Returns ------- list List of the data. """ if isrelativeconvergence: userelative = 1 else: userelative = 0 list_data = ["NAME:ExpressionCache"] if type(expression_list) is list: i = 0 while i < len(expression_list): expression = expression_list[i] name = expression.replace("(", "_") + "1" name = name.replace(")", "_") name = name.replace(" ", "_") name = name.replace(".", "_") name = name.replace("/", "_") name = name.replace("*", "_") name = name.replace("+", "_") name = name.replace("-", "_") if type(report_type_list) is list: report_type = report_type_list[i] else: report_type = report_type_list if type(isconvergence_list) is list: isconvergence = isconvergence_list[i] else: isconvergence = isconvergence_list if type(intrinsics_list) is list: intrinsics = intrinsics_list[i] else: intrinsics = intrinsics_list list_data.append( [ "NAME:CacheItem", "Title:=", name, "Expression:=", expression, "Intrinsics:=", intrinsics, "IsConvergence:=", isconvergence, "UseRelativeConvergence:=", 1, "MaxConvergenceDelta:=", 1, "MaxConvergeValue:=", "0.01", "ReportType:=", report_type, ["NAME:ExpressionContext"], ] ) i += 1 else: name = expression_list.replace("(", "") + "1" name = name.replace(")", "") name = name.replace(" ", "") name = name.replace(",", "_") list_data.append( [ "NAME:CacheItem", "Title:=", name, "Expression:=", expression_list, "Intrinsics:=", intrinsics_list, "IsConvergence:=", isconvergence_list, "UseRelativeConvergence:=", userelative, "MaxConvergenceDelta:=", conv_criteria, "MaxConvergeValue:=", str(conv_criteria), "ReportType:=", report_type_list, ["NAME:ExpressionContext"], ] ) return list_data
[docs] @pyaedt_function_handler() def enable_expression_cache( self, expressions, report_type="Fields", intrinsics="", isconvergence=True, isrelativeconvergence=True, conv_criteria=1, ): """Enable an expression cache. Parameters ---------- expressions : str or list One or more formulas to add to the expression cache. report_type : str or list, optional Type of the report for the expression. The default is ``Fields``. If a list of expressions is supplied, supply a corresponding list of report types. intrinsics : str or list, optional Intrinsic functions for the expressions. The default is ``""``. If a list of expressions is supplied, a corresponding list of intrinsic functions must be supplied. isconvergence : bool or str or list, optional Whether the expression is in the convergence criteria. The default is ``True``. If a list of expressions is supplied, a corresponding list of Boolean values must be supplied. isrelativeconvergence : bool, optional The default is ``True``. conv_criteria : The default is ``1``. Returns ------- bool ``True`` when successful, ``False`` when failed. References ---------- >>> oModule.EditSetup """ arg = ["NAME:" + self.name] _dict2arg(self.props, arg) expression_cache = self._expression_cache( expressions, report_type, intrinsics, isconvergence, isrelativeconvergence, conv_criteria ) arg.append(expression_cache) self.omodule.EditSetup(self.name, arg) return True
[docs] @pyaedt_function_handler() def add_derivatives(self, derivative_list): """Add derivatives to the setup. Parameters ---------- derivative_list : list List of derivatives. Returns ------- bool ``True`` when successful, ``False`` when failed. References ---------- >>> oModule.EditSetup """ arg = ["NAME:" + self.name] _dict2arg(self.props, arg) arg.append("VariablesForDerivatives:=") arg.append(derivative_list) self.omodule.EditSetup(self.name, arg) return True
[docs] @pyaedt_function_handler() def enable(self, setup_name=None): """Enable a setup. Parameters ---------- setup_name : str, optional Name of the setup. The default is ``None``. Returns ------- bool ``True`` when successful, ``False`` when failed. References ---------- >>> oModule.EditSetup """ if not setup_name: setup_name = self.name self.omodule.EditSetup(setup_name, ["NAME:" + setup_name, "IsEnabled:=", True]) return True
[docs] @pyaedt_function_handler() def disable(self, setup_name=None): """Disable a setup. Parameters ---------- setup_name : str, optional Name of the setup. The default is ``None``. Returns ------- bool ``True`` when successful, ``False`` when failed. References ---------- >>> oModule.EditSetup """ if not setup_name: setup_name = self.name self.omodule.EditSetup(setup_name, ["NAME:" + setup_name, "IsEnabled:", False]) return True
[docs] @pyaedt_function_handler() def add_sweep(self, sweepname=None, sweeptype="Interpolating"): """Add a sweep to the project. Parameters ---------- sweepname : str, optional Name of the sweep. The default is ``None``. sweeptype : str, optional Type of the sweep. The default is ``"Interpolating"``. Returns ------- :class:`pyaedt.modules.SetupTemplates.SweepHFSS` or :class:`pyaedt.modules.SetupTemplates.SweepQ3D` Sweep object. References ---------- >>> oModule.InsertFrequencySweep """ if not sweepname: sweepname = generate_unique_name("Sweep") if self.setuptype == 7: self._app.logger.warning("This method only applies to HFSS and Q3d. Use add_eddy_current_sweep method.") return False if self.setuptype <= 4: sweep_n = SweepHFSS(self.omodule, self.name, sweepname, sweeptype) else: sweep_n = SweepQ3D(self.omodule, self.name, sweepname, sweeptype) sweep_n.create() self.sweeps.append(sweep_n) return sweep_n
[docs] @pyaedt_function_handler() def add_eddy_current_sweep(self, range_type="LinearStep", start=0.1, end=100, count=0.1, units="Hz", clear=True): """Create a Maxwell Eddy Current Sweep. Parameters ---------- range_type : str Type of the subrange. Options are ``"LinearCount"``, ``"LinearStep"``, ``"LogScale"`` and ``"SinglePoints"``. start : float Starting frequency. end : float, optional Stopping frequency. Required for ``rangetype="LinearCount"|"LinearStep"|"LogScale"``. count : int or float, optional Frequency count or frequency step. Required for ``rangetype="LinearCount"|"LinearStep"|"LogScale"``. units : str, optional Unit of the frequency. For example, ``"MHz`` or ``"GHz"``. The default is ``"Hz"``. clear : boolean, optional If set to ``True``, all other subranges will be suppressed except the current one under creation. Default value is ``False``. Returns ------- bool """ if self.setuptype != 7: self._app.logger.warning("This method only applies to Maxwell Eddy Current Solution.") return False legacy_update = self.auto_update self.auto_update = False props = OrderedDict() props["RangeType"] = range_type props["RangeStart"] = "{}{}".format(start, units) if range_type == "LinearStep": props["RangeEnd"] = "{}{}".format(end, units) props["RangeStep"] = "{}{}".format(count, units) elif range_type == "LinearCount": props["RangeEnd"] = "{}{}".format(end, units) props["RangeCount"] = count elif range_type == "LogScale": props["RangeEnd"] = "{}{}".format(end, units) props["RangeSamples"] = count elif range_type == "SinglePoints": props["RangeEnd"] = "{}{}".format(start, units) if clear: self.props["SweepRanges"]["Subrange"] = props elif isinstance(self.props["SweepRanges"]["Subrange"], list): self.props["SweepRanges"]["Subrange"].append(props) else: self.props["SweepRanges"]["Subrange"] = [self.props["SweepRanges"]["Subrange"], props] self.update() self.auto_update = legacy_update return True
[docs] @pyaedt_function_handler() def enable_adaptive_setup_single(self, freq=None, max_passes=None, max_delta_s=None): """Enable HFSS single frequency setup. Parameters ---------- freq : float, str, optional Frequency at which to set the adaptive convergence. The default is ``None`` which will not update the value in setup. You can enter a float value in (GHz) or a string. max_passes : int, optional Maximum number of adaptive passes. The default is ``None`` which will not update the value in setup. max_delta_s : float, optional Delta S convergence criteria. The default is ``None`` which will not update the value in setup. Returns ------- bool ``True`` when successful, ``False`` when failed. """ if self.setuptype != 1 or self.p_app.solution_type not in ["Modal", "Terminal"]: self._app.logger.error("Method applies only to HFSS-driven solutions.") return False self.auto_update = False self.props["SolveType"] = "Single" if isinstance(freq, (int, float)): freq = "{}GHz".format(freq) if freq: self.props["Frequency"] = freq if max_passes: self.props["MaximumPasses"] = max_passes if max_delta_s: self.props["MaxDeltaS"] = max_delta_s self.auto_update = True return self.update()
[docs] @pyaedt_function_handler() def enable_adaptive_setup_broadband(self, low_frequency, high_frquency, max_passes=6, max_delta_s=0.02): """Enable HFSS broadband setup. Parameters ---------- low_frequency : float, str Lower Frequency at which set the adaptive convergence. It can be float (GHz) or str. high_frquency : float, str Lower Frequency at which set the adaptive convergence. It can be float (GHz) or str. max_passes : int, optional Maximum number of adaptive passes. The default is ``6``. max_delta_s : float, optional Delta S Convergence criteria. The default is ``0.02``. Returns ------- bool ``True`` when successful, ``False`` when failed. """ if self.setuptype != 1 or self.p_app.solution_type not in ["Modal", "Terminal"]: self._app.logger.error("Method applies only to HFSS-driven solutions.") return False self.auto_update = False self.props["SolveType"] = "BroadBand" for el in list(self.props["MultipleAdaptiveFreqsSetup"].keys()): del self.props["MultipleAdaptiveFreqsSetup"][el] if isinstance(low_frequency, (int, float)): low_frequency = "{}GHz".format(low_frequency) if isinstance(high_frquency, (int, float)): high_frquency = "{}GHz".format(high_frquency) self.props["MultipleAdaptiveFreqsSetup"]["Low"] = low_frequency self.props["MultipleAdaptiveFreqsSetup"]["High"] = high_frquency self.props["MaximumPasses"] = max_passes self.props["MaxDeltaS"] = max_delta_s self.auto_update = True return self.update()
[docs] @pyaedt_function_handler() def enable_adaptive_setup_multifrequency(self, frequencies, max_delta_s=0.02): """Enable HFSS multi-frequency setup. Parameters ---------- frequencies : list Frequency at which to set the adaptive convergence. You can enter list entries as float values in GHz or as strings. max_delta_s : list, float Delta S convergence criteria. The default is ``0.02``. Returns ------- bool ``True`` when successful, ``False`` when failed. """ if self.setuptype != 1 or self.p_app.solution_type not in ["Modal", "Terminal"]: self._app.logger.error("Method applies only to HFSS-driven solutions.") return False self.auto_update = False self.props["SolveType"] = "MultiFrequency" for el in list(self.props["MultipleAdaptiveFreqsSetup"].keys()): del self.props["MultipleAdaptiveFreqsSetup"][el] i = 0 for f in frequencies: if isinstance(max_delta_s, float): if isinstance(f, (int, float)): f = "{}GHz".format(f) self.props["MultipleAdaptiveFreqsSetup"][f] = [max_delta_s] else: if isinstance(f, (int, float)): f = "{}GHz".format(f) try: self.props["MultipleAdaptiveFreqsSetup"][f] = [max_delta_s[i]] except IndexError: self.props["MultipleAdaptiveFreqsSetup"][f] = [0.02] i += 1 self.auto_update = True return self.update()
[docs]class SetupCircuit(object): """Initializes, creates, and updates a circuit setup. Parameters ---------- app : :class:`pyaedt.application.AnalysisNexxim.FieldAnalysisCircuit` Inherited app object. solutiontype : str, int Type of the setup. setupname : str, optional Name of the setup. The default is ``"MySetupAuto"``. isnewsetup : bool, optional Whether to create the setup from a template. The default is ``True.`` If ``False``, access is to the existing setup. """ def __init__(self, app, solutiontype, setupname="MySetupAuto", isnewsetup=True): self.auto_update = False self._app = None self.p_app = app if not solutiontype: self.setuptype = self.p_app.design_solutions.default_setup elif isinstance(solutiontype, int): self.setuptype = solutiontype elif solutiontype in SetupKeys.SetupNames: self.setuptype = SetupKeys.SetupNames.index(solutiontype) else: self.setuptype = self.p_app.design_solutions._solution_options[solutiontype]["default_setup"] self._Name = "LinearFrequency" props = {} if isnewsetup: setup_template = SetupKeys.SetupTemplates[self.setuptype] for t in setup_template: _tuple2dict(t, props) self.props = SetupProps(self, props) else: try: setups_data = self.p_app.design_properties["SimSetups"]["SimSetup"] if type(setups_data) is not list: setups_data = [setups_data] for setup in setups_data: if setupname == setup["Name"]: setup_data = setup setup_data.pop("Sweeps", None) self.props = SetupProps(self, setup_data) except: self.props = SetupProps(self, OrderedDict()) self._Name = setupname self.props["Name"] = setupname self.auto_update = True @property def name(self): """Name.""" return self._Name @name.setter def name(self, name): self._Name = name self.props["Name"] = name @property def p_app(self): """AEDT app module for setting up the analysis.""" return self._app @p_app.setter def p_app(self, name): self._app = name @property def _odesign(self): """Design.""" return self._app._odesign @property def omodule(self): """Analysis module. Parameters ---------- app : str Inherited app object. solutiontype : str, int Type of the setup. setupname : str, optional Name of the setup. The default is ``"MySetupAuto"``. isnewsetup : bool, optional Whether to create the setup from a template. The default is ``True.`` If ``False``, access is to the existing setup. Returns ------- str Name of the setup. """ return self._app.oanalysis
[docs] @pyaedt_function_handler() def create(self): """Add a new setup based on class settings in AEDT. Returns ------- dict Dictionary of the arguments. References ---------- >>> oModule.AddLinearNetworkAnalysis >>> oModule.AddDCAnalysis >>> oModule.AddTransient >>> oModule.AddQuickEyeAnalysis >>> oModule.AddVerifEyeAnalysis >>> oModule.AddAMIAnalysis """ soltype = SetupKeys.SetupNames[self.setuptype] arg = ["NAME:SimSetup"] _dict2arg(self.props, arg) self._setup(soltype, arg) return arg
@pyaedt_function_handler() def _setup(self, soltype, arg, newsetup=True): if newsetup: if soltype == "NexximLNA": self.omodule.AddLinearNetworkAnalysis(arg) elif soltype == "NexximDC": self.omodule.AddDCAnalysis(arg) elif soltype == "NexximTransient": self.omodule.AddTransient(arg) elif soltype == "NexximQuickEye": self.omodule.AddQuickEyeAnalysis(arg) elif soltype == "NexximVerifEye": self.omodule.AddVerifEyeAnalysis(arg) elif soltype == "NexximAMI": self.omodule.AddAMIAnalysis(arg) else: warnings.warn("Solution Not Implemented Yet") else: if soltype == "NexximLNA": self.omodule.EditLinearNetworkAnalysis(self.name, arg) elif soltype == "NexximDC": self.omodule.EditDCAnalysis(self.name, arg) elif soltype == "NexximTransient": self.omodule.EditTransient(self.name, arg) elif soltype == "NexximQuickEye": self.omodule.EditQuickEyeAnalysis(self.name, arg) elif soltype == "NexximVerifEye": self.omodule.EditVerifEyeAnalysis(self.name, arg) elif soltype == "NexximAMI": self.omodule.EditAMIAnalysis(self.name, arg) else: raise NotImplementedError("Solution type '{}' is not implemented yet".format(soltype)) return True
[docs] @pyaedt_function_handler() def update(self, update_dictionary=None): """Update the setup based on the class arguments or a dictionary. Parameters ---------- update_dictionary : dict, optional Dictionary of settings to apply. The default is ``None``. Returns ------- bool ``True`` when successful, ``False`` when failed. References ---------- >>> oModule.EditLinearNetworkAnalysis >>> oModule.EditDCAnalysis >>> oModule.EditTransient >>> oModule.EditQuickEyeAnalysis >>> oModule.EditVerifEyeAnalysis >>> oModule.EditAMIAnalysis """ legacy_update = self.auto_update self.auto_update = False if update_dictionary: for el in update_dictionary: self.props[el] = update_dictionary[el] arg = ["NAME:SimSetup"] soltype = SetupKeys.SetupNames[self.setuptype] _dict2arg(self.props, arg) self._setup(soltype, arg, False) self.auto_update = legacy_update return True
[docs] @pyaedt_function_handler() def add_sweep_points(self, sweep_variable="Freq", sweep_points=1, units="GHz", override_existing_sweep=True): """Add a linear count sweep to existing Circuit Setup. Parameters ---------- sweep_variable : str, optional Variable to which the sweep belongs. Default is ``"Freq``. sweep_points : float or str or list, optional Sweep points to apply linear sweep. It can be a list or single points. Points can be float or str. If ``str`` then no units will be applied. end_point float or str, optional End Point of Linear Count sweep. If ``str`` then no units will be applied. units : str, optional Sweeps Units. It will be ignored if strings are provided as start_point or end_point override_existing_sweep : bool, optional Define if existing sweep on the same variable has to be overridden or kept and added to this new sweep. Returns ------- bool ``True`` is succeeded. References ---------- >>> oModule.EditLinearNetworkAnalysis >>> oModule.EditDCAnalysis >>> oModule.EditTransient >>> oModule.EditQuickEyeAnalysis >>> oModule.EditVerifEyeAnalysis >>> oModule.EditAMIAnalysis """ if isinstance(sweep_points, (int, float)): sweep_points = [sweep_points] sweeps = [] for el in sweep_points: if isinstance(el, (int, float)): sweeps.append(str(el) + units) else: sweeps.append(el) lin_data = " ".join(sweeps) return self._add_sweep(sweep_variable, lin_data, override_existing_sweep)
[docs] @pyaedt_function_handler() def add_sweep_count( self, sweep_variable="Freq", start_point=1, end_point=100, count=100, units="GHz", count_type="Linear", override_existing_sweep=True, ): """Add a step sweep to existing Circuit Setup. It can be ``"Linear"``, ``"Decade"`` or ``"Octave"``. Parameters ---------- sweep_variable : str, optional Variable to which the sweep belongs. Default is ``"Freq``. start_point : float or str, optional Start Point of Linear Count sweep. If ``str`` then no units will be applied. end_point : float or str, optional End Point of Linear Count sweep. If ``str`` then no units will be applied. count : int, optional Number of points. Default is ``100``. units : str, optional Sweeps Units. It will be ignored if strings are provided as start_point or end_point. count_type : str, optional Count Type. Default is ``"Linear"``. It can be also ``"Decade"`` or ``"Octave"``. override_existing_sweep : bool, optional Define if existing sweep on the same variable has to be overridden or kept and added to this new sweep. Returns ------- bool ``True`` is succeeded. References ---------- >>> oModule.EditLinearNetworkAnalysis >>> oModule.EditDCAnalysis >>> oModule.EditTransient >>> oModule.EditQuickEyeAnalysis >>> oModule.EditVerifEyeAnalysis >>> oModule.EditAMIAnalysis """ if isinstance(start_point, (int, float)): start_point = str(start_point) + units if isinstance(end_point, (int, float)): end_point = str(end_point) + units lin_in = "LINC" if count_type.lower() == "decade": lin_in = "DEC" elif count_type.lower() == "octave": lin_in = "OCT" lin_data = "{} {} {} {}".format(lin_in, start_point, end_point, count) return self._add_sweep(sweep_variable, lin_data, override_existing_sweep)
[docs] @pyaedt_function_handler() def add_sweep_step( self, sweep_variable="Freq", start_point=1, end_point=100, step_size=1, units="GHz", override_existing_sweep=True, ): """Add a linear count sweep to existing Circuit Setup. Parameters ---------- sweep_variable : str, optional Variable to which the sweep belongs. Default is ``"Freq``. start_point : float or str, optional Start Point of Linear Count sweep. If ``str`` then no units will be applied. end_point : float or str, optional End Point of Linear Count sweep. If ``str`` then no units will be applied. step_size : float or str, optional Step Size of sweep. If ``str`` then no units will be applied. units : str, optional Sweeps Units. It will be ignored if strings are provided as start_point or end_point override_existing_sweep : bool, optional Define if existing sweep on the same variable has to be overridden or kept and added to this new sweep. Returns ------- bool ``True`` is succeeded. References ---------- >>> oModule.EditLinearNetworkAnalysis >>> oModule.EditDCAnalysis >>> oModule.EditTransient >>> oModule.EditQuickEyeAnalysis >>> oModule.EditVerifEyeAnalysis >>> oModule.EditAMIAnalysis """ if isinstance(start_point, (int, float)): start_point = str(start_point) + units if isinstance(end_point, (int, float)): end_point = str(end_point) + units if isinstance(step_size, (int, float)): step_size = str(step_size) + units linc_data = "LIN {} {} {}".format(start_point, end_point, step_size) return self._add_sweep(sweep_variable, linc_data, override_existing_sweep)
@pyaedt_function_handler() def _add_sweep(self, sweep_variable, equation, override_existing_sweep): if isinstance(self.props["SweepDefinition"], list): for sw in self.props["SweepDefinition"]: if sw["Variable"] == sweep_variable: if override_existing_sweep: sw["Data"] = equation else: sw["Data"] += " " + equation return self.update() elif self.props["SweepDefinition"]["Variable"] == sweep_variable: if override_existing_sweep: self.props["SweepDefinition"]["Data"] = equation else: self.props["SweepDefinition"]["Data"] += " " + equation return self.update() if isinstance(self.props["SweepDefinition"], (OrderedDict, dict)): self.props["SweepDefinition"] = [self.props["SweepDefinition"]] prop = OrderedDict({"Variable": sweep_variable, "Data": equation, "OffsetF1": False, "Synchronize": 0}) self.props["SweepDefinition"].append(prop) return self.update() @pyaedt_function_handler() def _expression_cache( self, expression_list, report_type_list, intrinsics_list, isconvergence_list, isrelativeconvergence, conv_criteria, ): """Retrieve data from the expression setup cache. Parameters ---------- expressions_list : list List of formulas to retrieve. report_type_list : list List of report types for the expressions. intrinsics_list : list List of intrinsic functions for the expressions. isconvergence_list : list List of Boolean values indicating whether the expressions are in the convergence criteria. isrelativeconvergence : conv_criteria: Returns ------- list List of the data. """ if isrelativeconvergence: userelative = 1 else: userelative = 0 list_data = ["NAME:ExpressionCache"] if type(expression_list) is list: i = 0 while i < len(expression_list): expression = expression_list[i] name = expression.replace("(", "_") + "1" name = name.replace(")", "_") name = name.replace(" ", "_") if type(report_type_list) is list: report_type = report_type_list[i] else: report_type = report_type_list if type(isconvergence_list) is list: isconvergence = isconvergence_list[i] else: isconvergence = isconvergence_list if type(intrinsics_list) is list: intrinsics = intrinsics_list[i] else: intrinsics = intrinsics_list list_data.append( [ "NAME:CacheItem", "Title:=", name, "Expression:=", expression, "Intrinsics:=", intrinsics, "IsConvergence:=", isconvergence, "UseRelativeConvergence:=", 1, "MaxConvergenceDelta:=", 1, "MaxConvergeValue:=", "0.01", "ReportType:=", report_type, ["NAME:ExpressionContext"], ] ) i += 1 else: name = expression_list.replace("(", "") + "1" name = name.replace(")", "") name = name.replace(" ", "") name = name.replace(",", "_") list_data.append( [ "NAME:CacheItem", "Title:=", name, "Expression:=", expression_list, "Intrinsics:=", intrinsics_list, "IsConvergence:=", isconvergence_list, "UseRelativeConvergence:=", userelative, "MaxConvergenceDelta:=", conv_criteria, "MaxConvergeValue:=", str(conv_criteria), "ReportType:=", report_type_list, ["NAME:ExpressionContext"], ] ) return list_data
[docs] @pyaedt_function_handler() def enable_expression_cache( self, expressions, report_type="Fields", intrinsics="", isconvergence=True, isrelativeconvergence=True, conv_criteria=1, ): """Enable a setup expression cache. Parameters ---------- expressions : str or list One or more formulas to add to the expression cache. report_type : str or list, optional Type of the report for the expression. The default is ``"Fields"``. If a list of expressions is supplied, a corresponding list of report types must be supplied. intrinsics : str or list, optional Intrinsic functions for the expressions. The default is ``""``. If a list of expressions is supplied, a corresponding list of intrinsic expressesions must be supplied. isconvergence : bool, str, or list, optional Whether the expression is in the convergence criteria. The default is ``True``. If a list of expressions is supplied, a corresponding list of Boolean values must be supplied. isrelativeconvergence : bool, optional The default is ``True``. conv_criteria The default is ``1``. Returns ------- bool ``True`` when successful, ``False`` when failed. References ---------- >>> oModule.EditSetup """ arg = ["Name:SimSetup"] _dict2arg(self.props, arg) expression_cache = self._expression_cache( expressions, report_type, intrinsics, isconvergence, isrelativeconvergence, conv_criteria ) arg.append(expression_cache) self.omodule.EditSetup(self.name, arg) return True
[docs] @pyaedt_function_handler() def add_derivatives(self, derivative_list): """Add derivatives to the setup. Parameters ---------- derivative_list : list List of derivatives. Returns ------- bool ``True`` when successful, ``False`` when failed. References ---------- >>> oModule.EditSetup """ arg = ["Name:SimSetup"] _dict2arg(self.props, arg) arg.append("VariablesForDerivatives:=") arg.append(derivative_list) self.omodule.EditSetup(self.name, arg) return True
[docs] @pyaedt_function_handler() def enable(self, setup_name=None): """Enable a setup. Parameters ---------- setup_name : str, optional Name of the setup. The default is ``None``. Returns ------- bool ``True`` when successful, ``False`` when failed. References ---------- >>> oModule.EditSetup """ if not setup_name: setup_name = self.name self._odesign.EnableSolutionSetup(setup_name, True) return True
[docs] @pyaedt_function_handler() def disable(self, setup_name=None): """Disable a setup. Parameters ---------- setup_name : str, optional Name of the setup. The default is ``None``. Returns ------- bool ``True`` when successful, ``False`` when failed. References ---------- >>> oModule.EditSetup """ if not setup_name: setup_name = self.name self._odesign.EnableSolutionSetup(setup_name, False) return True
[docs]class Setup3DLayout(object): """Initializes, creates, and updates a 3D Layout setup. Parameters ---------- app : :class:`pyaedt.application.Analysis3DLayout.FieldAnalysis3DLayout` Inherited app object. solutiontype : int or str Type of the setup. setupname : str, optional Name of the setup. The default is ``"MySetupAuto"``. isnewsetup : bool, optional Whether to create the setup from a template. The default is ``True.`` If ``False``, access is to the existing setup. """ @property def omodule(self): """Analysis module. Returns ------- type Analysis module. """ return self._app.oanalysis def __init__(self, app, solutiontype, setupname="MySetupAuto", isnewsetup=True): self.auto_update = False self._app = app if not solutiontype: self._solutiontype = self._app.design_solutions.default_setup elif isinstance(solutiontype, int): self._solutiontype = solutiontype else: self._solutiontype = self._app.design_solutions._solution_options[solutiontype]["default_setup"] self.name = setupname self.props = OrderedDict() self.sweeps = [] if isnewsetup: setup_template = SetupKeys.SetupTemplates[self._solutiontype] for t in setup_template: _tuple2dict(t, self.props) self.props = SetupProps(self, self.props) else: try: setups_data = self._app.design_properties["Setup"]["Data"] if setupname in setups_data: setup_data = setups_data[setupname] if "Data" in setup_data: # 0 and 7 represent setup HFSSDrivenAuto app = setup_data["Data"] for el in app: if isinstance(app[el], (OrderedDict, dict)): self.sweeps.append(SweepHFSS3DLayout(self.omodule, setupname, el, props=app[el])) self.props = SetupProps(self, OrderedDict(setup_data)) except: self.props = SetupProps(self, OrderedDict()) self.auto_update = True @property def setup_type(self): """Setup type. Returns ------- type Setup type. """ if "SolveSetupType" in self.props: return self.props["SolveSetupType"] else: return None
[docs] @pyaedt_function_handler() def create(self): """Add a new setup based on class settings in AEDT. Returns ------- bool ``True`` when successful, ``False`` when failed. References ---------- >>> oModule.Add """ arg = ["NAME:" + self.name] _dict2arg(self.props, arg) self.omodule.Add(arg) return True
[docs] @pyaedt_function_handler() def update(self, update_dictionary=None): """Update the setup based on the class arguments or a dictionary. Parameters ---------- update_dictionary : dict, optional Dictionary of settings to apply. Returns ------- bool ``True`` when successful, ``False`` when failed. References ---------- >>> oModule.Edit """ if update_dictionary: for el in update_dictionary: self.props._setitem_without_update(el, update_dictionary[el]) arg = ["NAME:" + self.name] _dict2arg(self.props, arg) self.omodule.Edit(self.name, arg) return True
[docs] @pyaedt_function_handler() def enable(self): """Enable a setup. Parameters ---------- setup_name : str, optional Name of the setup. Returns ------- bool ``True`` when successful, ``False`` when failed. References ---------- >>> oModule.Edit """ self.props["Properties"]["Enable"] = "true" self.update() return True
[docs] @pyaedt_function_handler() def disable(self): """Disable a setup. Parameters ---------- setup_name : str, optional Name of the setup. Returns ------- bool ``True`` when successful, ``False`` when failed. References ---------- >>> oModule.Edit """ self.props["Properties"]["Enable"] = "false" self.update() return True
[docs] @pyaedt_function_handler() def export_to_hfss(self, file_fullname): """Export the project to a file. Parameters ---------- file_fullname : str Full path and file name for exporting the project. Returns ------- bool ``True`` when successful, ``False`` when failed. References ---------- >>> oModule.ExportToHfss """ file_fullname = os.path.normpath(file_fullname) if not os.path.isdir(os.path.dirname(file_fullname)): return False file_fullname = os.path.splitext(file_fullname)[0] + ".aedt" self.omodule.ExportToHfss(self.name, file_fullname) return True
[docs] @pyaedt_function_handler() def add_sweep(self, sweepname=None, sweeptype="Interpolating"): """Add a frequency sweep. Parameters ---------- sweepname : str, optional Name of the sweep. The default is ``None``. sweeptype : str, optional Type of the sweep. Options are ``"Interpolating"`` and ``"Discrete"``. The default is ``"Interpolating"``. Returns ------- :class:`pyaedt.modules.SetupTemplates.SweepHFSS3DLayout` Sweep object. References ---------- >>> oModule.AddSweep """ if not sweepname: sweepname = generate_unique_name("Sweep") sweep_n = SweepHFSS3DLayout(self.omodule, self.name, sweepname, sweeptype) if sweep_n.create(): self.sweeps.append(sweep_n) return sweep_n return False
class SetupHFSS(Setup, object): """Initializes, creates, and updates an HFSS setup. Parameters ---------- app : :class:`pyaedt.application.Analysis3D.FieldAnalysis3D` Inherited app object. solutiontype : int, str Type of the setup. setupname : str, optional Name of the setup. The default is ``"MySetupAuto"``. isnewsetup : bool, optional Whether to create the setup from a template. The default is ``True``. If ``False``, access is to the existing setup. """ def __init__(self, app, solutiontype, setupname="MySetupAuto", isnewsetup=True): Setup.__init__(self, app, solutiontype, setupname, isnewsetup) @pyaedt_function_handler() def create_linear_count_sweep( self, unit, freqstart, freqstop, num_of_freq_points, sweepname=None, save_fields=True, save_rad_fields=False, sweep_type="Discrete", interpolation_tol=0.5, interpolation_max_solutions=250, ): """Create a sweep with the specified number of points. Parameters ---------- unit : str Frequency Units. freqstart : float Starting frequency of the sweep, such as ``1``. freqstop : float Stopping frequency of the sweep. num_of_freq_points : int Number of frequency points in the range. sweepname : str, optional Name of the sweep. The default is ``None``. save_fields : bool, optional Whether to save the fields. The default is ``True``. save_rad_fields : bool, optional Whether to save the radiating fields. The default is ``False``. sweep_type : str, optional Type of the sweep. Options are ``"Fast"``, ``"Interpolating"``, and ``"Discrete"``. The default is ``"Discrete"``. interpolation_tol : float, optional Error tolerance threshold for the interpolation process. The default is ``0.5``. interpolation_max_solutions : int, optional Maximum number of solutions evaluated for the interpolation process. The default is ``250``. Returns ------- :class:`pyaedt.modules.SetupTemplates.SweepHFSS` or bool Sweep object if successful, ``False`` otherwise. References ---------- >>> oModule.InsertFrequencySweep Examples -------- Create a setup named ``"LinearCountSetup"`` and use it in a linear count sweep named ``"LinearCountSweep"``. >>> setup = hfss.create_setup("LinearCountSetup") >>> linear_count_sweep = hfss.create_linear_count_sweep(setupname="LinearCountSetup", ... sweepname="LinearCountSweep", ... unit="MHz", freqstart=1.1e3, ... freqstop=1200.1, num_of_freq_points=1658) >>> type(linear_count_sweep) <class 'pyaedt.modules.SetupTemplates.SweepHFSS'> """ if sweep_type not in ["Discrete", "Interpolating", "Fast"]: raise AttributeError("Invalid in `sweep_type`. It has to be either 'Discrete', 'Interpolating', or 'Fast'") if sweepname is None: sweepname = generate_unique_name("Sweep") if sweepname in [sweep.name for sweep in self.sweeps]: oldname = sweepname sweepname = generate_unique_name(oldname) self.logger.warning("Sweep %s is already present. Sweep has been renamed in %s.", oldname, sweepname) sweepdata = self.add_sweep(sweepname, sweep_type) if not sweepdata: return False sweepdata.props["RangeType"] = "LinearCount" sweepdata.props["RangeStart"] = str(freqstart) + unit sweepdata.props["RangeEnd"] = str(freqstop) + unit sweepdata.props["RangeCount"] = num_of_freq_points sweepdata.props["Type"] = sweep_type if sweep_type == "Interpolating": sweepdata.props["InterpTolerance"] = interpolation_tol sweepdata.props["InterpMaxSolns"] = interpolation_max_solutions sweepdata.props["InterpMinSolns"] = 0 sweepdata.props["InterpMinSubranges"] = 1 sweepdata.props["SaveFields"] = save_fields sweepdata.props["SaveRadFields"] = save_rad_fields sweepdata.update() self.logger.info("Linear count sweep {} has been correctly created".format(sweepname)) return sweepdata @pyaedt_function_handler() def create_linear_step_sweep( self, setupname, unit, freqstart, freqstop, step_size, sweepname=None, save_fields=True, save_rad_fields=False, sweep_type="Discrete", ): """Create a Sweep with a specified frequency step. References ---------- >>> oModule.InsertFrequencySweep Parameters ---------- setupname : str Name of the setup. unit : str Unit of the frequency. For example, ``"MHz`` or ``"GHz"``. freqstart : float Starting frequency of the sweep. freqstop : float Stopping frequency of the sweep. step_size : float Frequency size of the step. sweepname : str, optional Name of the sweep. The default is ``None``. save_fields : bool, optional Whether to save the fields. The default is ``True``. save_rad_fields : bool, optional Whether to save the radiating fields. The default is ``False``. sweep_type : str, optional Whether to create a ``"Discrete"``,``"Interpolating"`` or ``"Fast"`` sweep. The default is ``"Discrete"``. Returns ------- :class:`pyaedt.modules.SetupTemplates.SweepHFSS` or bool Sweep object if successful, ``False`` otherwise. Examples -------- Create a setup named ``"LinearStepSetup"`` and use it in a linear step sweep named ``"LinearStepSweep"``. >>> setup = hfss.create_setup("LinearStepSetup") >>> linear_step_sweep = hfss.create_linear_step_sweep(setupname="LinearStepSetup", ... sweepname="LinearStepSweep", ... unit="MHz", freqstart=1.1e3, ... freqstop=1200.1, step_size=153.8) >>> type(linear_step_sweep) <class 'pyaedt.modules.SetupTemplates.SweepHFSS'> """ if sweep_type not in ["Discrete", "Interpolating", "Fast"]: raise AttributeError("Invalid in `sweep_type`. It has to either 'Discrete', 'Interpolating', or 'Fast'") if sweepname is None: sweepname = generate_unique_name("Sweep") if setupname not in self.setup_names: return False for s in self.setups: if s.name == setupname: setupdata = s if sweepname in [sweep.name for sweep in setupdata.sweeps]: oldname = sweepname sweepname = generate_unique_name(oldname) self.logger.warning( "Sweep %s is already present. Sweep has been renamed in %s.", oldname, sweepname ) sweepdata = setupdata.add_sweep(sweepname, sweep_type) if not sweepdata: return False sweepdata.props["RangeType"] = "LinearStep" sweepdata.props["RangeStart"] = str(freqstart) + unit sweepdata.props["RangeEnd"] = str(freqstop) + unit sweepdata.props["RangeStep"] = str(step_size) + unit sweepdata.props["SaveFields"] = save_fields sweepdata.props["SaveRadFields"] = save_rad_fields sweepdata.props["ExtrapToDC"] = False sweepdata.props["Type"] = sweep_type if sweep_type == "Interpolating": sweepdata.props["InterpTolerance"] = 0.5 sweepdata.props["InterpMaxSolns"] = 250 sweepdata.props["InterpMinSolns"] = 0 sweepdata.props["InterpMinSubranges"] = 1 sweepdata.update() self.logger.info("Linear step sweep {} has been correctly created".format(sweepname)) return sweepdata return False @pyaedt_function_handler() def create_single_point_sweep( self, setupname, unit, freq, sweepname=None, save_single_field=True, save_fields=False, save_rad_fields=False, ): """Create a Sweep with a single frequency point. Parameters ---------- setupname : str Name of the setup. unit : str Unit of the frequency. For example, ``"MHz`` or ``"GHz"``. freq : float, list Frequency of the single point or list of frequencies to create distinct single points. sweepname : str, optional Name of the sweep. The default is ``None``. save_single_field : bool, list, optional Whether to save the fields of the single point. The default is ``True``. If a list is specified, the length must be the same as freq length. save_fields : bool, optional Whether to save the fields for all points and subranges defined in the sweep. The default is ``False``. save_rad_fields : bool, optional Whether to save only the radiating fields. The default is ``False``. Returns ------- :class:`pyaedt.modules.SetupTemplates.SweepHFSS` or bool Sweep object if successful, ``False`` otherwise. References ---------- >>> oModule.InsertFrequencySweep Examples -------- Create a setup named ``"LinearStepSetup"`` and use it in a single point sweep named ``"SinglePointSweep"``. >>> setup = hfss.create_setup("LinearStepSetup") >>> single_point_sweep = hfss.create_single_point_sweep(setupname="LinearStepSetup", ... sweepname="SinglePointSweep", ... unit="MHz", freq=1.1e3) >>> type(single_point_sweep) <class 'pyaedt.modules.SetupTemplates.SweepHFSS'> """ if sweepname is None: sweepname = generate_unique_name("SinglePoint") if isinstance(save_single_field, list): if not isinstance(freq, list) or len(save_single_field) != len(freq): raise AttributeError("The length of save_single_field must be the same as freq length.") add_subranges = False if isinstance(freq, list): if not freq: raise AttributeError("Frequency list is empty! Specify at least one frequency point.") freq0 = freq.pop(0) if freq: add_subranges = True else: freq0 = freq if isinstance(save_single_field, list): save0 = save_single_field.pop(0) else: save0 = save_single_field if add_subranges: save_single_field = [save0] * len(freq) if setupname not in self.setup_names: return False for s in self.setups: if s.name == setupname: setupdata = s if sweepname in [sweep.name for sweep in setupdata.sweeps]: oldname = sweepname sweepname = generate_unique_name(oldname) self.logger.warning( "Sweep %s is already present. Sweep has been renamed in %s.", oldname, sweepname ) sweepdata = setupdata.add_sweep(sweepname, "Discrete") sweepdata.props["RangeType"] = "SinglePoints" sweepdata.props["RangeStart"] = str(freq0) + unit sweepdata.props["RangeEnd"] = str(freq0) + unit sweepdata.props["SaveSingleField"] = save0 sweepdata.props["SaveFields"] = save_fields sweepdata.props["SaveRadFields"] = save_rad_fields sweepdata.props["SMatrixOnlySolveMode"] = "Auto" if add_subranges: for f, s in zip(freq, save_single_field): sweepdata.add_subrange(rangetype="SinglePoints", start=f, unit=unit, save_single_fields=s) sweepdata.update() self.logger.info("Single point sweep {} has been correctly created".format(sweepname)) return sweepdata return False