-
Notifications
You must be signed in to change notification settings - Fork 73
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactor sim validation and add unit test example
- Loading branch information
Showing
5 changed files
with
172 additions
and
93 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
120 changes: 120 additions & 0 deletions
120
klayout_package/python/kqcircuits/simulations/export/simulation_validate.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
# 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). | ||
|
||
class ValidateSim(): | ||
"""Validation class that contains consistency checks.""" | ||
|
||
def __init__(self): | ||
pass | ||
|
||
def has_no_ports_when_required(self, simulation, solution): | ||
"""Validation check: ensures that a simulation object has ports when the solution type requires it. | ||
Args: | ||
simulations: A Simulation object. | ||
solutions: A Solution object. | ||
Raises: | ||
Errors when validation criteria are not met. | ||
""" | ||
port_names = get_port_names(simulation) | ||
solution_type = get_solution_type(solution) | ||
sim_name = simulation.name | ||
if not port_names and solution_type in [ | ||
"AnsysHfssSolution", | ||
"AnsysVoltageSolution", | ||
"AnsysCurrentSolution", | ||
"ElmerVectorHelmholtzSolution", | ||
"ElmerCapacitanceSolution", | ||
]: | ||
raise ValidateSimError(f"Simulation '{sim_name}' has no ports assigned. This is incompatible with {solution_type}") | ||
|
||
def has_edgeport_when_forbidden(self, simulation, solution): | ||
"""Validation check: ensure that if at least one "EdgePort" is present, some solution types can't be chosen. | ||
Args: | ||
simulations: A Simulation object. | ||
solutions: A Solution object. | ||
Raises: | ||
Errors when validation criteria are not met. | ||
""" | ||
port_names = get_port_names(simulation) | ||
solution_type = get_solution_type(solution) | ||
sim_name = simulation.name | ||
if "EdgePort" in port_names and solution_type in [ | ||
"AnsysEigenmodeSolution", | ||
"AnsysVoltageSolution", | ||
"AnsysCurrentSolution", | ||
]: | ||
raise ValidateSimError( | ||
f"Simulation '{sim_name}' has at least one 'EdgePort'. This is incompatible with {solution_type}" | ||
) | ||
|
||
|
||
def flux_integration_layer_exists_if_needed(self, simulation, solution): | ||
"""Validation check related to the presence of layers and magnetic flux integration. | ||
Args: | ||
simulation: A Simulation object. | ||
Raises: | ||
Errors when validation criteria are not met. | ||
""" | ||
sim_name = simulation.name | ||
has_integrate_flux = hasattr(solution, "integrate_magnetic_flux") | ||
integrate_flux = solution.integrate_magnetic_flux if has_integrate_flux else False | ||
|
||
# Ensures that a layer with thickness == 0 and a material != "pec" exsists in the setup when "integrate_magnetic_flux" is True. | ||
if integrate_flux: | ||
layers = simulation.layers | ||
has_flux_integration_layer = False | ||
for layer_name in layers: | ||
if "thickness" in layers[layer_name] and "material" in layers[layer_name]: | ||
if layers[layer_name]["thickness"] == 0 and layers[layer_name]["material"] != "pec": | ||
has_flux_integration_layer = True | ||
break | ||
if not has_flux_integration_layer: | ||
raise ValidateSimError( | ||
f"Simulation '{sim_name}' has 'integrate_magnetic_flux = True' but the integration layer is missing." | ||
) | ||
|
||
|
||
def get_port_names(simulation): | ||
"""Helper function that returns a list of port names in a Simulation object. | ||
Args: | ||
simulation: A Simulation object. | ||
Returns: | ||
port_names: A list of names related to the ports present in simulation. | ||
""" | ||
port_list = simulation.ports | ||
port_names = [] | ||
for port in port_list: | ||
port_names.append(type(port).__name__) | ||
return port_names | ||
|
||
|
||
def get_solution_type(solution): | ||
"""Helper function that returns the solution type name of a Solution object. | ||
Args: | ||
solution: A Solution object. | ||
Returns: | ||
A string containing the name of the solution type of simulation. | ||
""" | ||
return type(solution).__name__ | ||
|
||
|
||
class ValidateSimError(Exception): | ||
"""Custom exception class for specific error handling.""" | ||
def __init__(self, message, errors=None): | ||
super().__init__(message) | ||
self.errors = errors |
41 changes: 41 additions & 0 deletions
41
tests/simulations/simulation_validate/test_simulation_validate.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
# 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 kqcircuits.simulations.export.simulation_validate import ValidateSim | ||
from kqcircuits.simulations.simulation import Simulation | ||
from kqcircuits.pya_resolver import pya | ||
from kqcircuits.simulations.port import InternalPort | ||
from kqcircuits.util.export_helper import ( | ||
get_active_or_new_layout, | ||
) | ||
from kqcircuits.simulations.export.ansys.ansys_solution import AnsysCurrentSolution, AnsysHfssSolution, AnsysQ3dSolution, AnsysEigenmodeSolution | ||
from kqcircuits.simulations.export.elmer.elmer_solution import ElmerVectorHelmholtzSolution, ElmerCapacitanceSolution, ElmerCrossSectionSolution | ||
|
||
class MockSimulation(Simulation): | ||
def build(self): | ||
pass | ||
self.ports.append(InternalPort(0, *self.etched_line(pya.DPoint(0, 0), pya.DPoint(1, 1)))) | ||
layout = get_active_or_new_layout() | ||
simulation = MockSimulation(layout) | ||
|
||
def test_has_no_ports_when_required(): | ||
simulation.ports = [InternalPort(0, *simulation.etched_line(pya.DPoint(0, 0), pya.DPoint(1, 1)))] | ||
simulation.name = "test_sim" | ||
solution = AnsysCurrentSolution() | ||
validator = ValidateSim() | ||
validator.has_no_ports_when_required(simulation, solution) |