Source code for kqcircuits.simulations.export.ansys.ansys_solution

# This code is part of KQCircuits
# Copyright (C) 2024 IQM Finland Oy
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
# License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with this program. If not, see
# https://www.gnu.org/licenses/gpl-3.0.html.
#
# The software distribution should follow IQM trademark policy for open-source software
# (meetiqm.com/iqm-open-source-trademark-policy). IQM welcomes contributions to the code.
# Please see our contribution agreements for individuals (meetiqm.com/iqm-individual-contributor-license-agreement)
# and organizations (meetiqm.com/iqm-organization-contributor-license-agreement).
from dataclasses import dataclass
from typing import ClassVar
from kqcircuits.simulations.export.solution import Solution


[docs] @dataclass(kw_only=True, frozen=True) class AnsysSolution(Solution): """ A Base class for Ansys solution parameters Args: percent_refinement: Percentage of mesh refinement on each iteration. maximum_passes: Maximum number of iterations in simulation. minimum_passes: Minimum number of iterations in simulation. minimum_converged_passes: Determines how many iterations have to meet the stopping criterion to stop simulation. frequency_units: Units of frequency. mesh_size: Dictionary to determine manual mesh refinement on layers. Set key as the layer name and value as the maximal mesh element length inside the layer. simulation_flags: Optional export processing, given as list of strings. See Simulation Export in docs. ansys_project_template: path to the simulation template """ ansys_tool: ClassVar[str] = "" percent_refinement: float = 30.0 maximum_passes: int = 12 minimum_passes: int = 1 minimum_converged_passes: int = 1 frequency_units: str = "GHz" mesh_size: dict | None = None simulation_flags: list[str] | None = None ansys_project_template: str | None = None
[docs] def get_solution_data(self): """Return the solution data in dictionary form.""" return { "solution_name": self.name, "ansys_tool": self.ansys_tool, "analysis_setup": { "percent_refinement": self.percent_refinement, "maximum_passes": self.maximum_passes, "minimum_passes": self.minimum_passes, "minimum_converged_passes": self.minimum_converged_passes, "frequency_units": self.frequency_units, }, "mesh_size": {} if self.mesh_size is None else self.mesh_size, "simulation_flags": [] if self.simulation_flags is None else self.simulation_flags, **({} if self.ansys_project_template is None else {"ansys_project_template": self.ansys_project_template}), }
[docs] @dataclass(kw_only=True, frozen=True) class AnsysHfssSolution(AnsysSolution): """ Class for Ansys S-parameter (HFSS) solution parameters Args: frequency: Frequency for mesh refinement. To set up multifrequency analysis in HFSS use list of numbers. max_delta_s: Stopping criterion in HFSS simulation. sweep_enabled: Determines if HFSS frequency sweep is enabled. sweep_start: The lowest frequency in the sweep. sweep_end: The highest frequency in the sweep. sweep_count: Number of frequencies in the sweep. sweep_type: choices are "interpolating", "discrete" or "fast" capacitance_export: If True, the capacitance matrices are exported from S-parameter simulation integrate_energies: Calculate energy integrals over each layer and save them into a file integrate_magnetic_flux: Integrate magnetic fluxes through each non-pec sheet and save them into a file """ ansys_tool: ClassVar[str] = "hfss" frequency: float | list[float] = 5 max_delta_s: float = 0.1 sweep_enabled: bool = True sweep_start: float = 0 sweep_end: float = 10 sweep_count: int = 101 sweep_type: str = "interpolating" capacitance_export: bool = False integrate_energies: bool = False integrate_magnetic_flux: bool = False
[docs] def get_solution_data(self): """Return the solution data in dictionary form.""" data = super().get_solution_data() return { **data, "analysis_setup": { **data["analysis_setup"], "frequency": self.frequency, "max_delta_s": self.max_delta_s, "sweep_enabled": self.sweep_enabled, "sweep_start": self.sweep_start, "sweep_end": self.sweep_end, "sweep_count": self.sweep_count, "sweep_type": self.sweep_type, }, "capacitance_export": self.capacitance_export, "integrate_energies": self.integrate_energies, "integrate_magnetic_flux": self.integrate_magnetic_flux, }
[docs] @dataclass(kw_only=True, frozen=True) class AnsysQ3dSolution(AnsysSolution): """ Class for Ansys capacitance matrix (Q3D) solution parameters Args: frequency: Nominal solution frequency (has no effect on capacitance matrix at the moment). percent_error: Stopping criterion in Q3D simulation. """ ansys_tool: ClassVar[str] = "q3d" frequency: float = 5 percent_error: float = 1
[docs] def get_solution_data(self): """Return the solution data in dictionary form.""" data = super().get_solution_data() return { **data, "analysis_setup": { **data["analysis_setup"], "frequency": self.frequency, "percent_error": self.percent_error, }, }
[docs] @dataclass(kw_only=True, frozen=True) class AnsysEigenmodeSolution(AnsysSolution): """ Class for Ansys eigenmode solution parameters Args: min_frequency: Minimum allowed eigenmode frequency max_delta_f: Maximum allowed relative difference in eigenfrequency (%) n_modes: Number of eigenmodes to solve. integrate_energies: Calculate energy integrals over each layer and save them into a file integrate_magnetic_flux: Integrate magnetic fluxes through each non-pec sheet and save them into a file """ ansys_tool: ClassVar[str] = "eigenmode" min_frequency: float = 0.1 max_delta_f: float = 0.1 n_modes: int = 2 integrate_energies: bool = False integrate_magnetic_flux: bool = False
[docs] def get_solution_data(self): """Return the solution data in dictionary form.""" data = super().get_solution_data() return { **data, "analysis_setup": { **data["analysis_setup"], "min_frequency": self.min_frequency, "max_delta_f": self.max_delta_f, "n_modes": self.n_modes, }, "integrate_energies": self.integrate_energies, "integrate_magnetic_flux": self.integrate_magnetic_flux, }
[docs] @dataclass(kw_only=True, frozen=True) class AnsysCurrentSolution(AnsysSolution): """ Class for Ansys current excitation solution parameters Args: frequency: Frequency of alternating current excitation. max_delta_e: Stopping criterion in current excitation simulation. integrate_energies: Calculate energy integrals over each layer and save them into a file integrate_magnetic_flux: Integrate magnetic fluxes through each non-pec sheet and save them into a file """ ansys_tool: ClassVar[str] = "current" frequency: float = 0.1 max_delta_e: float = 0.1 integrate_energies: bool = False integrate_magnetic_flux: bool = False
[docs] def get_solution_data(self): """Return the solution data in dictionary form.""" data = super().get_solution_data() return { **data, "analysis_setup": { **data["analysis_setup"], "frequency": self.frequency, "max_delta_e": self.max_delta_e, }, "integrate_energies": self.integrate_energies, "integrate_magnetic_flux": self.integrate_magnetic_flux, }
[docs] @dataclass(kw_only=True, frozen=True) class AnsysVoltageSolution(AnsysSolution): """ Class for Ansys voltage excitation solution parameters Args: frequency: Frequency of alternating voltage excitation. max_delta_e: Stopping criterion in voltage excitation simulation. integrate_energies: Calculate energy integrals over each layer and save them into a file integrate_magnetic_flux: Integrate magnetic fluxes through each non-pec sheet and save them into a file """ ansys_tool: ClassVar[str] = "voltage" frequency: float = 5 max_delta_e: float = 0.1 integrate_energies: bool = False integrate_magnetic_flux: bool = False
[docs] def get_solution_data(self): """Return the solution data in dictionary form.""" data = super().get_solution_data() return { **data, "analysis_setup": { **data["analysis_setup"], "frequency": self.frequency, "max_delta_e": self.max_delta_e, }, "integrate_energies": self.integrate_energies, "integrate_magnetic_flux": self.integrate_magnetic_flux, }
[docs] @dataclass(kw_only=True, frozen=True) class AnsysCrossSectionSolution(AnsysSolution): """ Class for Ansys cross-section solution parameters. Produces capacitance and inductance per unit length. Args: frequency: Nominal solution frequency (has no effect on results at the moment). percent_error: Stopping criterion in cross-section simulation. integrate_energies: Calculate energy integrals over each layer and save them into a file """ ansys_tool: ClassVar[str] = "cross-section" frequency: float = 5 percent_error: float = 0.01 integrate_energies: bool = False
[docs] def get_solution_data(self): """Return the solution data in dictionary form.""" data = super().get_solution_data() return { **data, "analysis_setup": { **data["analysis_setup"], "frequency": self.frequency, "percent_error": self.percent_error, }, "integrate_energies": self.integrate_energies, }
[docs] def get_ansys_solution(ansys_tool="hfss", **solution_params): """Returns an instance of AnsysSolution subclass. Args: ansys_tool: Determines the subclass of AnsysSolution. solution_params: Arguments passed for AnsysSolution subclass. """ for c in [ AnsysHfssSolution, AnsysQ3dSolution, AnsysEigenmodeSolution, AnsysCurrentSolution, AnsysVoltageSolution, AnsysCrossSectionSolution, ]: if ansys_tool == c.ansys_tool: return c(**solution_params) raise ValueError(f"No AnsysSolution found for ansys_tool={ansys_tool}.")