Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/changelog.d/612.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Feat: Added import thermal signal API
2 changes: 2 additions & 0 deletions doc/source/api/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Use the search feature or click links to view API documentation.
layer
layer_types
lifecycle
lifecycle_types
model
parts
parts_types
Expand All @@ -35,6 +36,7 @@ Use the search feature or click links to view API documentation.
ansys.sherlock.core.layer
ansys.sherlock.core.types.layer_types
ansys.sherlock.core.lifecycle
ansys.sherlock.core.types.lifecycle_types
ansys.sherlock.core.model
ansys.sherlock.core.parts
ansys.sherlock.core.types.parts_types
Expand Down
13 changes: 13 additions & 0 deletions doc/source/api/lifecycle_types.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
.. _ref_common_types:

LifeCycle Types
===============
.. automodule:: ansys.sherlock.core.types.lifecycle_types
.. currentmodule:: ansys.sherlock.core.types.lifecycle_types

Constants
---------
.. autoclass:: ThermalSignalFileProperties
:members:
.. autoclass:: ImportThermalSignalRequest
:members:
56 changes: 56 additions & 0 deletions src/ansys/sherlock/core/lifecycle.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

"""Module containing all life cycle management capabilities."""
try:
import SherlockCommonService_pb2
import SherlockLifeCycleService_pb2
import SherlockLifeCycleService_pb2_grpc
except ModuleNotFoundError:
from ansys.api.sherlock.v0 import SherlockCommonService_pb2
from ansys.api.sherlock.v0 import SherlockLifeCycleService_pb2
from ansys.api.sherlock.v0 import SherlockLifeCycleService_pb2_grpc

Expand Down Expand Up @@ -35,6 +37,7 @@
SherlockNoGrpcConnectionException,
)
from ansys.sherlock.core.grpc_stub import GrpcStub
from ansys.sherlock.core.types.lifecycle_types import ImportThermalSignalRequest
from ansys.sherlock.core.utils.version_check import require_version


Expand Down Expand Up @@ -2107,3 +2110,56 @@ def load_shock_profile_pulses(
for error in e.str_itr():
LOG.error(error)
raise e

@require_version(261)
def import_thermal_signal(
self, request: ImportThermalSignalRequest
) -> SherlockCommonService_pb2.ReturnCode:
"""Import a thermal signal to a life cycle phase.

Available Since: 2026R1

Parameters
----------
request: ImportThermalSignalRequest
Request object containing the information needed to import a thermal signal.

Returns
-------
SherlockCommonService_pb2.ReturnCode
Status code of the response. 0 for success.

Examples
--------
>>> from ansys.sherlock.core.types.lifecycle_types import ImportThermalSignalRequest
>>> from ansys.sherlock.core.types.lifecycle_types import ThermalSignalFileProperties
>>> from ansys.sherlock.core.launcher import launch_sherlock
>>> sherlock = launch_sherlock()
>>> response = sherlock.lifecycle.import_thermal_signal(
>>> ImportThermalSignalRequest(
>>> file_name="/path/to/thermal_signal_file.csv",
>>> project="TestProject",
>>> thermal_signal_file_properties=ThermalSignalFileProperties(
>>> header_row_count=0,
>>> numeric_format="English",
>>> column_delimiter=",",
>>> time_column="Time",
>>> time_units="sec",
>>> temperature_column="Temperature",
>>> temperature_units="C"
>>> ),
>>> phase_name=phaseName,
>>> time_removal= False,
>>> load_range_percentage=0.25,
>>> number_of_bins=0,
>>> filtering_limit=0.0,
>>> generated_cycles_label="Second Generated Cycles from Python",
>>> )
>>> )
"""
import_thermal_signal_request = request._convert_to_grpc()

if not self._is_connection_up():
raise SherlockNoGrpcConnectionException()

return self.stub.importThermalSignal(import_thermal_signal_request)
110 changes: 110 additions & 0 deletions src/ansys/sherlock/core/types/lifecycle_types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# Copyright (C) 2021 - 2025 ANSYS, Inc. and/or its affiliates.

"""Module containing types for the Lifecycle Service."""

from pydantic import BaseModel, ValidationInfo, field_validator

from ansys.sherlock.core.types.common_types import basic_str_validator

try:
import SherlockLifeCycleService_pb2
except ModuleNotFoundError:
from ansys.api.sherlock.v0 import SherlockLifeCycleService_pb2


class ThermalSignalFileProperties(BaseModel):
"""Properties of a thermal signal file."""

header_row_count: int
"""Number of rows before the column header in the file."""
numeric_format: str
"""Numeric format for the values."""
column_delimiter: str
"""Delimiter used to separate columns in the file."""
time_column: str
"""Name of the column containing time values."""
time_units: str
"""Units of the time values."""
temperature_column: str
"""Name of the column containing temperature values."""
temperature_units: str
"""Units of the temperature values."""

def _convert_to_grpc(
self,
) -> SherlockLifeCycleService_pb2.ImportThermalSignalRequest.ThermalSignalFileProperties:
"""Convert to gRPC ThermalSignalFileProperties."""
return SherlockLifeCycleService_pb2.ImportThermalSignalRequest.ThermalSignalFileProperties(
headerRowCount=self.header_row_count,
numericFormat=self.numeric_format,
columnDelimiter=self.column_delimiter,
timeColumn=self.time_column,
timeUnits=self.time_units,
temperatureColumn=self.temperature_column,
temperatureUnits=self.temperature_units,
)

@field_validator("header_row_count")
@classmethod
def non_negative_int_validation(cls, value: int, info: ValidationInfo):
"""Validate integer fields listed contain non-negative values."""
if value < 0:
raise ValueError(f"{info.field_name} must be greater than or equal to 0.")
return value

@field_validator("time_column", "time_units", "temperature_column", "temperature_units")
@classmethod
def str_validation(cls, value: str, info: ValidationInfo):
"""Validate string fields listed."""
return basic_str_validator(value, info.field_name)


class ImportThermalSignalRequest(BaseModel):
"""Request to import a thermal signal file."""

file_name: str
"""The full path to the CSV thermal signal file to be imported."""
project: str
"""Sherlock project name in which the thermal signal is imported."""
phase_name: str
"""Name of the phase in which the thermal signal is imported."""
thermal_signal_file_properties: ThermalSignalFileProperties
"""Properties of the thermal signal file."""
time_removal: bool
"""Option to indicate that time results with shorter half-cycle durations are removed."""
load_range_percentage: float
"""Defines the fraction of the range near peaks and valleys considered as a dwell region"""
number_of_bins: int
"""Number of bins for binning cycles, 0 for no binning"""
filtering_limit: float
"""Minimum cycle range to include in results, 0 for not filtering"""
generated_cycles_label: str
"""Label used to define the name of all generated thermal events."""

@field_validator("file_name", "project", "phase_name", "generated_cycles_label")
@classmethod
def str_validation(cls, value: str, info: ValidationInfo):
"""Validate string fields listed."""
return basic_str_validator(value, info.field_name)

@field_validator("number_of_bins")
@classmethod
def non_negative_int_validation(cls, value: int, info: ValidationInfo):
"""Validate integer fields listed contain non-negative values."""
if value < 0:
raise ValueError(f"{info.field_name} must be greater than or equal to 0.")
return value

def _convert_to_grpc(self) -> SherlockLifeCycleService_pb2.ImportThermalSignalRequest:
"""Convert to gRPC ImportThermalSignalRequest."""
return SherlockLifeCycleService_pb2.ImportThermalSignalRequest(
thermalSignalFile=self.file_name,
project=self.project,
phaseName=self.phase_name,
fileProperties=self.thermal_signal_file_properties._convert_to_grpc(),
timeRemoval=self.time_removal,
loadRangePercentage=self.load_range_percentage,
numberOfBins=self.number_of_bins,
filteringLimit=self.filtering_limit,
generatedCyclesLabel=self.generated_cycles_label,
)
Loading
Loading