diff --git a/pyaedt/application/Analysis3D.py b/pyaedt/application/Analysis3D.py index 04c72fcb8f0..48ab8586985 100644 --- a/pyaedt/application/Analysis3D.py +++ b/pyaedt/application/Analysis3D.py @@ -151,6 +151,8 @@ def post(self): if self._post is None: if is_ironpython: # pragma: no cover from pyaedt.modules.PostProcessor import PostProcessor + elif self.design_type == "Icepak": + from pyaedt.modules.AdvancedPostProcessing import IcepakPostProcessor as PostProcessor else: from pyaedt.modules.AdvancedPostProcessing import PostProcessor self._post = PostProcessor(self) diff --git a/pyaedt/icepak.py b/pyaedt/icepak.py index a77e55c9e93..ca1f661140b 100644 --- a/pyaedt/icepak.py +++ b/pyaedt/icepak.py @@ -35,7 +35,6 @@ from pyaedt.modules.Boundary import NativeComponentObject from pyaedt.modules.Boundary import NetworkObject from pyaedt.modules.Boundary import _create_boundary -from pyaedt.modules.PostProcessor import IcepakPostProcessor from pyaedt.modules.monitor_icepak import Monitor @@ -168,7 +167,6 @@ def __init__( ) self._monitor = Monitor(self) self._configurations = ConfigurationsIcepak(self) - self._post = IcepakPostProcessor(self) def _init_from_design(self, *args, **kwargs): self.__init__(*args, **kwargs) diff --git a/pyaedt/modules/AdvancedPostProcessing.py b/pyaedt/modules/AdvancedPostProcessing.py index d0a8c97aa0f..ab7d1a0c2e6 100644 --- a/pyaedt/modules/AdvancedPostProcessing.py +++ b/pyaedt/modules/AdvancedPostProcessing.py @@ -6,14 +6,20 @@ from __future__ import absolute_import # noreorder +import csv import os +import re import warnings +from pyaedt import generate_unique_name +from pyaedt import settings from pyaedt.generic.general_methods import is_ironpython from pyaedt.generic.general_methods import open_file from pyaedt.generic.general_methods import pyaedt_function_handler from pyaedt.generic.plot import ModelPlotter +from pyaedt.modules.PostProcessor import FieldSummary from pyaedt.modules.PostProcessor import PostProcessor as Post +from pyaedt.modules.PostProcessor import TOTAL_QUANTITIES if not is_ironpython: try: @@ -1005,3 +1011,342 @@ def plot_scene( scene.convert_fields_in_db = convert_fields_in_db scene.log_multiplier = log_multiplier scene.animate() + + +class IcepakPostProcessor(PostProcessor, object): + def __init__(self, app): + PostProcessor.__init__(self, app) + + @pyaedt_function_handler() + def create_field_summary(self): + return FieldSummary(self._app) + + @pyaedt_function_handler() + def get_fans_operating_point(self, export_file=None, setup_name=None, timestep=None, design_variation=None): + """ + Get the operating point of the fans in the design. + + Parameters + ---------- + export_file : str, optional + Name of the file to save the operating point of the fans to. The default is + ``None``, in which case the filename is automatically generated. + setup_name : str, optional + Setup name to determine the operating point of the fans. The default is + ``None``, in which case the first available setup is used. + timestep : str, optional + Time, with units, at which to determine the operating point of the fans. The default + is ``None``, in which case the first available timestep is used. This parameter is + only relevant in transient simulations. + design_variation : str, optional + Design variation to determine the operating point of the fans from. The default is + ``None``, in which case the nominal variation is used. + + Returns + ------- + list + First element of the list is the CSV filename. The second and third elements + are the quantities with units describing the operating point of the fans. + The fourth element is a dictionary with the names of the fan instances + as keys and lists with volumetric flow rates and pressure rise floats associated + with the operating point as values. + + References + ---------- + + >>> oModule.ExportFanOperatingPoint + + Examples + -------- + >>> from pyaedt import Icepak + >>> ipk = Icepak() + >>> ipk.create_fan() + >>> filename, vol_flow_name, p_rise_name, op_dict= ipk.get_fans_operating_point() + """ + + if export_file is None: + path = self._app.temp_directory + base_name = "{}_{}_FanOpPoint".format(self._app.project_name, self._app.design_name) + export_file = os.path.join(path, base_name + ".csv") + while os.path.exists(export_file): + file_name = generate_unique_name(base_name) + export_file = os.path.join(path, file_name + ".csv") + if setup_name is None: + setup_name = "{} : {}".format(self._app.get_setups()[0], self._app.solution_type) + if timestep is None: + timestep = "" + if self._app.solution_type == "Transient": + self._app.logger.warning("No timestep is specified. First timestep is exported.") + else: + if not self._app.solution_type == "Transient": + self._app.logger.warning("Simulation is steady-state. Timestep argument is ignored.") + timestep = "" + if design_variation is None: + design_variation = "" + self._app.osolution.ExportFanOperatingPoint( + [ + "SolutionName:=", + setup_name, + "DesignVariationKey:=", + design_variation, + "ExportFilePath:=", + export_file, + "Overwrite:=", + True, + "TimeStep:=", + timestep, + ] + ) + with open(export_file, "r") as f: + reader = csv.reader(f) + for line in reader: + if "Fan Instances" in line: + vol_flow = line[1] + p_rise = line[2] + break + var = {line[0]: [float(line[1]), float(line[2])] for line in reader} + return [export_file, vol_flow, p_rise, var] + + @pyaedt_function_handler + def _parse_field_summary_content(self, fs, setup_name, design_variation, quantity_name): + content = fs.get_field_summary_data(sweep_name=setup_name, design_variation=design_variation) + pattern = r"\[([^]]*)\]" + match = re.search(pattern, content["Quantity"][0]) + if match: + content["Unit"] = [match.group(1)] + else: # pragma: no cover + content["Unit"] = [None] + + if quantity_name in TOTAL_QUANTITIES: + return {i: content[i][0] for i in ["Total", "Unit"]} + return {i: content[i][0] for i in ["Min", "Max", "Mean", "Stdev", "Unit"]} + + @pyaedt_function_handler() + def evaluate_faces_quantity( + self, + faces_list, + quantity_name, + side="Default", + setup_name=None, + design_variation=None, + ref_temperature="", + ): + """Export the field surface output. + + Parameters + ---------- + faces_list : list + List of faces to apply. + quantity_name : str + Name of the quantity to export. + side : str, optional + Which side of the mesh face to use. The default is ``Default``. + Options are ``"Adjacent"``, ``"Combined"``, and ``"Default"``. + setup_name : str, optional + Name of the setup and name of the sweep. For example, ``"IcepakSetup1 : SteatyState"``. + The default is ``None``, in which case the active setup and active sweep are used. + design_variation : dict, optional + Dictionary of parameters defined for the specific setup with values. The default is ``{}``. + ref_temperature: str, optional + Reference temperature to use for heat transfer coefficient computation. The default is ``""``. + + Returns + ------- + dict + Output dictionary, which depending on the quantity chosen, contains one + of these sets of keys: + + - ``"Min"``, ``"Max"``, ``"Mean"``, ``"Stdev"``, and ``"Unit"`` + - ``"Total"`` and ``"Unit"`` + + References + ---------- + + >>> oModule.ExportFieldsSummary + """ + if design_variation is None: + design_variation = {} + name = generate_unique_name(quantity_name) + self._app.modeler.create_face_list(faces_list, name) + fs = self.create_field_summary() + fs.add_calculation("Object", "Surface", name, quantity_name, side=side, ref_temperature=ref_temperature) + return self._parse_field_summary_content(fs, setup_name, design_variation, quantity_name) + + @pyaedt_function_handler() + def evaluate_boundary_quantity( + self, + boundary_name, + quantity_name, + side="Default", + volume=False, + setup_name=None, + design_variation=None, + ref_temperature="", + ): + """Export the field output on a boundary. + + Parameters + ---------- + boundary_name : str + Name of boundary to perform the computation on. + quantity_name : str + Name of the quantity to export. + side : str, optional + Side of the mesh face to use. The default is ``"Default"``. + Options are ``"Adjacent"``, ``"Combined"``, and ``"Default"``. + volume : bool, optional + Whether to compute the quantity on the volume or on the surface. + The default is ``False``, in which case the quantity will be evaluated + only on the surface . + setup_name : str, optional + Name of the setup and name of the sweep. For example, ``"IcepakSetup1 : SteatyState"``. + The default is ``None``, in which case the active setup and active sweep are used. + design_variation : dict, optional + Dictionary of parameters defined for the specific setup with values. The default is ``{}``. + ref_temperature: str, optional + Reference temperature to use for heat transfer coefficient computation. The default is ``""``. + + Returns + ------- + dict + Output dictionary, which depending on the quantity chosen, contains one + of these sets of keys: + - ``"Min"``, ``"Max"``, ``"Mean"``, ``"Stdev"``, and ``"Unit"`` + - ``"Total"`` and ``"Unit"`` + + References + ---------- + + >>> oModule.ExportFieldsSummary + """ + if design_variation is None: + design_variation = {} + fs = self.create_field_summary() + fs.add_calculation( + "Boundary", + ["Surface", "Volume"][int(volume)], + boundary_name, + quantity_name, + side=side, + ref_temperature=ref_temperature, + ) + return self._parse_field_summary_content(fs, setup_name, design_variation, quantity_name) + + @pyaedt_function_handler() + def evaluate_monitor_quantity( + self, + monitor_name, + quantity_name, + side="Default", + setup_name=None, + design_variation=None, + ref_temperature="", + ): + """Export monitor field output. + + Parameters + ---------- + monitor_name : str + Name of monitor to perform the computation on. + quantity_name : str + Name of the quantity to export. + side : str, optional + Side of the mesh face to use. The default is ``"Default"``. + Options are ``"Adjacent"``, ``"Combined"``, and ``"Default"``. + setup_name : str, optional + Name of the setup and name of the sweep. For example, ``"IcepakSetup1 : SteatyState"``. + The default is ``None``, in which case the active setup and active sweep are used. + design_variation : dict, optional + Dictionary of parameters defined for the specific setup with values. The default is ``{}``. + ref_temperature: str, optional + Reference temperature to use for heat transfer coefficient computation. The default is ``""``. + + Returns + ------- + dict + Output dictionary, which depending on the quantity chosen, contains one + of these sets of keys: + + - ``"Min"``, ``"Max"``, ``"Mean"``, ``"Stdev"``, and ``"Unit"`` + - ``"Total"`` and ``"Unit"`` + + References + ---------- + + >>> oModule.ExportFieldsSummary + """ + if design_variation is None: + design_variation = {} + if settings.aedt_version < "2024.1": + raise NotImplementedError("Monitors are not supported in field summary in versions earlier than 2024 R1.") + else: # pragma: no cover + if self._app.monitor.face_monitors.get(monitor_name, None): + field_type = "Surface" + elif self._app.monitor.point_monitors.get(monitor_name, None): + field_type = "Volume" + else: + raise AttributeError("Monitor {} is not found in the design.".format(monitor_name)) + fs = self.create_field_summary() + fs.add_calculation( + "Monitor", field_type, monitor_name, quantity_name, side=side, ref_temperature=ref_temperature + ) + return self._parse_field_summary_content(fs, setup_name, design_variation, quantity_name) + + @pyaedt_function_handler() + def evaluate_object_quantity( + self, + object_name, + quantity_name, + side="Default", + volume=False, + setup_name=None, + design_variation=None, + ref_temperature="", + ): + """Export the field output on or in an object. + + Parameters + ---------- + object_name : str + Name of object to perform the computation on. + quantity_name : str + Name of the quantity to export. + side : str, optional + Side of the mesh face to use. The default is ``"Default"``. + Options are ``"Adjacent"``, ``"Combined"``, and ``"Default"``. + volume : bool, optional + Whether to compute the quantity on the volume or on the surface. The default is ``False``. + setup_name : str, optional + Name of the setup and name of the sweep. For example, ``"IcepakSetup1 : SteatyState"``. + The default is ``None``, in which case the active setup and active sweep are used. + design_variation : dict, optional + Dictionary of parameters defined for the specific setup with values. The default is ``{}``. + ref_temperature: str, optional + Reference temperature to use for heat transfer coefficient computation. The default is ``""``. + + Returns + ------- + dict + Output dictionary, which depending on the quantity chosen, contains one + of these sets of keys: + + - ``"Min"``, ``"Max"``, ``"Mean"``, ``"Stdev"``, and ``"Unit"`` + - ``"Total"`` and ``"Unit"`` + + References + ---------- + + >>> oModule.ExportFieldsSummary + """ + if design_variation is None: + design_variation = {} + fs = self.create_field_summary() + fs.add_calculation( + "Boundary", + ["Surface", "Volume"][int(volume)], + object_name, + quantity_name, + side=side, + ref_temperature=ref_temperature, + ) + return self._parse_field_summary_content(fs, setup_name, design_variation, quantity_name) diff --git a/pyaedt/modules/PostProcessor.py b/pyaedt/modules/PostProcessor.py index 78cb34f82f0..030ee475012 100644 --- a/pyaedt/modules/PostProcessor.py +++ b/pyaedt/modules/PostProcessor.py @@ -13,7 +13,6 @@ import csv import os import random -import re import string import tempfile @@ -5038,334 +5037,3 @@ def _create_field_summary(self, setup, variation): arg.append("Calculation:=") arg.append(i) self._app.osolution.EditFieldsSummarySetting(arg) - - -class IcepakPostProcessor(PostProcessor, object): - def __init__(self, app): - PostProcessorCommon.__init__(self, app) - - @pyaedt_function_handler() - def create_field_summary(self): - return FieldSummary(self._app) - - @pyaedt_function_handler() - def get_fans_operating_point(self, export_file=None, setup_name=None, timestep=None, design_variation=None): - """ - Get the operating point of the fans in the design. - - Parameters - ---------- - export_file : str, optional - Name of the file to save the operating point of the fans to. The default is - ``None``, in which case the filename is automatically generated. - setup_name : str, optional - Setup name to determine the operating point of the fans. The default is - ``None``, in which case the first available setup is used. - timestep : str, optional - Time, with units, at which to determine the operating point of the fans. The default - is ``None``, in which case the first available timestep is used. This parameter is - only relevant in transient simulations. - design_variation : str, optional - Design variation to determine the operating point of the fans from. The default is - ``None``, in which case the nominal variation is used. - - Returns - ------- - list - First element of the list is the CSV filename. The second and third elements - are the quantities with units describing the operating point of the fans. - The fourth element is a dictionary with the names of the fan instances - as keys and lists with volumetric flow rates and pressure rise floats associated - with the operating point as values. - - References - ---------- - - >>> oModule.ExportFanOperatingPoint - - Examples - -------- - >>> from pyaedt import Icepak - >>> ipk = Icepak() - >>> ipk.create_fan() - >>> filename, vol_flow_name, p_rise_name, op_dict= ipk.get_fans_operating_point() - """ - - if export_file is None: - path = self._app.temp_directory - base_name = "{}_{}_FanOpPoint".format(self._app.project_name, self._app.design_name) - export_file = os.path.join(path, base_name + ".csv") - while os.path.exists(export_file): - file_name = generate_unique_name(base_name) - export_file = os.path.join(path, file_name + ".csv") - if setup_name is None: - setup_name = "{} : {}".format(self._app.get_setups()[0], self._app.solution_type) - if timestep is None: - timestep = "" - if self._app.solution_type == "Transient": - self._app.logger.warning("No timestep is specified. First timestep is exported.") - else: - if not self._app.solution_type == "Transient": - self._app.logger.warning("Simulation is steady-state. Timestep argument is ignored.") - timestep = "" - if design_variation is None: - design_variation = "" - self._app.osolution.ExportFanOperatingPoint( - [ - "SolutionName:=", - setup_name, - "DesignVariationKey:=", - design_variation, - "ExportFilePath:=", - export_file, - "Overwrite:=", - True, - "TimeStep:=", - timestep, - ] - ) - with open(export_file, "r") as f: - reader = csv.reader(f) - for line in reader: - if "Fan Instances" in line: - vol_flow = line[1] - p_rise = line[2] - break - var = {line[0]: [float(line[1]), float(line[2])] for line in reader} - return [export_file, vol_flow, p_rise, var] - - @pyaedt_function_handler - def _parse_field_summary_content(self, fs, setup_name, design_variation, quantity_name): - content = fs.get_field_summary_data(sweep_name=setup_name, design_variation=design_variation) - pattern = r"\[([^]]*)\]" - match = re.search(pattern, content["Quantity"][0]) - if match: - content["Unit"] = [match.group(1)] - else: # pragma: no cover - content["Unit"] = [None] - - if quantity_name in TOTAL_QUANTITIES: - return {i: content[i][0] for i in ["Total", "Unit"]} - return {i: content[i][0] for i in ["Min", "Max", "Mean", "Stdev", "Unit"]} - - @pyaedt_function_handler() - def evaluate_faces_quantity( - self, - faces_list, - quantity_name, - side="Default", - setup_name=None, - design_variation={}, - ref_temperature="", - ): - """Export the field surface output. - - Parameters - ---------- - faces_list : list - List of faces to apply. - quantity_name : str - Name of the quantity to export. - side : str, optional - Which side of the mesh face to use. The default is ``Default``. - Options are ``"Adjacent"``, ``"Combined"``, and ``"Default"``. - setup_name : str, optional - Name of the setup and name of the sweep. For example, ``"IcepakSetup1 : SteatyState"``. - The default is ``None``, in which case the active setup and active sweep are used. - design_variation : dict, optional - Dictionary of parameters defined for the specific setup with values. The default is ``{}``. - ref_temperature: str, optional - Reference temperature to use for heat transfer coefficient computation. The default is ``""``. - - Returns - ------- - dict - Output dictionary, which depending on the quantity chosen, contains one - of these sets of keys: - - - ``"Min"``, ``"Max"``, ``"Mean"``, ``"Stdev"``, and ``"Unit"`` - - ``"Total"`` and ``"Unit"`` - - References - ---------- - - >>> oModule.ExportFieldsSummary - """ - name = generate_unique_name(quantity_name) - self._app.modeler.create_face_list(faces_list, name) - fs = self.create_field_summary() - fs.add_calculation("Object", "Surface", name, quantity_name, side=side, ref_temperature=ref_temperature) - return self._parse_field_summary_content(fs, setup_name, design_variation, quantity_name) - - @pyaedt_function_handler() - def evaluate_boundary_quantity( - self, - boundary_name, - quantity_name, - side="Default", - volume=False, - setup_name=None, - design_variation={}, - ref_temperature="", - ): - """Export the field output on a boundary. - - Parameters - ---------- - boundary_name : str - Name of boundary to perform the computation on. - quantity_name : str - Name of the quantity to export. - side : str, optional - Side of the mesh face to use. The default is ``"Default"``. - Options are ``"Adjacent"``, ``"Combined"``, and ``"Default"``. - volume : bool, optional - Whether to compute the quantity on the volume or on the surface. - The default is ``False``, in which case the quantity will be evaluated - only on the surface . - setup_name : str, optional - Name of the setup and name of the sweep. For example, ``"IcepakSetup1 : SteatyState"``. - The default is ``None``, in which case the active setup and active sweep are used. - design_variation : dict, optional - Dictionary of parameters defined for the specific setup with values. The default is ``{}``. - ref_temperature: str, optional - Reference temperature to use for heat transfer coefficient computation. The default is ``""``. - - Returns - ------- - dict - Output dictionary, which depending on the quantity chosen, contains one - of these sets of keys: - - ``"Min"``, ``"Max"``, ``"Mean"``, ``"Stdev"``, and ``"Unit"`` - - ``"Total"`` and ``"Unit"`` - - References - ---------- - - >>> oModule.ExportFieldsSummary - """ - fs = self.create_field_summary() - fs.add_calculation( - "Boundary", - ["Surface", "Volume"][int(volume)], - boundary_name, - quantity_name, - side=side, - ref_temperature=ref_temperature, - ) - return self._parse_field_summary_content(fs, setup_name, design_variation, quantity_name) - - @pyaedt_function_handler() - def evaluate_monitor_quantity( - self, - monitor_name, - quantity_name, - side="Default", - setup_name=None, - design_variation={}, - ref_temperature="", - ): - """Export monitor field output. - - Parameters - ---------- - monitor_name : str - Name of monitor to perform the computation on. - quantity_name : str - Name of the quantity to export. - side : str, optional - Side of the mesh face to use. The default is ``"Default"``. - Options are ``"Adjacent"``, ``"Combined"``, and ``"Default"``. - setup_name : str, optional - Name of the setup and name of the sweep. For example, ``"IcepakSetup1 : SteatyState"``. - The default is ``None``, in which case the active setup and active sweep are used. - design_variation : dict, optional - Dictionary of parameters defined for the specific setup with values. The default is ``{}``. - ref_temperature: str, optional - Reference temperature to use for heat transfer coefficient computation. The default is ``""``. - - Returns - ------- - dict - Output dictionary, which depending on the quantity chosen, contains one - of these sets of keys: - - - ``"Min"``, ``"Max"``, ``"Mean"``, ``"Stdev"``, and ``"Unit"`` - - ``"Total"`` and ``"Unit"`` - - References - ---------- - - >>> oModule.ExportFieldsSummary - """ - if settings.aedt_version < "2024.1": - raise NotImplementedError("Monitors are not supported in field summary in versions earlier than 2024 R1.") - else: # pragma: no cover - if self._app.monitor.face_monitors.get(monitor_name, None): - field_type = "Surface" - elif self._app.monitor.point_monitors.get(monitor_name, None): - field_type = "Volume" - else: - raise AttributeError("Monitor {} is not found in the design.".format(monitor_name)) - fs = self.create_field_summary() - fs.add_calculation( - "Monitor", field_type, monitor_name, quantity_name, side=side, ref_temperature=ref_temperature - ) - return self._parse_field_summary_content(fs, setup_name, design_variation, quantity_name) - - @pyaedt_function_handler() - def evaluate_object_quantity( - self, - object_name, - quantity_name, - side="Default", - volume=False, - setup_name=None, - design_variation={}, - ref_temperature="", - ): - """Export the field output on or in an object. - - Parameters - ---------- - object_name : str - Name of object to perform the computation on. - quantity_name : str - Name of the quantity to export. - side : str, optional - Side of the mesh face to use. The default is ``"Default"``. - Options are ``"Adjacent"``, ``"Combined"``, and ``"Default"``. - volume : bool, optional - Whether to compute the quantity on the volume or on the surface. The default is ``False``. - setup_name : str, optional - Name of the setup and name of the sweep. For example, ``"IcepakSetup1 : SteatyState"``. - The default is ``None``, in which case the active setup and active sweep are used. - design_variation : dict, optional - Dictionary of parameters defined for the specific setup with values. The default is ``{}``. - ref_temperature: str, optional - Reference temperature to use for heat transfer coefficient computation. The default is ``""``. - - Returns - ------- - dict - Output dictionary, which depending on the quantity chosen, contains one - of these sets of keys: - - - ``"Min"``, ``"Max"``, ``"Mean"``, ``"Stdev"``, and ``"Unit"`` - - ``"Total"`` and ``"Unit"`` - - References - ---------- - - >>> oModule.ExportFieldsSummary - """ - fs = self.create_field_summary() - fs.add_calculation( - "Boundary", - ["Surface", "Volume"][int(volume)], - object_name, - quantity_name, - side=side, - ref_temperature=ref_temperature, - ) - return self._parse_field_summary_content(fs, setup_name, design_variation, quantity_name)