Source code for ansys.aedt.toolkits.radar_explorer.backend.api
# -*- coding: utf-8 -*-
#
# Copyright (C) 2024 - 2025 ANSYS, Inc. and/or its affiliates.
# SPDX-License-Identifier: Apache-2.0
#
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os
from pathlib import Path
import sys
import time
sys.path.append(str(Path(__file__).parent))
import gc # noqa: I001
import numpy as np
import pyvista as pv
from ansys.aedt.core.generic.file_utils import generate_unique_name
from ansys.aedt.core.visualization.post.rcs_exporter import MonostaticRCSExporter
from ansys.aedt.toolkits.common.backend.api import AEDTCommon
from ansys.aedt.toolkits.radar_explorer.backend.models import properties
from ansys.aedt.toolkits.radar_explorer.backend.rcs_utils.domain_transforms import DomainTransforms
[docs]
class ToolkitBackend(AEDTCommon):
"""API to control the toolkit workflow.
This class provides methods to connect to a selected design and create geometries.
Examples
--------
>>> from ansys.aedt.toolkits.radar_explorer.backend.api import ToolkitBackend
>>> toolkit_api = ToolkitBackend()
>>> toolkit_api.launch_aedt()
>>> toolkit_api.wait_to_be_idle()
"""
def __init__(self):
"""Initialize the ``toolkit`` class."""
AEDTCommon.__init__(self, properties)
self.properties = properties
self.multiplier = 1.0
[docs]
def update_rcs_properties(self, range_is_system=True, azimuth_is_system=True, elevation_is_system=True):
"""Update radar setup properties for RCS.
Parameters
----------
range_is_system : bool, default:`` True``
Update range properties system based.
azimuth_is_system : bool, default: ``True``
Update azimuth properties system based.
elevation_is_system : bool, default: ``True``
Update elevation properties system based.
"""
# Range
self.__update_range_properties(range_is_system)
# Azimuth
self.__update_azimuth_properties(azimuth_is_system)
# Elevation
self.__update_elevation_properties(elevation_is_system)
[docs]
def update_range_profile_properties(self, is_system=True):
"""Update radar setup properties for range profile.
Parameters
----------
is_system : bool, default: ``True``
Update system to performance properties.
"""
self.__update_range_properties(is_system)
[docs]
def update_waterfall_properties(self, range_is_system=True, azimuth_is_system=True):
"""Update radar setup properties for waterfall.
Parameters
----------
range_is_system : bool, default: ``True``
Update system to performance properties.
azimuth_is_system : bool, default: ``True``
Update azimuth properties system based.
"""
# Range
self.__update_range_properties(range_is_system)
# Azimuth
self.__update_azimuth_properties(azimuth_is_system)
[docs]
def update_isar_2d_properties(self, range_is_system=True, azimuth_is_system=True):
"""Update radar setup properties for the range profile.
Parameters
----------
range_is_system : bool, default: ``True``
Update system to performance properties.
azimuth_is_system : bool, default: ``True``
Update azimuth properties system based.
"""
# Range
self.__update_range_properties(range_is_system)
# Azimuth
self.__update_azimuth_properties(azimuth_is_system)
@staticmethod
def __update_range_properties(is_system=True):
center_freq_hz = properties.setup.center_freq
if is_system:
# The definition of bandwidth includes the df/2 tails at the extrema of the interval, like in ADP
bandwidth_hz = properties.setup.fft_bandwidth
num_freq = properties.setup.num_freq
delta_freq = bandwidth_hz / num_freq
num_freq_step = num_freq - 1
upper_half_bw = num_freq_step // 2 * delta_freq
lower_half_bw = upper_half_bw if num_freq % 2 == 1 else upper_half_bw + delta_freq
freq_domain = center_freq_hz + np.linspace(-lower_half_bw, upper_half_bw, num=num_freq)
dt = DomainTransforms(freq_domain=freq_domain)
else:
range_max_tgt = properties.radar.range_max
range_res_tgt = properties.radar.range_res
num_range = int(np.ceil(range_max_tgt / range_res_tgt))
range_domain = np.linspace(0, range_res_tgt * (num_range - 1), num=num_range)
dt = DomainTransforms(range_domain=range_domain, center_freq=center_freq_hz)
properties.setup.center_freq = dt.center_freq
properties.setup.fft_bandwidth = dt.fft_bandwidth
properties.setup.num_freq = dt.num_freq
properties.radar.range_res = dt.range_resolution
properties.radar.range_max = dt.range_period
@staticmethod
def __update_azimuth_properties(is_system=True):
center_freq_hz = properties.setup.center_freq
if is_system:
aspect_ang_phi = properties.radar.aspect_ang_phi
aspect_domain = np.linspace(-aspect_ang_phi / 2, aspect_ang_phi / 2, num=properties.radar.num_phi)
dt = DomainTransforms(aspect_domain=aspect_domain, center_freq=center_freq_hz)
properties.radar.range_res_az = dt.range_resolution
properties.radar.range_max_az = dt.range_period
else:
range_max_tgt = properties.radar.range_max_az
range_res_tgt = properties.radar.range_res_az
num_range = int(np.ceil(range_max_tgt / range_res_tgt))
if num_range == 1:
num_range = 2
range_domain = np.linspace(0, range_res_tgt * (num_range - 1), num=num_range)
dt = DomainTransforms(range_domain=range_domain, center_freq=center_freq_hz)
properties.radar.aspect_ang_phi = dt.aspect_angle
properties.radar.num_phi = dt.num_aspect_angle
@staticmethod
def __update_elevation_properties(is_system=True):
center_freq_hz = properties.setup.center_freq
if is_system:
aspect_ang_theta = properties.radar.aspect_ang_theta
aspect_domain = np.linspace(-aspect_ang_theta / 2, aspect_ang_theta / 2, num=properties.radar.num_theta)
dt = DomainTransforms(aspect_domain=aspect_domain, center_freq=center_freq_hz)
properties.radar.range_res_el = dt.range_resolution
properties.radar.range_max_el = dt.range_period
else:
range_max_tgt = properties.radar.range_max_el
range_res_tgt = properties.radar.range_res_el
num_range = int(np.ceil(range_max_tgt / range_res_tgt))
if num_range == 1:
num_range = 2
range_domain = np.linspace(0, range_res_tgt * (num_range - 1), num=num_range)
dt = DomainTransforms(range_domain=range_domain, center_freq=center_freq_hz)
properties.radar.aspect_ang_theta = dt.aspect_angle
properties.radar.num_theta = dt.num_aspect_angle
[docs]
def is_sbr_design(self):
"""Check if the design is the SBR+ solution type.
Returns
-------
bool
Returns ``True`` if it is an SBR+ design, ``False`` otherwise.
"""
self.connect_design()
is_sbr = False
self.logger.info(self.properties.active_design)
self.logger.info(self.aedtapp.solution_type)
if self.aedtapp:
if self.aedtapp.solution_type == "SBR+":
is_sbr = True
else: # pragma: no cover
self.logger.error("Toolkit cannot connect to AEDT.")
self.release_aedt(False, False)
return is_sbr
[docs]
def generate_3d_component(self):
"""Generate a 3D component from current design.
Returns
-------
str or bool
Returns ``True`` if the connection is successful, ``False`` otherwise.
Examples
--------
>>> from ansys.aedt.toolkits.radar_explorer.backend.api import ToolkitBackend
>>> toolkit_api = ToolkitBackend()
>>> toolkit_api.launch_aedt()
>>> toolkit_api.wait_to_be_idle()
>>> toolkit_api.generate_3d_component()
"""
self.connect_design()
if self.aedtapp:
self.aedtapp.solution_type = "SBR+"
# Export design to 3D Component
file_folder = self.aedtapp.working_directory
base_name = "geo"
ext = ".a3dcomp"
file_name = f"{base_name}{ext}"
component_file = Path(file_folder) / file_name
increment = 1
while component_file.is_file():
file_name = f"{base_name}_{increment}{ext}"
component_file = Path(file_folder) / file_name
increment += 1
# Get available 3D Components
comp_defs = self.aedtapp.modeler.user_defined_component_names
obj_names = self.aedtapp.modeler.object_names
is_created = False
if len(comp_defs) == 0 and obj_names: # pragma: no cover
self.aedtapp.modeler.create_3dcomponent(input_file=str(component_file), excitations=[])
if component_file.is_file():
is_created = True
else:
active_project = self.get_project_name(self.properties.active_project)
is_flattened = self.aedtapp.flatten_3d_components(purge_history=False)
if self.properties.non_graphical:
# In non-graphical mode it is necessary to set the active project and design
self.desktop.odesktop.SetActiveProject(active_project)
self.release_aedt(False, False)
self.connect_design()
if not is_flattened: # pragma: no cover
self.logger.error("PyAEDT could not flatten the design, remove 3D Components manually.")
self.release_aedt(False, False)
return False
is_created = self.aedtapp.modeler.create_3dcomponent(
input_file=str(component_file), excitations=[], variables_to_include=[]
)
if is_created:
self.logger.info("3D component created.")
self.release_aedt(False, False)
return component_file
else: # pragma: no cover
self.logger.error("3D Component not created.")
self.release_aedt(False, False)
return False
else: # pragma: no cover
self.logger.error("Toolkit can not connect to AEDT.")
self.release_aedt(False, False)
return False
[docs]
def insert_sbr_design(self, input_file, name=None):
"""Insert SBR+ design and insert a component if passed.
Parameters
----------
input_file : str
Path of the component file.
name : str, default: ``None``
Design name.
Returns
-------
str
Design name.
Examples
--------
>>> from ansys.aedt.toolkits.radar_explorer.backend.api import ToolkitBackend
>>> toolkit_api = ToolkitBackend()
>>> toolkit_api.launch_aedt()
>>> toolkit_api.wait_to_be_idle()
>>> toolkit_api.insert_sbr_design()
"""
self.connect_design()
if self.aedtapp:
if not name:
name = generate_unique_name(self.aedtapp.design_name)
self.aedtapp.insert_design(name=name, solution_type="SBR+")
design_name = self.aedtapp.design_name
self.properties.active_design = design_name
self.save_project(release_aedt=False)
# Set active design after reload project
self.properties.active_design = design_name
self.aedtapp.modeler.model_units = properties.cad.model_units
self.logger.info(f"SBR design inserted: {self.properties.active_design}.")
self.logger.debug("Add coordinate system parameters.")
self.aedtapp["rot_ang1"] = f"{properties.radar.rotation_ang1}deg"
self.aedtapp["rot_ang2"] = f"{properties.radar.rotation_ang2}deg"
self.aedtapp["rot_ang3"] = f"{properties.radar.rotation_ang3}deg"
cs = self.aedtapp.modeler.create_coordinate_system(
mode=properties.radar.rotation_order.lower(), phi="rot_ang1", theta="rot_ang2", psi="rot_ang3"
)
self.aedtapp.modeler.insert_3d_component(input_file=input_file, coordinate_system=cs.name)
self.logger.info("3D Component inserted.")
self.save_project(release_aedt=True, project_path=self.properties.active_project)
# Set active design after reload project
self.properties.active_design = design_name
return design_name
else: # pragma: no cover
self.logger.error("Toolkit can not connect to AEDT.")
self.release_aedt(False, False)
return False
[docs]
def insert_cad_sbr(self, name=None):
"""Insert CAD in the SBR+ design.
Parameters
----------
name : str, default: ``None``
New design name.
Returns
-------
str
Design name.
"""
if not properties.cad.input_file: # pragma: no cover
self.logger.error("No CAD files added.")
return False
cad_files = properties.cad.input_file
materials = properties.cad.material
positions = properties.cad.position
if properties.cad.material and len(materials) != len(cad_files):
self.logger.error("Number of materials must be the same as in CAD files.")
return False
elif not materials:
materials = ["pec"] * len(cad_files)
if properties.cad.position and len(positions) != len(cad_files):
self.logger.error("Number of positions must be the same as in CAD files.")
return False
elif not positions:
positions = [[0, 0, 0]] * len(cad_files)
self.connect_design()
if self.aedtapp:
self.aedtapp.modeler.model_units = properties.cad.model_units
if not name:
name = generate_unique_name(self.aedtapp.design_name)
if name in self.aedtapp.design_list: # pragma: no cover
name = self.aedtapp._generate_unique_design_name(name)
self.aedtapp.insert_design(name=name, solution_type="SBR+")
self.aedtapp.modeler.model_units = properties.cad.model_units
design_name = self.aedtapp.design_name
self.properties.active_design = design_name
self.save_project(release_aedt=False)
# Set active design after reload project
self.properties.active_design = design_name
self.logger.info(f"SBR design inserted: {self.properties.active_design}.")
# Insert CAD
for cont_file, cad_file in enumerate(cad_files):
cad_file = Path(cad_file)
material = materials[cont_file]
position = positions[cont_file]
path = Path(self.aedtapp.toolkit_directory)
file = cad_file.stem
extension = cad_file.suffix
new_file_name = file + ".stl"
new_file_name_obj = file + ".obj"
new_file_full_path = path / new_file_name
new_file_full_path_obj = path / new_file_name_obj
# Convert to STL
if extension == ".obj":
reader = pv.get_reader(cad_file)
obj_mesh = reader.read()
obj_mesh.save(new_file_full_path)
cad_file = new_file_full_path
elif extension == ".gltf" or extension == ".glb":
pl = pv.Plotter()
pl.import_gltf(cad_file)
pl.export_obj(new_file_full_path_obj)
reader = pv.get_reader(new_file_full_path_obj)
obj_mesh = reader.read()
obj_mesh.save(new_file_full_path)
cad_file = new_file_full_path
new_extension = cad_file.suffix
old_model_objects = self.aedtapp.modeler.model_objects
if new_extension == ".stl" and cad_file.is_file():
self.aedtapp.modeler.import_3d_cad(str(cad_file), create_lightweigth_part=True)
else: # pragma: no cover
self.logger.error("Wrong CAD format.")
self.release_aedt(False, False)
return False
new_models = self.aedtapp.modeler.model_objects
new_model_objects = [solid for solid in new_models if solid not in old_model_objects]
for new_model in new_model_objects:
model_object = self.aedtapp.modeler[new_model]
if model_object.object_type == "Sheet":
if material == "pec":
self.aedtapp.assign_perfecte_to_sheets(model_object)
else:
self.aedtapp.assign_finite_conductivity(model_object, material=material)
elif model_object.object_type == "Solid": # pragma: no cover
model_object.material_name = material
model_object.move(position)
# Replace 3D Component
self.aedtapp.modeler.replace_3dcomponent(name="rcs_scenario", assignment=self.aedtapp.modeler.model_objects)
self.logger.info("3D Component inserted.")
self.logger.debug("Add coordinate system parameters.")
self.aedtapp["rot_ang1"] = f"{properties.radar.rotation_ang1}deg"
self.aedtapp["rot_ang2"] = f"{properties.radar.rotation_ang2}deg"
self.aedtapp["rot_ang3"] = f"{properties.radar.rotation_ang3}deg"
cs = self.aedtapp.modeler.create_coordinate_system(
mode=properties.radar.rotation_order.lower(), phi="rot_ang1", theta="rot_ang2", psi="rot_ang3"
)
component_name = self.aedtapp.modeler.user_defined_component_names[0]
component = self.aedtapp.modeler.user_defined_components[component_name]
component.target_coordinate_system = cs.name
self.save_project(release_aedt=True, project_path=self.properties.active_project)
# Set active design after reload project
self.properties.active_design = design_name
return design_name
else: # pragma: no cover
self.logger.error("Toolkit cannot connect to AEDT.")
self.release_aedt(False, False)
return False
[docs]
def duplicate_sbr_design(self, name=None):
"""Duplicate an existing SBR+ design.
Parameters
----------
name : str, default: ``None``
New design name.
Returns
-------
str
Design name.
"""
self.connect_design()
if self.aedtapp:
self.aedtapp.duplicate_design(self.aedtapp.design_name)
self.save_project(release_aedt=False)
self.logger.info("SBR design duplicated.")
# Check if Native solids exist to convert them to 3D Components
solids_in_components = []
for model_object in self.aedtapp.modeler.user_defined_components.values():
for part in model_object.parts:
solids_in_components.append(part)
# Convert native object to 3D Component
solids_to_component = []
for model_part in self.aedtapp.modeler.objects:
if model_part not in solids_in_components:
solids_to_component.append(self.aedtapp.modeler.objects[model_part].name)
if solids_to_component:
_ = self.aedtapp.modeler.replace_3dcomponent(assignment=solids_to_component)
self.logger.debug("Add coordinate system parameters.")
self.aedtapp["rot_ang1"] = f"{properties.radar.rotation_ang1}deg"
self.aedtapp["rot_ang2"] = f"{properties.radar.rotation_ang2}deg"
self.aedtapp["rot_ang3"] = f"{properties.radar.rotation_ang3}deg"
cs = self.aedtapp.modeler.create_coordinate_system(
mode=properties.radar.rotation_order.lower(), phi="rot_ang1", theta="rot_ang2", psi="rot_ang3"
)
for model_object in self.aedtapp.modeler.user_defined_components.values():
model_object.target_coordinate_system = cs.name
if name:
self.aedtapp.design_name = name
design_name = self.aedtapp.design_name
self.save_project(release_aedt=True)
return design_name
else: # pragma: no cover
self.logger.error("Toolkit can not connect to AEDT.")
self.release_aedt(False, False)
return False
[docs]
def add_plane_wave(self, name="IncPWave1", polarization="Vertical"):
"""Insert plane wave.
Parameters
----------
name : str, default: ``None``
Name of the plane wave.
polarization : str, default: ``"Vertical"``
Polarization type. Options are ``"Horizontal"``
and ``"Vertical"``.
Returns
-------
str
Plane wave name.
Examples
--------
>>> from ansys.aedt.toolkits.radar_explorer.backend.api import ToolkitBackend
>>> toolkit_api = ToolkitBackend()
>>> toolkit_api.launch_aedt()
>>> toolkit_api.wait_to_be_idle()
>>> toolkit_api.add_plane_wave()
"""
self.connect_design()
if self.aedtapp:
calc_type = properties.radar.calculation_type
if calc_type == "Range Profile": # pragma: no cover
phi_start = 0
phi_stop = 0
num_phi = 1
theta_start = 90
theta_stop = 90
num_theta = 1
elif calc_type == "2D ISAR": # pragma: no cover
phi_start = -properties.radar.aspect_ang_phi / 2
phi_stop = +properties.radar.aspect_ang_phi / 2
num_phi = properties.radar.num_phi
theta_start = 90
theta_stop = 90
num_theta = 1
else:
phi_start = -properties.radar.aspect_ang_phi / 2
phi_stop = +properties.radar.aspect_ang_phi / 2
num_phi = properties.radar.num_phi
theta_start = -properties.radar.aspect_ang_theta / 2 + 90
theta_stop = +properties.radar.aspect_ang_theta / 2 + 90
num_theta = properties.radar.num_theta
e_theta = {"Vertical": 1, "Horizontal": 0}
e_phi = {"Vertical": 0, "Horizontal": 1}
propagation_vector = [
[str(phi_start) + "deg", str(phi_stop) + "deg", num_phi],
[str(theta_start) + "deg", str(theta_stop) + "deg", num_theta],
]
plane_wave = self.aedtapp.plane_wave(
vector_format="Spherical",
origin=None,
polarization=[e_phi[polarization], e_theta[polarization]],
propagation_vector=propagation_vector,
wave_type="Propagating",
wave_type_properties=None,
name=name,
)
plane_wave_name = plane_wave.name
if not properties.setup.plane_wave_names:
properties.setup.plane_wave_names = plane_wave_name
else:
properties.setup.plane_wave_names += ", " + plane_wave_name
self.logger.info(f"Plane wave {plane_wave_name} created.")
self.release_aedt(False, False)
return plane_wave_name
else: # pragma: no cover
self.logger.error("Toolkit can not connect to AEDT.")
self.release_aedt(False, False)
return False
[docs]
def add_setup(self, name=None):
"""Insert setup.
Parameters
----------
name : str, default: ``None``
Name of the setup.
Returns
-------
str
Setup name.
Examples
--------
>>> from ansys.aedt.toolkits.radar_explorer.backend.api import ToolkitBackend
>>> toolkit_api = ToolkitBackend()
>>> toolkit_api.launch_aedt()
>>> toolkit_api.wait_to_be_idle()
>>> toolkit_api.add_sbr_setup()
"""
self.connect_design()
if self.aedtapp:
if not name:
name = properties.setup.setup_name
ray_density = properties.setup.ray_density
num_bounces = properties.setup.num_bounces
ffl = properties.setup.ffl
ptd_utd = properties.setup.ptd_utd
num_freq = properties.setup.num_freq
# note that simulation frequency is not fft frequency
start_freq_ghz = properties.setup.sim_freq_lower * 1e-9
end_freq_ghz = properties.setup.sim_freq_upper * 1e-9
ptd_option = None
if ptd_utd:
ptd_option = "PTD Correction + UTD Rays"
setup = self.aedtapp.create_setup(
name=name,
MaxNumberOfBounces=num_bounces,
RayDensityPerWavelength=ray_density,
FastFrequencyLooping=ffl,
PTDUTDSimulationSettings=ptd_option,
)
setup.auto_update = False
setup.props["ComputeFarFields"] = True
setup.props["IsMonostaticRCS"] = True
setup.update()
setup.props["Sweeps"]["Sweep"]["RangeType"] = "LinearCount"
setup.props["Sweeps"]["Sweep"]["RangeStart"] = str(start_freq_ghz) + "GHz"
setup.props["Sweeps"]["Sweep"]["RangeEnd"] = str(end_freq_ghz) + "GHz"
setup.props["Sweeps"]["Sweep"]["RangeCount"] = num_freq
setup.update()
setup_name = setup.name
properties.setup.setup_name = setup_name
if len(self.aedtapp.modeler.coordinate_systems) != 0:
# Update CS
cs = self.aedtapp.modeler.coordinate_systems[0]
if cs.props["Mode"] != "Axis/Position":
if properties.radar.rotation_order == "ZYZ":
cs.props["Mode"] = "Euler Angle ZYZ"
else:
cs.props["Mode"] = "Euler Angle ZXZ"
self.aedtapp["rot_ang1"] = f"{properties.radar.rotation_ang1}deg"
self.aedtapp["rot_ang2"] = f"{properties.radar.rotation_ang2}deg"
self.aedtapp["rot_ang3"] = f"{properties.radar.rotation_ang3}deg"
self.logger.info(f"Setup {setup_name} created.")
self.release_aedt(False, False)
return setup_name
else: # pragma: no cover
self.logger.error("Toolkit can not connect to AEDT.")
self.release_aedt(False, False)
return False
[docs]
def analyze(self): # pragma: no cover
"""Analyze the design.
Launch analysis. AEDT is released once it is opened.
Returns
-------
bool
``True`` when successful, ``False`` when failed.
Examples
--------
>>> import time
>>> from ansys.aedt.toolkits.radar.backend.api import ToolkitBackend
>>> toolkit = ToolkitBackend()
>>> msg1 = toolkit_api.launch_thread(toolkit.launch_aedt)
>>> idle = toolkit_api.wait_to_be_idle()
>>> toolkit.analyze()
"""
self.connect_design()
if self.aedtapp:
num_cores = properties.setup.num_cores
self.aedtapp.save_project()
if not self.properties.setup.solve_interactive and not self.properties.non_graphical: # pragma: no cover
active_design = self.properties.active_design
self.release_aedt(True, True)
self.properties.selected_process = 0
self.properties.non_graphical = True
self.launch_aedt()
self.open_project()
self.properties.active_design = active_design
self.connect_design()
self.aedtapp.analyze(cores=num_cores, blocking=False)
while True:
if not self.aedtapp.are_there_simulations_running:
break
else:
time.sleep(1)
gc.collect()
self.release_aedt(False, False)
return True
else: # pragma: no cover
self.logger.error("Toolkit can not connect to AEDT.")
self.release_aedt(False, False)
return False
[docs]
def export_rcs(self, excitation=None, expression=None, encode=False):
"""Get RCS data.
Parameters
----------
excitation : str, default: ``None``
Excitation name.
expression : str, default: ``None``
Expression name.
encode : bool, default: ``False``
Whether to encode the file.
Returns
-------
list or str
Metadata file path.
"""
self.connect_design()
if self.aedtapp:
if expression or excitation:
setup_name = properties.setup.setup_name
sweep_name = properties.setup.sweep_name
setup_sweep_name = f"{setup_name} : {sweep_name}"
variations = self.aedtapp.available_variations.nominal_w_values_dict_w_dependent
excitations = self.aedtapp.excitations
if len(excitations) > 1:
if excitation == excitations[0]:
self.aedtapp.edit_sources(assignment={excitations[0]: "1", excitations[1]: "0"})
else:
self.aedtapp.edit_sources(assignment={excitations[0]: "0", excitations[1]: "1"})
rcs_data = self.aedtapp.post.get_solution_data(
expressions=expression,
variations=variations,
setup_sweep_name=setup_sweep_name,
report_category="Monostatic RCS",
)
frequencies = None
if rcs_data and getattr(rcs_data, "primary_sweep_values", None) is not None:
frequencies = list(rcs_data.primary_sweep_values)
rcs = MonostaticRCSExporter(
self.aedtapp,
setup_name=setup_sweep_name,
frequencies=frequencies,
expression=expression,
)
if excitation == "IncWaveHpol":
if expression == "ComplexMonostaticRCSTheta": # pragma: no cover
metadata_name = "VH"
data_name = "VH_data"
else:
metadata_name = "HH"
data_name = "HH_data"
else:
if expression == "ComplexMonostaticRCSTheta":
metadata_name = "VV"
data_name = "VV_data"
else:
metadata_name = "HV"
data_name = "HV_data"
rcs.column_name = metadata_name
rcs.export_rcs(name=data_name, metadata_name=metadata_name)
if encode:
encoded_json_file = None
encoded_geometry_files = []
metadata_file = rcs.metadata_file
metadata_dir = Path(metadata_file).parent
if Path(metadata_file).is_file():
serialized_file = self.serialize_obj_base64(metadata_file)
encoded_json_file = serialized_file.decode("utf-8")
geometry_path = (metadata_dir / "geometry").resolve()
if geometry_path.exists():
for root, _, files in os.walk(geometry_path):
for file in files:
if file.lower().endswith(".obj"):
geometry_file = (Path(root) / file).resolve()
serialized_file = self.serialize_obj_base64(geometry_file)
encoded_geometry_files.append(serialized_file.decode("utf-8"))
data_file = Path(rcs.data_file).resolve()
serialized_file = self.serialize_obj_base64(data_file)
encoded_rcs_file = serialized_file.decode("utf-8")
return encoded_json_file, encoded_geometry_files, encoded_rcs_file
self.release_aedt(False, False)
return rcs.metadata_file
else:
rcs = MonostaticRCSExporter(self.aedtapp)
rcs.export_rcs(name="Geometry", metadata_name="geo", only_geometry=True)
if encode:
encoded_json_file = None
encoded_geometry_files = []
metadata_file = rcs.metadata_file
metadata_dir = Path(metadata_file).parent
if Path(metadata_file).is_file():
serialized_file = self.serialize_obj_base64(metadata_file)
encoded_json_file = serialized_file.decode("utf-8")
geometry_path = (metadata_dir / "geometry").resolve()
if geometry_path.exists():
for root, _, files in os.walk(geometry_path):
for file in files:
if file.lower().endswith(".obj"):
geometry_file = (Path(root) / file).resolve()
serialized_file = self.serialize_obj_base64(geometry_file)
encoded_geometry_files.append(serialized_file.decode("utf-8"))
encoded_rcs_file = None
self.release_aedt(False, False)
return encoded_json_file, encoded_geometry_files, encoded_rcs_file
self.release_aedt(False, False)
return rcs.metadata_file
else: # pragma: no cover
self.logger.error("Toolkit cannot connect to AEDT.")
self.release_aedt(False, False)
return False
[docs]
def get_setups(self):
"""Get setups."""
if not self.aedtapp:
self.connect_design()
available_setups = []
if self.aedtapp:
available_setups = self.aedtapp.setup_names
self.release_aedt(False, False)
return available_setups
[docs]
def get_sweeps(self):
"""Get sweeps."""
if not self.aedtapp:
self.connect_design()
sweeps = ["Sweep"]
if self.aedtapp and not self.aedtapp.solution_type == "SBR+" and properties.setup.setup_name != "No Setup":
setup = self.aedtapp.get_setup(properties.setup.setup_name)
sweeps = ["LastAdaptive"]
if setup:
setup_sweeps = setup.get_sweep_names()
sweeps.extend(setup_sweeps)
self.release_aedt(False, False)
return sweeps
[docs]
def get_plane_waves(self):
"""Get plane waves."""
if not self.aedtapp:
self.connect_design()
available_setups = []
if self.aedtapp:
available_setups = self.aedtapp.excitations
self.release_aedt(False, False)
return available_setups
[docs]
def get_materials(self):
"""Get available materials."""
if not self.aedtapp:
self.connect_design()
final_materials = []
if self.aedtapp:
materials = self.aedtapp.materials.mat_names_aedt
materials = [m for m in materials if not m.startswith("$")]
filtered = list(dict.fromkeys(materials))
final_materials = sorted(filtered, key=lambda x: (x.lower() != "pec", x[0].isupper(), x.lower(), x))
self.release_aedt(False, False)
return final_materials