diff --git a/doc/source/api/analysis_types.rst b/doc/source/api/analysis_types.rst index 16343b44..3b00f1ca 100644 --- a/doc/source/api/analysis_types.rst +++ b/doc/source/api/analysis_types.rst @@ -21,3 +21,7 @@ Constants :members: .. autoclass:: UpdatePcbModelingPropsRequestPcbModelType :members: +.. autoclass:: ComponentFailureMechanism + :members: +.. autoclass:: UpdateComponentFailureMechanismPropsRequest + :members: diff --git a/pyproject.toml b/pyproject.toml index e674fba6..807d5f2e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "flit_core.buildapi" [project] name = "ansys-sherlock-core" -version = "0.3.dev0" +version = "0.9.dev0" description = "A python wrapper for Ansys Sherlock" readme = "README.rst" requires-python = ">=3.10,<4" @@ -23,7 +23,7 @@ classifiers = [ ] dependencies = [ - "ansys-api-sherlock==0.1.36", + "ansys-api-sherlock==0.1.37", "grpcio>=1.17, <1.68.0", "protobuf>=3.20", "pydantic>=2.9.2", diff --git a/src/ansys/sherlock/core/analysis.py b/src/ansys/sherlock/core/analysis.py index 7d6cb497..fd57dff7 100644 --- a/src/ansys/sherlock/core/analysis.py +++ b/src/ansys/sherlock/core/analysis.py @@ -10,6 +10,7 @@ ModelSource, RunAnalysisRequestAnalysisType, RunStrainMapAnalysisRequestAnalysisType, + UpdateComponentFailureMechanismPropsRequest, UpdatePcbModelingPropsRequestAnalysisType, UpdatePcbModelingPropsRequestPcbMaterialModel, UpdatePcbModelingPropsRequestPcbModelType, @@ -18,7 +19,9 @@ try: import SherlockAnalysisService_pb2 import SherlockAnalysisService_pb2_grpc + import SherlockCommonService_pb2 except ModuleNotFoundError: + from ansys.api.sherlock.v0 import SherlockCommonService_pb2 from ansys.api.sherlock.v0 import SherlockAnalysisService_pb2 from ansys.api.sherlock.v0 import SherlockAnalysisService_pb2_grpc @@ -2166,3 +2169,68 @@ def get_parts_list_validation_analysis_props( except SherlockGetPartsListValidationAnalysisPropsError as e: LOG.error(str(e)) raise e + + @require_version(252) + def update_component_failure_mechanism_analysis_props( + self, + request: UpdateComponentFailureMechanismPropsRequest, + ) -> list[SherlockCommonService_pb2.ReturnCode]: + r"""Update properties for one or more Component Failure Mechanism analysis. + + Parameters + ---------- + request: UpdateComponentFailureMechanismPropsRequest + Contains all the information needed to update the properties for one or more component + failure mechanism analyses per project. + + Returns + ------- + list[SherlockCommonService_pb2.ReturnCode] + Return codes for each request. + + Examples + -------- + >>> from ansys.sherlock.core.launcher import launch_sherlock + >>> from ansys.sherlock.core.types.analysis_types import ( + ComponentFailureMechanism, + UpdateComponentFailureMechanismPropsRequest, + ) + >>> sherlock = launch_sherlock() + >>> sherlock.project.import_project_zip_archive( + project="Assembly Tutorial", + category="category", + archive_file=\ + "C:\\Program Files\\ANSYS Inc\\v252\\sherlock\\tutorial\\Assembly Tutorial.zip", + ) + >>> update_request1 = ComponentFailureMechanism( + cca_name="Main Board", + default_part_temp_rise=1.5, + default_part_temp_rise_units="K", + part_temp_rise_min_enabled=True, + part_validation_enabled=False, + ) + >>> update_request2 = ComponentFailureMechanism( + cca_name="Memory Card 1", + default_part_temp_rise=-3.25, + default_part_temp_rise_units="F", + part_temp_rise_min_enabled=False, + part_validation_enabled=True, + ) + >>> request = UpdateComponentFailureMechanismPropsRequest( + project="Test", + component_failure_mechanism_properties_per_cca=[ + update_request1, + update_request2 + ] + ) + >>> return_codes = sherlock.analysis.\ + update_component_failure_mechanism_analysis_props(request) + >>> for return_code in return_codes: + print(f"Return code: value={return_code.value}, message={return_code.message}") + """ + update_request = request._convert_to_grpc() + + responses = [] + for return_code in self.stub.updateComponentFailureMechanismProps(update_request): + responses.append(return_code) + return responses diff --git a/src/ansys/sherlock/core/types/analysis_types.py b/src/ansys/sherlock/core/types/analysis_types.py index 568f0260..d3f9eb06 100644 --- a/src/ansys/sherlock/core/types/analysis_types.py +++ b/src/ansys/sherlock/core/types/analysis_types.py @@ -2,6 +2,10 @@ """Module containing types for the Analysis Service.""" +from pydantic import BaseModel, ValidationInfo, field_validator + +from ansys.sherlock.core.types.common_types import basic_str_validator + try: import SherlockAnalysisService_pb2 except ModuleNotFoundError: @@ -114,3 +118,64 @@ class UpdatePcbModelingPropsRequestPcbModelType: __model_type = analysis_service.UpdatePcbModelingPropsRequest.Analysis.PcbModelType BONDED = __model_type.Bonded "BONDED" + + +class ComponentFailureMechanism(BaseModel): + """Contains the properties of a component failure mechanism update request.""" + + cca_name: str + """Name of the CCA.""" + default_part_temp_rise: float + """Default part temperature rise.""" + default_part_temp_rise_units: str + """Default part temperature rise units.""" + part_temp_rise_min_enabled: bool + """Whether part temperature rise value is applied to the minimum temperature defined in the + thermal cycle.""" + part_validation_enabled: bool + """Whether part validation should be performed.""" + + def _convert_to_grpc( + self, + ) -> analysis_service.UpdateComponentFailureMechanismPropsRequest.ComponentFailureMechanism: + + grpc_data = ( + analysis_service.UpdateComponentFailureMechanismPropsRequest.ComponentFailureMechanism() + ) + + grpc_data.ccaName = self.cca_name + grpc_data.defaultPartTempRise = self.default_part_temp_rise + grpc_data.defaultPartTempRiseUnits = self.default_part_temp_rise_units + grpc_data.partTempRiseMinEnabled = self.part_temp_rise_min_enabled + grpc_data.partValidationEnabled = self.part_validation_enabled + return grpc_data + + @field_validator("cca_name") + @classmethod + def str_validation(cls, value: str, info: ValidationInfo): + """Validate string fields listed.""" + return basic_str_validator(value, info.field_name) + + +class UpdateComponentFailureMechanismPropsRequest(BaseModel): + """Contains the properties of a component failure mechanism update per project.""" + + project: str + """Name of the Sherlock project.""" + component_failure_mechanism_properties_per_cca: list[ComponentFailureMechanism] + """List of potting region data to update.""" + + @field_validator("project") + @classmethod + def str_validation(cls, value: str, info: ValidationInfo): + """Validate string fields listed.""" + return basic_str_validator(value, info.field_name) + + def _convert_to_grpc( + self, + ) -> analysis_service.UpdateComponentFailureMechanismPropsRequest: + request = analysis_service.UpdateComponentFailureMechanismPropsRequest() + request.project = self.project + for properties in self.component_failure_mechanism_properties_per_cca: + request.componentFailureMechanismProperties.append(properties._convert_to_grpc()) + return request diff --git a/tests/test_analysis.py b/tests/test_analysis.py index 57ea06eb..a4c572db 100644 --- a/tests/test_analysis.py +++ b/tests/test_analysis.py @@ -8,6 +8,7 @@ from unittest.mock import Mock import grpc +import pydantic import pytest from ansys.sherlock.core.analysis import Analysis @@ -26,10 +27,12 @@ SherlockUpdateSolderFatiguePropsError, ) from ansys.sherlock.core.types.analysis_types import ( + ComponentFailureMechanism, ElementOrder, ModelSource, RunAnalysisRequestAnalysisType, RunStrainMapAnalysisRequestAnalysisType, + UpdateComponentFailureMechanismPropsRequest, UpdatePcbModelingPropsRequestAnalysisType, UpdatePcbModelingPropsRequestPcbMaterialModel, UpdatePcbModelingPropsRequestPcbModelType, @@ -62,6 +65,7 @@ def test_all(): helper_test_update_part_modeling_props(analysis) helper_test_update_parts_list_validation_props(analysis) helper_test_get_parts_list_validation_analysis_props(analysis) + helper_test_update_component_failure_mechanism_props(analysis) def helper_test_run_analysis(analysis: Analysis): @@ -1942,5 +1946,85 @@ def helper_test_get_parts_list_validation_analysis_props(analysis: Analysis): pytest.fail(str(e)) +def helper_test_update_component_failure_mechanism_props(analysis: Analysis): + """Test update component failure mechanism properties API.""" + try: + ComponentFailureMechanism( + cca_name="", + default_part_temp_rise=0.1, + default_part_temp_rise_units="F", + part_temp_rise_min_enabled=True, + part_validation_enabled=False, + ) + pytest.fail("No exception raised when using an invalid parameter") + except Exception as e: + assert isinstance(e, pydantic.ValidationError) + assert ( + e.errors()[0]["msg"] == "Value error, cca_name is invalid because it is None or empty." + ) + + try: + UpdateComponentFailureMechanismPropsRequest( + project="", + ) + pytest.fail("No exception raised when using an invalid parameter") + except Exception as e: + assert isinstance(e, pydantic.ValidationError) + assert ( + e.errors()[0]["msg"] == "Value error, project is invalid because it is None or empty." + ) + + try: + UpdateComponentFailureMechanismPropsRequest( + project="Test", + component_failure_mechanism_properties_per_cca=["Not a ComponentFailureMechanism"], + ) + pytest.fail("No exception raised when using an invalid parameter") + except Exception as e: + assert isinstance(e, pydantic.ValidationError) + assert ( + e.errors()[0]["msg"] + == "Input should be a valid dictionary or instance of ComponentFailureMechanism" + ) + + component_failure_mechanism = ComponentFailureMechanism( + cca_name="Main Board", + default_part_temp_rise=1.2, + default_part_temp_rise_units="K", + part_temp_rise_min_enabled=False, + part_validation_enabled=False, + ) + component_failure_mechanism2 = ComponentFailureMechanism( + cca_name="Memory Card 1", + default_part_temp_rise=2.1, + default_part_temp_rise_units="F", + part_temp_rise_min_enabled=False, + part_validation_enabled=False, + ) + request = UpdateComponentFailureMechanismPropsRequest( + project="Invalid project", + component_failure_mechanism_properties_per_cca=[ + component_failure_mechanism, + component_failure_mechanism2, + ], + ) + + if analysis._is_connection_up(): + responses = analysis.update_component_failure_mechanism_analysis_props(request) + + assert len(responses) == 2 + for return_code in responses: + assert return_code.value == -1 + assert return_code.message == f"Cannot find project: {request.project}" + + request.project = "AssemblyTutorial" + responses = analysis.update_component_failure_mechanism_analysis_props(request) + + assert len(responses) == 2 + for return_code in responses: + assert return_code.value == 0 + assert return_code.message == "" + + if __name__ == "__main__": test_all()