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/633.documentation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
MAINT: Bump pytest from 8.4.1 to 8.4.2
1 change: 1 addition & 0 deletions doc/changelog.d/641.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Feat: Added APIs saveHarmonicProfile(), saveRandomVibeProfile(), saveShockPulseProfile() and saveThermalProfile()
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ classifiers = [
]

dependencies = [
"ansys-api-sherlock==0.1.49",
"ansys-api-sherlock==0.1.50",
"grpcio>=1.17, <1.68.0",
"protobuf>=3.20",
"pydantic>=2.9.2",
Expand All @@ -34,7 +34,7 @@ dependencies = [
tests = [
"grpcio>=1.17, <1.68.0",
"protobuf==5.28.0",
"pytest==8.4.1",
"pytest==8.4.2",
"pytest-cov==6.2.1",
]
doc = [
Expand Down
12 changes: 12 additions & 0 deletions src/ansys/sherlock/core/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -1292,3 +1292,15 @@ def __init__(self, message):
def __str__(self):
"""Format error message."""
return f"Export layer image error: {self.message}"


class SherlockSaveProfileError(Exception):
"""Contains the errors raised when a profile for an existing event cannot be saved."""

def __init__(self, message):
"""Initialize error message."""
self.message = message

def __str__(self):
"""Format error message."""
return f"Save profile error: {self.message}"
188 changes: 187 additions & 1 deletion src/ansys/sherlock/core/lifecycle.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,16 @@
SherlockLoadShockProfilePulsesError,
SherlockLoadThermalProfileError,
SherlockNoGrpcConnectionException,
SherlockSaveProfileError,
)
from ansys.sherlock.core.grpc_stub import GrpcStub
from ansys.sherlock.core.types.lifecycle_types import ImportThermalSignalRequest
from ansys.sherlock.core.types.lifecycle_types import (
ImportThermalSignalRequest,
SaveHarmonicProfileRequest,
SaveRandomVibeProfileRequest,
SaveShockPulseProfileRequest,
SaveThermalProfileRequest,
)
from ansys.sherlock.core.utils.version_check import require_version


Expand Down Expand Up @@ -2167,3 +2174,182 @@ def import_thermal_signal(
raise SherlockNoGrpcConnectionException()

return self.stub.importThermalSignal(import_thermal_signal_request)

@require_version(261)
def save_harmonic_profile(
self, request: SaveHarmonicProfileRequest
) -> SherlockCommonService_pb2.ReturnCode:
"""Save a harmonic life cycle event profile to a .dat or .csv file.

Available Since: 2026R1

Parameters
----------
request : SaveHarmonicProfileRequest
Request object containing the information needed to save a harmonic profile.

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

Examples
--------
>>> from ansys.sherlock.core.types.lifecycle_types import SaveHarmonicProfileRequest
>>> from ansys.sherlock.core.launcher import launch_sherlock
>>> sherlock = launch_sherlock()
>>> response = sherlock.lifecycle.save_harmonic_profile(
>>> SaveHarmonicProfileRequest(
>>> project="MyProject",
>>> phase_name="DurabilityPhase",
>>> event_name="Harmonic_100Hz",
>>> triaxial_axis="x",
>>> file_path="/tmp/Harmonic_100Hz.csv",
>>> )
>>> )
>>> assert response.value == 0
"""
grpc_request = request._convert_to_grpc()

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

response = self.stub.saveHarmonicProfile(grpc_request)

# Raise error if save failed
if response.value != 0:
raise SherlockSaveProfileError(response.message)

return response

@require_version(261)
def save_random_vibe_profile(
self, request: SaveRandomVibeProfileRequest
) -> SherlockCommonService_pb2.ReturnCode:
"""Save a random vibe life cycle event profile to a .dat or .csv file.

Available Since: 2026R1

Parameters
----------
request : SaveRandomVibeProfileRequest
Request object containing the information needed to save a random vibe profile.

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

Examples
--------
>>> from ansys.sherlock.core.types.lifecycle_types import SaveRandomVibeProfileRequest
>>> from ansys.sherlock.core.launcher import launch_sherlock
>>> sherlock = launch_sherlock()
>>> response = sherlock.lifecycle.save_random_vibe_profile(
>>> SaveRandomVibeProfileRequest(
>>> project="MyProject",
>>> phase_name="RandomVibePhase",
>>> event_name="RV_Event_01",
>>> file_path="/tmp/RV_Event_01.dat",
>>> )
>>> )
>>> assert response.value == 0
"""
grpc_request = request._convert_to_grpc()

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

response = self.stub.saveRandomVibeProfile(grpc_request)

# Raise error if save failed
if response.value != 0:
raise SherlockSaveProfileError(response.message)

@require_version(261)
def save_shock_pulse_profile(
self, request: SaveShockPulseProfileRequest
) -> SherlockCommonService_pb2.ReturnCode:
"""Save a shock pulse life cycle event profile to a .dat or .csv file.

Available Since: 2026R1

Parameters
----------
request : SaveShockPulseProfileRequest
Request object containing the information needed to save a shock pulse profile.

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

Examples
--------
>>> from ansys.sherlock.core.types.lifecycle_types import SaveShockPulseProfileRequest
>>> from ansys.sherlock.core.launcher import launch_sherlock
>>> sherlock = launch_sherlock()
>>> response = sherlock.lifecycle.save_shock_pulse_profile(
>>> SaveShockPulseProfileRequest(
>>> project="MyProject",
>>> phase_name="ShockPhase",
>>> event_name="Pulse_200g",
>>> file_path="/tmp/Pulse_200g.csv",
>>> )
>>> )
>>> assert response.value == 0
"""
grpc_request = request._convert_to_grpc()

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

response = self.stub.saveShockPulseProfile(grpc_request)

# Raise error if save failed
if response.value != 0:
raise SherlockSaveProfileError(response.message)

@require_version(261)
def save_thermal_profile(
self, request: SaveThermalProfileRequest
) -> SherlockCommonService_pb2.ReturnCode:
"""Save a thermal life cycle event profile to a .dat or .csv file.

Available Since: 2026R1

Parameters
----------
request : SaveThermalProfileRequest
Request object containing the information needed to save a thermal profile.

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

Examples
--------
>>> from ansys.sherlock.core.types.lifecycle_types import SaveThermalProfileRequest
>>> from ansys.sherlock.core.launcher import launch_sherlock
>>> sherlock = launch_sherlock()
>>> response = sherlock.lifecycle.save_thermal_profile(
>>> SaveThermalProfileRequest(
>>> project="MyProject",
>>> phase_name="ThermalPhase",
>>> event_name="ThermalCycle_A",
>>> file_path="/tmp/ThermalCycle_A.dat",
>>> )
>>> )
>>> assert response.value == 0
"""
grpc_request = request._convert_to_grpc()

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

response = self.stub.saveThermalProfile(grpc_request)

# Raise error if save failed
if response.value != 0:
raise SherlockSaveProfileError(response.message)
130 changes: 130 additions & 0 deletions src/ansys/sherlock/core/types/lifecycle_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,3 +122,133 @@ def _convert_to_grpc(self) -> SherlockLifeCycleService_pb2.ImportThermalSignalRe
timeFilteringLimitUnits=self.time_filtering_limit_units,
generatedCyclesLabel=self.generated_cycles_label,
)


class SaveHarmonicProfileRequest(BaseModel):
"""Request to save a harmonic life cycle event profile to a .dat or .csv file."""

project: str
"""Sherlock project name."""

phase_name: str
"""The name of the life cycle phase this event is associated with."""

event_name: str
"""Harmonic event name."""

triaxial_axis: str | None = None
"""If the harmonic profile type is 'Triaxial', the axis this profile should be assigned to.
Valid values are: x, y, z.
"""

file_path: str
"""Full destination path for the .dat or .csv file."""

@field_validator("project", "phase_name", "event_name", "file_path", "triaxial_axis")
@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) -> SherlockLifeCycleService_pb2.SaveHarmonicProfileRequest:
"""Convert to gRPC SaveHarmonicProfileRequest."""
return SherlockLifeCycleService_pb2.SaveHarmonicProfileRequest(
project=self.project,
phaseName=self.phase_name,
eventName=self.event_name,
triaxialAxis=self.triaxial_axis or "",
filePath=self.file_path,
)


class SaveRandomVibeProfileRequest(BaseModel):
"""Request to save a random vibe life cycle event profile to a .dat or .csv file."""

project: str
"""Sherlock project name."""

phase_name: str
"""The name of the life cycle phase this event is associated with."""

event_name: str
"""Random vibe event name."""

file_path: str
"""Full destination path for the .dat or .csv file."""

@field_validator("project", "phase_name", "event_name", "file_path")
@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) -> SherlockLifeCycleService_pb2.SaveRandomVibeProfileRequest:
"""Convert to gRPC SaveRandomVibeProfileRequest."""
return SherlockLifeCycleService_pb2.SaveRandomVibeProfileRequest(
project=self.project,
phaseName=self.phase_name,
eventName=self.event_name,
filePath=self.file_path,
)


class SaveShockPulseProfileRequest(BaseModel):
"""Request to save a shock pulse life cycle event profile to a .dat or .csv file."""

project: str
"""Sherlock project name."""

phase_name: str
"""The name of the life cycle phase this event is associated with."""

event_name: str
"""Shock event name."""

file_path: str
"""Full destination path for the .dat or .csv file."""

@field_validator("project", "phase_name", "event_name", "file_path")
@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) -> SherlockLifeCycleService_pb2.SaveShockPulseProfileRequest:
"""Convert to gRPC SaveShockPulseProfileRequest."""
return SherlockLifeCycleService_pb2.SaveShockPulseProfileRequest(
project=self.project,
phaseName=self.phase_name,
eventName=self.event_name,
filePath=self.file_path,
)


class SaveThermalProfileRequest(BaseModel):
"""Request to save a thermal life cycle event profile to a .dat or .csv file."""

project: str
"""Sherlock project name."""

phase_name: str
"""The name of the life cycle phase this event is associated with."""

event_name: str
"""Thermal event name."""

file_path: str
"""Full destination path for the .dat or .csv file."""

@field_validator("project", "phase_name", "event_name", "file_path")
@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) -> SherlockLifeCycleService_pb2.SaveThermalProfileRequest:
"""Convert to gRPC SaveThermalProfileRequest."""
return SherlockLifeCycleService_pb2.SaveThermalProfileRequest(
project=self.project,
phaseName=self.phase_name,
eventName=self.event_name,
filePath=self.file_path,
)
Loading
Loading