From e9781a115d71f5445a32ab3ec5a426c37ca2ea77 Mon Sep 17 00:00:00 2001 From: StudioWEngineers Date: Tue, 17 Feb 2026 21:37:51 +0100 Subject: [PATCH 1/6] apply pycodestyle suggestions --- .../cdb_reader/_internal/cable_data.py | 51 +++++++------ .../cdb_reader/_internal/cable_load.py | 74 ++++++++++--------- .../cdb_reader/_internal/cable_results.py | 69 +++++++++-------- 3 files changed, 104 insertions(+), 90 deletions(-) diff --git a/src/py_sofistik_utils/cdb_reader/_internal/cable_data.py b/src/py_sofistik_utils/cdb_reader/_internal/cable_data.py index bed5ce2..f6a9144 100644 --- a/src/py_sofistik_utils/cdb_reader/_internal/cable_data.py +++ b/src/py_sofistik_utils/cdb_reader/_internal/cable_data.py @@ -17,8 +17,8 @@ class _CableData: * store the retrieved data in a convenient format; * provide access to the data after the CDB is closed. - The underlying data structure is a :class:`pandas.DataFrame` with the following - columns: + The underlying data structure is a :class:`pandas.DataFrame` with the + following columns: * ``GROUP`` element group * ``ELEM_ID`` element number @@ -27,12 +27,14 @@ class _CableData: * ``L0``: initial length * ``PROPERTY``: property number (cross-section) - The ``DataFrame`` uses a MultiIndex with level ``ELEM_ID`` to enable fast lookups - via the `get` method. The index column is not dropped from the ``DataFrame``. + The ``DataFrame`` uses a MultiIndex with level ``ELEM_ID`` to enable + fast lookups via the `get` method. The index column is not dropped from + the ``DataFrame``. .. note:: - Not all available quantities are retrieved and stored. In particular: + Not all available quantities are retrieved and stored. In + particular: * normal direction * prestress @@ -43,12 +45,12 @@ class _CableData: are currently not included. - This is a deliberate design choice and may be changed in the future without - breaking the existing API. + This is a deliberate design choice and may be changed in the future + without breaking the existing API. """ def __init__(self, dll: SofDll) -> None: self._data = DataFrame( - columns = [ + columns=[ "GROUP", "ELEM_ID", "N1", @@ -66,15 +68,16 @@ def clear(self) -> None: self._data = self._data[0:0] def data(self, deep: bool = True) -> DataFrame: - """Return the :class:`pandas.DataFrame` containing the loaded key ``160/00``. + """Return the :class:`pandas.DataFrame` containing the loaded key + ``160/00``. Parameters ---------- deep : bool, default True - When ``deep=True``, a new object will be created with a copy of the calling - object's data and indices. Modifications to the data or indices of the - copy will not be reflected in the original object (refer to - :meth:`pandas.DataFrame.copy` documentation for details). + When ``deep=True``, a new object will be created with a copy of the + calling object's data and indices. Modifications to the data or + indices of the copy will not be reflected in the original object + (refer to :meth:`pandas.DataFrame.copy` documentation for details). """ return self._data.copy(deep=deep) @@ -83,7 +86,7 @@ def get( element_id: int, quantity: str = "L0", default: float | int | None = None - ) -> float | int: + ) -> float | int: """Retrieve the requested cable quantity. Parameters @@ -104,8 +107,8 @@ def get( Returns ------- value : float or int - The requested quantity if found. Otherwise, returns ``default`` when it is not - None. + The requested quantity if found. Otherwise, returns ``default`` + when it is not None. Raises ------ @@ -123,8 +126,8 @@ def get( ) from e def load(self) -> None: - """Retrieve all cable data. If the key does not exist or it is empty, a warning is - raised only if ``echo_level > 0``. + """Retrieve all cable data. If the key does not exist or it is empty, a + warning is raised only if ``echo_level > 0``. """ if self._dll.key_exist(160, 0): cabl = CCABL() @@ -165,8 +168,8 @@ def load(self) -> None: group_data = _GroupData(self._dll) group_data.load() - temp_df = DataFrame(data).sort_values("ELEM_ID", kind="mergesort") - elem_ids = temp_df["ELEM_ID"] + df = DataFrame(data).sort_values("ELEM_ID", kind="mergesort") + elem_ids = df["ELEM_ID"] for grp, grp_range in group_data.iterator_cable(): if grp_range.stop == 0: @@ -174,13 +177,13 @@ def load(self) -> None: left = elem_ids.searchsorted(grp_range.start, side="left") right = elem_ids.searchsorted(grp_range.stop - 1, side="right") - temp_df.loc[temp_df.index[left:right], "GROUP"] = grp + df.loc[df.index[left:right], "GROUP"] = grp # set indices for fast lookup - temp_df = temp_df.set_index(["ELEM_ID"], drop=False) + df = df.set_index(["ELEM_ID"], drop=False) # merge data if self._data.empty: - self._data = temp_df + self._data = df else: - self._data = concat([self._data, temp_df]) + self._data = concat([self._data, df]) diff --git a/src/py_sofistik_utils/cdb_reader/_internal/cable_load.py b/src/py_sofistik_utils/cdb_reader/_internal/cable_load.py index 03146fc..dce7196 100644 --- a/src/py_sofistik_utils/cdb_reader/_internal/cable_load.py +++ b/src/py_sofistik_utils/cdb_reader/_internal/cable_load.py @@ -17,8 +17,8 @@ class _CableLoad: * store the retrieved data in a convenient format; * provide access to the data after the CDB is closed. - The underlying data structure is a :class:`pandas.DataFrame` with the following - columns: + The underlying data structure is a :class:`pandas.DataFrame` with the + following columns: * ``LOAD_CASE`` load case number * ``GROUP`` element group @@ -27,9 +27,10 @@ class _CableLoad: * ``PA``: load value at cable start point * ``PE``: load value at cable end point - The ``DataFrame`` uses a MultiIndex with levels ``ELEM_ID``, ``LOAD_CASE`` and - ``TYPE`` (in this specific order) to enable fast lookups via the `get` method. The - index columns are not dropped from the ``DataFrame``. + The ``DataFrame`` uses a MultiIndex with levels ``ELEM_ID``, + ``LOAD_CASE`` and ``TYPE`` (in this specific order) to enable fast + lookups via the `get` method. The index columns are not dropped from + the ``DataFrame``. The load ``TYPE`` can be one of the following: @@ -47,8 +48,8 @@ class _CableLoad: .. important:: - Wind and snow loads are not implemented and will raise a runtime error if they - are present in the requested load case. + Wind and snow loads are not implemented and will raise a runtime + error if they are present in the requested load case. """ _LOAD_TYPE_MAP = { 10: "PG", @@ -67,7 +68,7 @@ class _CableLoad: def __init__(self, dll: SofDll) -> None: self._data = DataFrame( - columns = [ + columns=[ "LOAD_CASE", "GROUP", "ELEM_ID", @@ -98,15 +99,16 @@ def clear_all(self) -> None: self._loaded_lc.clear() def data(self, deep: bool = True) -> DataFrame: - """Return the :class:`pandas.DataFrame` containing the loaded keys ``161/LC``. + """Return the :class:`pandas.DataFrame` containing the loaded keys + ``161/LC``. Parameters ---------- deep : bool, default True - When ``deep=True``, a new object will be created with a copy of the calling - object's data and indices. Modifications to the data or indices of the - copy will not be reflected in the original object (refer to - :meth:`pandas.DataFrame.copy` documentation for details). + When ``deep=True``, a new object will be created with a copy of the + calling object's data and indices. Modifications to the data or + indices of the copy will not be reflected in the original object + (refer to :meth:`pandas.DataFrame.copy` documentation for details). """ return self._data.copy(deep=deep) @@ -117,7 +119,7 @@ def get( load_type: str, point: str = "PA", default: float | None = None - ) -> float: + ) -> float: """Retrieve the requested cable load. Parameters @@ -142,16 +144,16 @@ def get( - ``"PZP"`` point : str, default "PA" - Location on the cable where the load is applied; either the start (``"PA"``) - or the end (``"PE"``) + Location on the cable where the load is applied; either the start + (``"PA"``) or the end (``"PE"``) default : float or None, default None Value to return if the requested load is not found Returns ------- value : float - The requested load if found. Otherwise, returns ``default`` when it is not - None. + The requested load if found. Otherwise, returns ``default`` when it + is not None. Raises ------ @@ -159,18 +161,21 @@ def get( If the requested load is not found and ``default`` is None. """ try: - return self._data.at[(element_id, load_case, load_type), point] # type: ignore + return self._data.at[ + (element_id, load_case, load_type), + point + ] # type: ignore except (KeyError, ValueError) as e: if default is not None: return default raise LookupError( - f"Cable load entry not found for element id {element_id}, " - f"load case {load_case}, load type {load_type} and point {point}!" + f"Cable load entry not found for element id {element_id}, load" + f" case {load_case}, load type {load_type} and point {point}!" ) from e def load(self, load_cases: int | list[int]) -> None: - """Retrieve cable load data for the given ``load_cases``. If a load case is not - found, a warning is raised only if ``echo_level > 0``. + """Retrieve cable load data for the given ``load_cases``. If a load + case is not found, a warning is raised only if ``echo_level > 0``. Parameters ---------- @@ -179,8 +184,8 @@ def load(self, load_cases: int | list[int]) -> None: Notes ----- - Wind and snow loads are not implemented and will raise a runtime error if they are - present in the requested load case. + Wind and snow loads are not implemented and will raise a runtime error + if they are present in the requested load case. """ if isinstance(load_cases, int): load_cases = [load_cases] @@ -188,18 +193,18 @@ def load(self, load_cases: int | list[int]) -> None: load_cases = list(set(load_cases)) # remove duplicated entries # load data - temp_list: list[dict[str, float | int | str]] = [] + data: list[dict[str, float | int | str]] = [] for load_case in load_cases: if self._dll.key_exist(161, load_case): self.clear(load_case) - temp_list.extend(self._load(load_case)) + data.extend(self._load(load_case)) # assigning groups group_data = _GroupData(self._dll) group_data.load() - temp_df = DataFrame(temp_list).sort_values("ELEM_ID", kind="mergesort") - elem_ids = temp_df["ELEM_ID"] + df = DataFrame(data).sort_values("ELEM_ID", kind="mergesort") + elem_ids = df["ELEM_ID"] for grp, grp_range in group_data.iterator_cable(): if grp_range.stop == 0: @@ -207,16 +212,16 @@ def load(self, load_cases: int | list[int]) -> None: left = elem_ids.searchsorted(grp_range.start, side="left") right = elem_ids.searchsorted(grp_range.stop - 1, side="right") - temp_df.loc[temp_df.index[left:right], "GROUP"] = grp + df.loc[df.index[left:right], "GROUP"] = grp # set indices for fast lookup - temp_df = temp_df.set_index(["ELEM_ID", "LOAD_CASE", "TYPE"], drop=False) + df = df.set_index(["ELEM_ID", "LOAD_CASE", "TYPE"], drop=False) # merge data if self._data.empty: - self._data = temp_df + self._data = df else: - self._data = concat([self._data, temp_df]) + self._data = concat([self._data, df]) self._loaded_lc.update(load_cases) def set_echo_level(self, echo_level: int) -> None: @@ -257,7 +262,8 @@ def _load(self, load_case: int) -> list[dict[str, float | int | str]]: type_ = _CableLoad._LOAD_TYPE_MAP[cabl.m_typ] except KeyError as e: raise RuntimeError( - f"Unknown cable load type {cabl.m_typ} for element {cabl.m_nr}!" + f"Unknown cable load type {cabl.m_typ} for element" + f" {cabl.m_nr}!" ) from e data.append( diff --git a/src/py_sofistik_utils/cdb_reader/_internal/cable_results.py b/src/py_sofistik_utils/cdb_reader/_internal/cable_results.py index 7890f92..d5e9fc5 100644 --- a/src/py_sofistik_utils/cdb_reader/_internal/cable_results.py +++ b/src/py_sofistik_utils/cdb_reader/_internal/cable_results.py @@ -17,8 +17,8 @@ class _CableResults: * store the retrieved data in a convenient format; * provide access to the data after the CDB is closed. - The underlying data structure is a :class:`pandas.DataFrame` with the following - columns: + The underlying data structure is a :class:`pandas.DataFrame` with the + following columns: * ``LOAD_CASE`` load case number * ``GROUP`` element group @@ -30,25 +30,26 @@ class _CableResults: * ``TOTAL_STRAIN``: total strain * ``EFFECTIVE_STIFFNESS``: effective stiffness - The ``DataFrame`` uses a MultiIndex with levels ``ELEM_ID`` and ``LOAD_CASE`` - (in this specific order) to enable fast lookups via the `get` method. The - index columns are not dropped from the ``DataFrame``. + The ``DataFrame`` uses a MultiIndex with levels ``ELEM_ID`` and + ``LOAD_CASE`` (in this specific order) to enable fast lookups via the + `get` method. The index columns are not dropped from the ``DataFrame``. .. note:: - Not all available quantities are retrieved and stored. In particular: + Not all available quantities are retrieved and stored. In + particular: - * the maximum suspension of cable across axis and its components along the - global X, Y and Z axes + * the maximum suspension of cable across axis and its components + along the global X, Y and Z axes * vertical suspension of cable in load direction * nonlinear effects - are currently not included. This is a deliberate design choice and may be - changed in the future without breaking the existing API. + are currently not included. This is a deliberate design choice and + may be changed in the future without breaking the existing API. """ def __init__(self, dll: SofDll) -> None: self._data = DataFrame( - columns = [ + columns=[ "LOAD_CASE", "GROUP", "ELEM_ID", @@ -82,15 +83,16 @@ def clear_all(self) -> None: self._loaded_lc.clear() def data(self, deep: bool = True) -> DataFrame: - """Return the :class:`pandas.DataFrame` containing the loaded keys ``162/LC``. + """Return the :class:`pandas.DataFrame` containing the loaded keys + ``162/LC``. Parameters ---------- deep : bool, default True - When ``deep=True``, a new object will be created with a copy of the calling - object's data and indices. Modifications to the data or indices of the - copy will not be reflected in the original object (refer to - :meth:`pandas.DataFrame.copy` documentation for details). + When ``deep=True``, a new object will be created with a copy of the + calling object's data and indices. Modifications to the data or + indices of the copy will not be reflected in the original object + (refer to :meth:`pandas.DataFrame.copy` documentation for details). """ return self._data.copy(deep=deep) @@ -100,7 +102,7 @@ def get( load_case: int, quantity: str = "AXIAL_FORCE", default: float | None = None - ) -> float: + ) -> float: """Retrieve the requested cable result. Parameters @@ -125,8 +127,8 @@ def get( Returns ------- value : float - The requested value if found. If not found, returns ``default`` when it is not - None. + The requested value if found. If not found, returns ``default`` + when it is not None. Raises ------ @@ -134,7 +136,10 @@ def get( If the requested result is not found and ``default`` is None. """ try: - return self._data.at[(element_id, load_case), quantity] # type: ignore + return self._data.at[ + (element_id, load_case), + quantity + ] # type: ignore except (KeyError, ValueError) as e: if default is not None: return default @@ -144,8 +149,8 @@ def get( ) from e def load(self, load_cases: int | list[int]) -> None: - """Retrieve cable results for the given ``load_cases``. If a load case is not - found, a warning is raised only if ``echo_level > 0``. + """Retrieve cable results for the given ``load_cases``. If a load case + is not found, a warning is raised only if ``echo_level > 0``. Parameters ---------- @@ -158,18 +163,18 @@ def load(self, load_cases: int | list[int]) -> None: load_cases = list(set(load_cases)) # remove duplicated entries # load data - temp_list: list[dict[str, float | int | str]] = [] + data: list[dict[str, float | int]] = [] for load_case in load_cases: if self._dll.key_exist(162, load_case): self.clear(load_case) - temp_list.extend(self._load(load_case)) + data.extend(self._load(load_case)) # assigning groups group_data = _GroupData(self._dll) group_data.load() - temp_df = DataFrame(temp_list).sort_values("ELEM_ID", kind="mergesort") - elem_ids = temp_df["ELEM_ID"] + df = DataFrame(data).sort_values("ELEM_ID", kind="mergesort") + elem_ids = df["ELEM_ID"] for grp, grp_range in group_data.iterator_cable(): if grp_range.stop == 0: @@ -177,16 +182,16 @@ def load(self, load_cases: int | list[int]) -> None: left = elem_ids.searchsorted(grp_range.start, side="left") right = elem_ids.searchsorted(grp_range.stop - 1, side="right") - temp_df.loc[temp_df.index[left:right], "GROUP"] = grp + df.loc[df.index[left:right], "GROUP"] = grp # set indices for fast lookup - temp_df = temp_df.set_index(["ELEM_ID", "LOAD_CASE"], drop=False) + df = df.set_index(["ELEM_ID", "LOAD_CASE"], drop=False) # merge data if self._data.empty: - self._data = temp_df + self._data = df else: - self._data = concat([self._data, temp_df]) + self._data = concat([self._data, df]) self._loaded_lc.update(load_cases) def set_echo_level(self, echo_level: int) -> None: @@ -199,14 +204,14 @@ def set_echo_level(self, echo_level: int) -> None: """ self._echo_level = echo_level - def _load(self, load_case: int) -> list[dict[str, float | int | str]]: + def _load(self, load_case: int) -> list[dict[str, float | int]]: """Retrieve key ``162/load_case`` using SOFiSTiK dll. """ cable_res = CCABL_RES() record_length = c_int(sizeof(cable_res)) return_value = c_int(0) - data: list[dict[str, float | int | str]] = [] + data: list[dict[str, float | int]] = [] first_call = True while return_value.value < 2: return_value.value = self._dll.get( From c657f6ce81bd637c85dd5423776478e9d306b09b Mon Sep 17 00:00:00 2001 From: StudioWEngineers Date: Tue, 17 Feb 2026 22:01:14 +0100 Subject: [PATCH 2/6] make file name consistent --- .../cdb_reader/_internal/{cable_results.py => cable_result.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/py_sofistik_utils/cdb_reader/_internal/{cable_results.py => cable_result.py} (100%) diff --git a/src/py_sofistik_utils/cdb_reader/_internal/cable_results.py b/src/py_sofistik_utils/cdb_reader/_internal/cable_result.py similarity index 100% rename from src/py_sofistik_utils/cdb_reader/_internal/cable_results.py rename to src/py_sofistik_utils/cdb_reader/_internal/cable_result.py From 4844ba74f30768dc48afae3d6bcb03a6ea8b353f Mon Sep 17 00:00:00 2001 From: StudioWEngineers Date: Tue, 17 Feb 2026 22:03:11 +0100 Subject: [PATCH 3/6] make class name consistent --- src/py_sofistik_utils/cdb_reader/_internal/cable_result.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/py_sofistik_utils/cdb_reader/_internal/cable_result.py b/src/py_sofistik_utils/cdb_reader/_internal/cable_result.py index d5e9fc5..3b35055 100644 --- a/src/py_sofistik_utils/cdb_reader/_internal/cable_result.py +++ b/src/py_sofistik_utils/cdb_reader/_internal/cable_result.py @@ -10,7 +10,7 @@ from . sofistik_classes import CCABL_RES -class _CableResults: +class _CableResult: """This class provides methods and a data structure to: * access keys ``162/LC`` of the CDB file; From 63d679dc465fdce268bea33e8766ff238c9152bd Mon Sep 17 00:00:00 2001 From: StudioWEngineers Date: Tue, 17 Feb 2026 22:12:55 +0100 Subject: [PATCH 4/6] add _Cable class as high-level abstraction --- src/py_sofistik_utils/cdb_reader/__init__.py | 6 ++- .../cdb_reader/_internal/cable.py | 24 +++++++++++ src/py_sofistik_utils/cdb_reader/reader.py | 24 ++++------- tests/cdb_reader/test_cable_data.py | 22 +++++----- tests/cdb_reader/test_cable_load.py | 28 ++++++------- tests/cdb_reader/test_cable_result.py | 42 +++++++++---------- 6 files changed, 83 insertions(+), 63 deletions(-) create mode 100644 src/py_sofistik_utils/cdb_reader/_internal/cable.py diff --git a/src/py_sofistik_utils/cdb_reader/__init__.py b/src/py_sofistik_utils/cdb_reader/__init__.py index 5a201c5..fff9133 100644 --- a/src/py_sofistik_utils/cdb_reader/__init__.py +++ b/src/py_sofistik_utils/cdb_reader/__init__.py @@ -2,9 +2,10 @@ from . _internal.beam_load import _BeamLoad from . _internal.beam_results import _BeamResults from . _internal.beam_stresses import _BeamStress +from . _internal.cable import _Cable from . _internal.cable_data import _CableData from . _internal.cable_load import _CableLoad -from . _internal.cable_results import _CableResults +from . _internal.cable_result import _CableResult from . _internal.group_data import _GroupData from . _internal.group_lc_data import _GroupLCData from . _internal.load_cases import _LoadCases @@ -29,9 +30,10 @@ "_BeamLoad", "_BeamResults", "_BeamStress", + "_Cable", "_CableData", "_CableLoad", - "_CableResults", + "_CableResult", "_GroupData", "_GroupLCData", "_LoadCases", diff --git a/src/py_sofistik_utils/cdb_reader/_internal/cable.py b/src/py_sofistik_utils/cdb_reader/_internal/cable.py new file mode 100644 index 0000000..2de8180 --- /dev/null +++ b/src/py_sofistik_utils/cdb_reader/_internal/cable.py @@ -0,0 +1,24 @@ +# standard library imports + +# third party library imports + +# local library specific imports +from . cable_data import _CableData +from . cable_load import _CableLoad +from . cable_result import _CableResult +from . sofistik_dll import SofDll + + +class _Cable: + """ + High-level wrapper for cable-related data access and operations. + + The class aggregates the low-level interfaces ``_CableData``, + ``_CableLoad``, and ``_CableResults`` into a single abstraction. It + provides a structured entry point for reading, manipulating and evaluating + cable definitions, applied loads, and analysis results. + """ + def __init__(self, dll: SofDll) -> None: + self.data = _CableData(dll) + self.load = _CableLoad(dll) + self.result = _CableResult(dll) diff --git a/src/py_sofistik_utils/cdb_reader/reader.py b/src/py_sofistik_utils/cdb_reader/reader.py index 70adcb8..399be3b 100644 --- a/src/py_sofistik_utils/cdb_reader/reader.py +++ b/src/py_sofistik_utils/cdb_reader/reader.py @@ -14,9 +14,7 @@ from . _internal.beam_load import _BeamLoad from . _internal.beam_results import _BeamResults from . _internal.beam_stresses import _BeamStress -from . _internal.cable_data import _CableData -from . _internal.cable_load import _CableLoad -from . _internal.cable_results import _CableResults +from . _internal.cable import _Cable from . _internal.group_data import _GroupData from . _internal.group_lc_data import _GroupLCData from . _internal.load_cases import _LoadCases @@ -39,9 +37,7 @@ class SOFiSTiKCDBReader: beam_geo: _BeamData beam_load: _BeamLoad beam_res: _BeamResults - cable_data: _CableData - cable_load: _CableLoad - cable_res: _CableResults + cable: _Cable beam_stress: _BeamStress grp_data: _GroupData grp_lc_data: _GroupLCData @@ -62,7 +58,7 @@ def __init__( file_name: str, path_to_dlls: str, version: int = 2023 - ) -> None: + ) -> None: """The initializer of the ``SOFiSTiKCDBReader`` class. """ self._echo_level = 0 @@ -76,9 +72,7 @@ def __init__( self.beam_load = _BeamLoad(self._dll) self.beam_stress = _BeamStress(self._dll) - self.cable_data = _CableData(self._dll) - self.cable_load = _CableLoad(self._dll) - self.cable_res = _CableResults(self._dll) + self.cable = _Cable(self._dll) self.grp_data = _GroupData(self._dll) self.grp_lc_data = _GroupLCData(self._dll) @@ -103,9 +97,9 @@ def clear(self) -> None: """ #self.beam_res.clear_all_forces() #self.beam_geo.clear_connectivity() - self.cable_data.clear() - self.cable_load.clear_all() - self.cable_res.clear_all() + self.cable.data.clear() + self.cable.load.clear_all() + self.cable.result.clear_all() self.grp_data.clear() self.grp_lc_data.clear_all() self.sec_grp_lc_data.clear_all() @@ -120,7 +114,7 @@ def clear_data(self) -> None: """Clear all the loaded data. """ #self.beam_geo.clear_connectivity() - self.cable_data.clear() + self.cable.data.clear() self.grp_data.clear() self.grp_lc_data.clear_all() self.sec_grp_lc_data.clear_all() @@ -133,7 +127,7 @@ def clear_results(self) -> None: """Clear all the loaded results. """ #self.beam_res.clear_all_forces() - self.cable_res.clear_all() + self.cable.result.clear_all() self.nodes.results.clear_all() self.spring_res.clear_all() #self.load_case.clear_all() diff --git a/tests/cdb_reader/test_cable_data.py b/tests/cdb_reader/test_cable_data.py index f154ceb..1438c0d 100644 --- a/tests/cdb_reader/test_cable_data.py +++ b/tests/cdb_reader/test_cable_data.py @@ -33,7 +33,7 @@ def setUp(self) -> None: self.cdb = SOFiSTiKCDBReader(CDB_PATH, "CABLE_DATA", DLL_PATH, int(VERSION)) # type: ignore self.cdb.initialize() - self.cdb.cable_data.load() + self.cdb.cable.data.load() def tearDown(self) -> None: self.cdb.close() @@ -45,38 +45,38 @@ def test_data(self) -> None: # Float values loaded from the CDB contain inherent numerical noise. The chosen # tolerance is stricter than pandas default and reflects the maximum relative # error observed in practice, ensuring stable and reproducible comparisons. - assert_frame_equal(self.expected_data, self.cdb.cable_data.data(), rtol=1E-7) + assert_frame_equal(self.expected_data, self.cdb.cable.data.data(), rtol=1E-7) def test_get(self) -> None: """Test for the `get` method. """ with self.subTest(msg="First node id"): - self.assertEqual(self.cdb.cable_data.get(505, "N1"), 1) + self.assertEqual(self.cdb.cable.data.get(505, "N1"), 1) with self.subTest(msg="Second node id"): - self.assertEqual(self.cdb.cable_data.get(505, "N2"), 5) + self.assertEqual(self.cdb.cable.data.get(505, "N2"), 5) with self.subTest(msg="Initial length"): - self.assertEqual(self.cdb.cable_data.get(502, "L0"), 1.7320507764816284) + self.assertEqual(self.cdb.cable.data.get(502, "L0"), 1.7320507764816284) with self.subTest(msg="Property number"): - self.assertEqual(self.cdb.cable_data.get(502, "PROPERTY"), 3) + self.assertEqual(self.cdb.cable.data.get(502, "PROPERTY"), 3) with self.subTest(msg="Non existing entry without default"): with self.assertRaises(LookupError): - self.cdb.cable_data.get(505, "N3") + self.cdb.cable.data.get(505, "N3") with self.subTest(msg="Non existing entry with default"): - self.assertEqual(self.cdb.cable_data.get(505, "N3", 2), 2) + self.assertEqual(self.cdb.cable.data.get(505, "N3", 2), 2) def test_get_after_clear(self) -> None: """Test for the `get` method after a `clear` call. """ - self.cdb.cable_data.clear() + self.cdb.cable.data.clear() with self.subTest(msg="Check clear method"): with self.assertRaises(LookupError): - self.cdb.cable_data.get(505, "N1") + self.cdb.cable.data.get(505, "N1") - self.cdb.cable_data.load() + self.cdb.cable.data.load() with self.subTest(msg="Check indexes management"): self.test_get() diff --git a/tests/cdb_reader/test_cable_load.py b/tests/cdb_reader/test_cable_load.py index 9dca889..e3a9cdc 100644 --- a/tests/cdb_reader/test_cable_load.py +++ b/tests/cdb_reader/test_cable_load.py @@ -54,7 +54,7 @@ def setUp(self) -> None: self.cdb = SOFiSTiKCDBReader(CDB_PATH, "CABLE_LOAD", DLL_PATH, int(VERSION)) # type: ignore self.cdb.initialize() - self.cdb.cable_load.load(self.load_cases) + self.cdb.cable.load.load(self.load_cases) def tearDown(self) -> None: self.cdb.close() @@ -67,44 +67,44 @@ def test_data(self) -> None: # (e.g. -0.008 is represented as -0.00800000037997961). The chosen tolerance # rtol=1e-7 is stricter than pandas default and reflects the maximum relative # error observed in practice, ensuring stable and reproducible comparisons. - assert_frame_equal(self.expected_data, self.cdb.cable_load.data(), rtol=1E-7) + assert_frame_equal(self.expected_data, self.cdb.cable.load.data(), rtol=1E-7) def test_get(self) -> None: """Test for the `get` method. """ with self.subTest(msg="Existing entry"): - self.assertEqual(self.cdb.cable_load.get(5009, 7, "PZP", "PA"), -7.0) + self.assertEqual(self.cdb.cable.load.get(5009, 7, "PZP", "PA"), -7.0) with self.subTest(msg="Non existing entry with default"): - self.assertEqual(self.cdb.cable_load.get(9009, 7, "PZP", "PA", -3), -3.0) + self.assertEqual(self.cdb.cable.load.get(9009, 7, "PZP", "PA", -3), -3.0) def test_get_after_clear(self) -> None: """Test for the `get` method after a `clear` call. """ - self.cdb.cable_load.clear(7) + self.cdb.cable.load.clear(7) with self.subTest(msg="Check clear method"): with self.assertRaises(LookupError): - self.cdb.cable_load.get(5009, 7, "PZP", "PA") + self.cdb.cable.load.get(5009, 7, "PZP", "PA") - self.cdb.cable_load.load(7) + self.cdb.cable.load.load(7) with self.subTest(msg="Check indexes management"): self.test_get() def test_get_after_clear_all(self) -> None: """Test for the `get` method after a `clear_all` call. """ - self.cdb.cable_load.clear_all() + self.cdb.cable.load.clear_all() with self.subTest(msg="Check clear_all method"): with self.assertRaises(LookupError): - self.cdb.cable_load.get(5009, 7, "PZP", "PA") + self.cdb.cable.load.get(5009, 7, "PZP", "PA") - self.cdb.cable_load.load(self.load_cases) + self.cdb.cable.load.load(self.load_cases) with self.subTest(msg="Check indexes management"): - self.assertEqual(self.cdb.cable_load.get(5009, 7, "PZP", "PA"), -7.0) + self.assertEqual(self.cdb.cable.load.get(5009, 7, "PZP", "PA"), -7.0) def test_load_with_duplicated_load_cases(self) -> None: """Test for the `load` method with duplicated entries. """ - self.cdb.cable_load.clear_all() - self.cdb.cable_load.load(self.load_cases + [10]) - self.assertEqual(self.cdb.cable_load.get(5009, 7, "PZP", "PA"), -7.0) + self.cdb.cable.load.clear_all() + self.cdb.cable.load.load(self.load_cases + [10]) + self.assertEqual(self.cdb.cable.load.get(5009, 7, "PZP", "PA"), -7.0) diff --git a/tests/cdb_reader/test_cable_result.py b/tests/cdb_reader/test_cable_result.py index d79b807..972e224 100644 --- a/tests/cdb_reader/test_cable_result.py +++ b/tests/cdb_reader/test_cable_result.py @@ -48,7 +48,7 @@ def setUp(self) -> None: self.cdb = SOFiSTiKCDBReader(CDB_PATH, "CABLE_RESULT", DLL_PATH, int(VERSION)) # type: ignore self.cdb.initialize() - self.cdb.cable_res.load(self.load_cases) + self.cdb.cable.result.load(self.load_cases) def tearDown(self) -> None: self.cdb.close() @@ -60,75 +60,75 @@ def test_data(self) -> None: # Float values loaded from the CDB contain inherent numerical noise. The chosen # tolerance rtol is stricter than pandas default and reflects the maximum relative # error observed in practice, ensuring stable and reproducible comparisons. - assert_frame_equal(self.expected_data, self.cdb.cable_res.data(), rtol=1E-7) + assert_frame_equal(self.expected_data, self.cdb.cable.result.data(), rtol=1E-7) def test_get(self) -> None: """Test for the `get` method. """ with self.subTest(msg="Axial force"): self.assertEqual( - self.cdb.cable_res.get(102, 1000, "AXIAL_FORCE"), 1.7247849702835083 + self.cdb.cable.result.get(102, 1000, "AXIAL_FORCE"), 1.7247849702835083 ) with self.subTest(msg="Average axial force"): self.assertEqual( - self.cdb.cable_res.get(102, 1000, "AVG_AXIAL_FORCE"), 1.7247587442398071 + self.cdb.cable.result.get(102, 1000, "AVG_AXIAL_FORCE"), 1.7247587442398071 ) with self.subTest(msg="Axial displacement"): self.assertEqual( - self.cdb.cable_res.get(101, 1001, "AXIAL_DISPLACEMENT"), 8.366186521016061e-04 + self.cdb.cable.result.get(101, 1001, "AXIAL_DISPLACEMENT"), 8.366186521016061e-04 ) with self.subTest(msg="Relaxed length"): - self.assertEqual(self.cdb.cable_res.get(103, 1001, "RELAXED_LENGTH"), 1) + self.assertEqual(self.cdb.cable.result.get(103, 1001, "RELAXED_LENGTH"), 1) with self.subTest(msg="Total strain"): self.assertEqual( - self.cdb.cable_res.get(102, 1001, "TOTAL_STRAIN"), 1.0000000031710769e-30 + self.cdb.cable.result.get(102, 1001, "TOTAL_STRAIN"), 1.0000000031710769e-30 ) with self.subTest(msg="Effective stiffness"): self.assertEqual( - self.cdb.cable_res.get(102, 1000, "EFFECTIVE_STIFFNESS"), 0.8180915713310242 + self.cdb.cable.result.get(102, 1000, "EFFECTIVE_STIFFNESS"), 0.8180915713310242 ) with self.subTest(msg="Non existing entry without default"): with self.assertRaises(LookupError): - self.cdb.cable_res.get(102, 1000, "NON-EXISTING") + self.cdb.cable.result.get(102, 1000, "NON-EXISTING") with self.subTest(msg="Non existing entry with default"): - self.assertEqual(self.cdb.cable_res.get(102, 1000, "NON-EXISTING", 5), 5) + self.assertEqual(self.cdb.cable.result.get(102, 1000, "NON-EXISTING", 5), 5) def test_get_after_clear(self) -> None: """Test for the `get` method after a `clear` call. """ - self.cdb.cable_res.clear(1000) + self.cdb.cable.result.clear(1000) with self.subTest(msg="Check clear method"): with self.assertRaises(LookupError): - self.cdb.cable_res.get(102, 1000, "AXIAL_FORCE") + self.cdb.cable.result.get(102, 1000, "AXIAL_FORCE") - self.cdb.cable_res.load(1000) + self.cdb.cable.result.load(1000) with self.subTest(msg="Check indexes management"): - self.assertEqual(self.cdb.cable_res.get(103, 1000, "RELAXED_LENGTH"), 1) + self.assertEqual(self.cdb.cable.result.get(103, 1000, "RELAXED_LENGTH"), 1) def test_get_after_clear_all(self) -> None: """Test for the `get` method after a `clear_all` call. """ - self.cdb.cable_res.clear_all() + self.cdb.cable.result.clear_all() with self.subTest(msg="Check clear_all method"): with self.assertRaises(LookupError): - self.cdb.cable_res.get(102, 1000, "AXIAL_FORCE") + self.cdb.cable.result.get(102, 1000, "AXIAL_FORCE") - self.cdb.cable_res.load(self.load_cases) + self.cdb.cable.result.load(self.load_cases) with self.subTest(msg="Check indexes management"): self.assertEqual( - self.cdb.cable_res.get(102, 1000, "AXIAL_FORCE"), 1.7247849702835083 + self.cdb.cable.result.get(102, 1000, "AXIAL_FORCE"), 1.7247849702835083 ) def test_load_with_duplicated_load_cases(self) -> None: """Test for the `load` method with duplicated entries. """ - self.cdb.cable_res.clear_all() - self.cdb.cable_res.load(self.load_cases + [1000]) - self.assertEqual(self.cdb.cable_res.get(103, 1000, "RELAXED_LENGTH"), 1) + self.cdb.cable.result.clear_all() + self.cdb.cable.result.load(self.load_cases + [1000]) + self.assertEqual(self.cdb.cable.result.get(103, 1000, "RELAXED_LENGTH"), 1) From e0f6f22b638cc3a81802317547a2503bb5e716e9 Mon Sep 17 00:00:00 2001 From: StudioWEngineers Date: Tue, 17 Feb 2026 22:30:42 +0100 Subject: [PATCH 5/6] apply pycodestyle suggestions to tests --- tests/cdb_reader/test_cable_data.py | 50 +++++++------ tests/cdb_reader/test_cable_load.py | 101 +++++++++++++------------- tests/cdb_reader/test_cable_result.py | 88 +++++++++++++--------- 3 files changed, 132 insertions(+), 107 deletions(-) diff --git a/tests/cdb_reader/test_cable_data.py b/tests/cdb_reader/test_cable_data.py index 1438c0d..0e969e5 100644 --- a/tests/cdb_reader/test_cable_data.py +++ b/tests/cdb_reader/test_cable_data.py @@ -15,12 +15,26 @@ VERSION = environ.get("SOFISTIK_VERSION") -@skipUnless(all([CDB_PATH, DLL_PATH, VERSION]), "SOFiSTiK environment variables not set!") +@skipUnless( + all([CDB_PATH, DLL_PATH, VERSION]), + "SOFiSTiK environment variables not set!" +) class SOFiSTiKCDBReaderCableDataTestSuite(TestCase): - """Tests for the `_CableData` class. - """ def setUp(self) -> None: - self.expected_data = DataFrame( + self.cdb = SOFiSTiKCDBReader( + CDB_PATH, # type: ignore + "CABLE_DATA", + DLL_PATH, # type: ignore + int(VERSION) # type: ignore + ) + self.cdb.initialize() + self.cdb.cable.data.load() + + def tearDown(self) -> None: + self.cdb.close() + + def test_data(self) -> None: + data = DataFrame( { "GROUP": [50, 50], "ELEM_ID": [502, 505], @@ -31,25 +45,14 @@ def setUp(self) -> None: } ).set_index("ELEM_ID", drop=False) - self.cdb = SOFiSTiKCDBReader(CDB_PATH, "CABLE_DATA", DLL_PATH, int(VERSION)) # type: ignore - self.cdb.initialize() - self.cdb.cable.data.load() - - def tearDown(self) -> None: - self.cdb.close() - - def test_data(self) -> None: - """Test for the `data` method. - """ # NOTE: - # Float values loaded from the CDB contain inherent numerical noise. The chosen - # tolerance is stricter than pandas default and reflects the maximum relative - # error observed in practice, ensuring stable and reproducible comparisons. - assert_frame_equal(self.expected_data, self.cdb.cable.data.data(), rtol=1E-7) + # Float values loaded from the CDB contain inherent numerical noise. + # The chosen tolerance is stricter than pandas default and reflects the + # maximum relative error observed in practice, ensuring stable and + # reproducible comparisons. + assert_frame_equal(data, self.cdb.cable.data.data(), rtol=1E-7) def test_get(self) -> None: - """Test for the `get` method. - """ with self.subTest(msg="First node id"): self.assertEqual(self.cdb.cable.data.get(505, "N1"), 1) @@ -57,7 +60,10 @@ def test_get(self) -> None: self.assertEqual(self.cdb.cable.data.get(505, "N2"), 5) with self.subTest(msg="Initial length"): - self.assertEqual(self.cdb.cable.data.get(502, "L0"), 1.7320507764816284) + self.assertEqual( + self.cdb.cable.data.get(502, "L0"), + 1.7320507764816284 + ) with self.subTest(msg="Property number"): self.assertEqual(self.cdb.cable.data.get(502, "PROPERTY"), 3) @@ -70,8 +76,6 @@ def test_get(self) -> None: self.assertEqual(self.cdb.cable.data.get(505, "N3", 2), 2) def test_get_after_clear(self) -> None: - """Test for the `get` method after a `clear` call. - """ self.cdb.cable.data.clear() with self.subTest(msg="Check clear method"): with self.assertRaises(LookupError): diff --git a/tests/cdb_reader/test_cable_load.py b/tests/cdb_reader/test_cable_load.py index e3a9cdc..3641e2f 100644 --- a/tests/cdb_reader/test_cable_load.py +++ b/tests/cdb_reader/test_cable_load.py @@ -15,44 +15,47 @@ VERSION = environ.get("SOFISTIK_VERSION") -_COLUMNS = ["LOAD_CASE","GROUP", "ELEM_ID", "TYPE", "PA", "PE"] +_COLUMNS = ["LOAD_CASE", "GROUP", "ELEM_ID", "TYPE", "PA", "PE"] _DATA = [ - (1, 500, 5001, "PG", +1.0, -1.0), - (2, 500, 5001, "PXX", +2.0, +2.0), - (6, 500, 5001, "PYP", -6.0, -6.0), - (8, 500, 5001, "EX", -0.008, -0.008), - (11, 500, 5001, "VX", +11.0, +11.0), - (2, 500, 5009, "PXX", +2.0, +2.0), - (6, 500, 5009, "PYP", -6.0, -6.0), - (7, 500, 5009, "PZP", -7.0, -7.0), - (11, 500, 5009, "VX", +11.0, +11.0), - (3, 501, 5011, "PYY", -3.0, -3.0), - (4, 501, 5011, "PZZ", -4.0, -4.0), - (5, 501, 5011, "PXP", +5.0, +5.0), - (9, 501, 5011, "WX", +0.009, +0.009), - (10, 501, 5011, "DT", -10.0, -10.0), - (11, 501, 5011, "VX", +11.0, +11.0), - (3, 501, 5014, "PYY", -3.0, -3.0), - (5, 501, 5014, "PXP", +5.0, +5.0), - (8, 501, 5014, "EX", -0.008, -0.008), - (9, 501, 5014, "WX", +0.009, +0.009), - (10, 501, 5014, "DT", -10.0, -10.0), - (11, 501, 5014, "VX", +11.0, +11.0) - ] - - -@skipUnless(all([CDB_PATH, DLL_PATH, VERSION]), "SOFiSTiK environment variables not set!") + (1, 500, 5001, "PG", +1.0, -1.0), + (2, 500, 5001, "PXX", +2.0, +2.0), + (6, 500, 5001, "PYP", -6.0, -6.0), + (8, 500, 5001, "EX", -0.008, -0.008), + (11, 500, 5001, "VX", +11.0, +11.0), + (2, 500, 5009, "PXX", +2.0, +2.0), + (6, 500, 5009, "PYP", -6.0, -6.0), + (7, 500, 5009, "PZP", -7.0, -7.0), + (11, 500, 5009, "VX", +11.0, +11.0), + (3, 501, 5011, "PYY", -3.0, -3.0), + (4, 501, 5011, "PZZ", -4.0, -4.0), + (5, 501, 5011, "PXP", +5.0, +5.0), + (9, 501, 5011, "WX", +0.009, +0.009), + (10, 501, 5011, "DT", -10.0, -10.0), + (11, 501, 5011, "VX", +11.0, +11.0), + (3, 501, 5014, "PYY", -3.0, -3.0), + (5, 501, 5014, "PXP", +5.0, +5.0), + (8, 501, 5014, "EX", -0.008, -0.008), + (9, 501, 5014, "WX", +0.009, +0.009), + (10, 501, 5014, "DT", -10.0, -10.0), + (11, 501, 5014, "VX", +11.0, +11.0) +] + + +@skipUnless( + all([CDB_PATH, DLL_PATH, VERSION]), + "SOFiSTiK environment variables not set!" +) class SOFiSTiKCDBReaderCableLoadTestSuite(TestCase): - """Tests for the `_CableLoad` class. - """ def setUp(self) -> None: - self.expected_data = DataFrame(_DATA, columns=_COLUMNS).set_index( - ["ELEM_ID", "LOAD_CASE", "TYPE"], drop=False - ) self.load_cases = list(range(1, 12, 1)) - self.cdb = SOFiSTiKCDBReader(CDB_PATH, "CABLE_LOAD", DLL_PATH, int(VERSION)) # type: ignore + self.cdb = SOFiSTiKCDBReader( + CDB_PATH, # type: ignore + "CABLE_LOAD", + DLL_PATH, # type: ignore + int(VERSION) # type: ignore + ) self.cdb.initialize() self.cdb.cable.load.load(self.load_cases) @@ -60,27 +63,29 @@ def tearDown(self) -> None: self.cdb.close() def test_data(self) -> None: - """Test for the `data` method. - """ + data = DataFrame( + _DATA, + columns=_COLUMNS + ).set_index(["ELEM_ID", "LOAD_CASE", "TYPE"], drop=False) + # NOTE: - # Float values loaded from the CDB contain inherent numerical noise - # (e.g. -0.008 is represented as -0.00800000037997961). The chosen tolerance - # rtol=1e-7 is stricter than pandas default and reflects the maximum relative - # error observed in practice, ensuring stable and reproducible comparisons. - assert_frame_equal(self.expected_data, self.cdb.cable.load.data(), rtol=1E-7) + # Float values loaded from the CDB contain inherent numerical noise. + # The chosen tolerance is stricter than pandas default and reflects the + # maximum relative error observed in practice, ensuring stable and + # reproducible comparisons. + assert_frame_equal(data, self.cdb.cable.load.data(), rtol=1E-7) def test_get(self) -> None: - """Test for the `get` method. - """ with self.subTest(msg="Existing entry"): - self.assertEqual(self.cdb.cable.load.get(5009, 7, "PZP", "PA"), -7.0) + self.assertEqual(self.cdb.cable.load.get(5009, 7, "PZP", "PA"), -7) with self.subTest(msg="Non existing entry with default"): - self.assertEqual(self.cdb.cable.load.get(9009, 7, "PZP", "PA", -3), -3.0) + self.assertEqual( + self.cdb.cable.load.get(9009, 7, "PZP", "PA", -3), + -3 + ) def test_get_after_clear(self) -> None: - """Test for the `get` method after a `clear` call. - """ self.cdb.cable.load.clear(7) with self.subTest(msg="Check clear method"): with self.assertRaises(LookupError): @@ -91,8 +96,6 @@ def test_get_after_clear(self) -> None: self.test_get() def test_get_after_clear_all(self) -> None: - """Test for the `get` method after a `clear_all` call. - """ self.cdb.cable.load.clear_all() with self.subTest(msg="Check clear_all method"): with self.assertRaises(LookupError): @@ -100,11 +103,9 @@ def test_get_after_clear_all(self) -> None: self.cdb.cable.load.load(self.load_cases) with self.subTest(msg="Check indexes management"): - self.assertEqual(self.cdb.cable.load.get(5009, 7, "PZP", "PA"), -7.0) + self.assertEqual(self.cdb.cable.load.get(5009, 7, "PZP", "PA"), -7) def test_load_with_duplicated_load_cases(self) -> None: - """Test for the `load` method with duplicated entries. - """ self.cdb.cable.load.clear_all() self.cdb.cable.load.load(self.load_cases + [10]) self.assertEqual(self.cdb.cable.load.get(5009, 7, "PZP", "PA"), -7.0) diff --git a/tests/cdb_reader/test_cable_result.py b/tests/cdb_reader/test_cable_result.py index 972e224..9ecdc47 100644 --- a/tests/cdb_reader/test_cable_result.py +++ b/tests/cdb_reader/test_cable_result.py @@ -28,25 +28,29 @@ ] _DATA = [ - (1000, 10, 101, 1.72499418258667, 1.7248634099960327, 4.0439590520691127e-05, 1.0, 1.0000000031710769e-30, 0.8181666135787964), - (1001, 10, 101, 31.723800659179688, 31.72079849243164, 8.366186521016061e-04, 1.0, 1.0000000031710769e-30, 0.9982974529266357), - (1000, 10, 102, 1.7247849702835083, 1.7247587442398071, 4.0434926631860435e-05, 1.0, 1.0000000031710769e-30, 0.8180915713310242), - (1001, 10, 102, 30.09699058532715, 30.09699058532715, 7.937877089716494e-04, 1.0, 1.0000000031710769e-30, 0.9983730912208557), - (1000, 10, 103, 1.72499418258667, 1.7248634099960327, 4.0439590520691127e-05, 1.0, 1.0000000031710769e-30, 0.8181666135787964), - (1001, 10, 103, 31.723800659179688, 31.72079849243164, 8.366186521016061e-04, 1.0, 1.0000000031710769e-30, 0.9982974529266357) + (1000, 10, 101, 1.72499418258667, 1.7248634099960327, 4.0439590520691127e-05, 1.0, 1.0000000031710769e-30, 0.8181666135787964), + (1001, 10, 101, 31.723800659179688, 31.72079849243164, 8.366186521016061e-04, 1.0, 1.0000000031710769e-30, 0.9982974529266357), + (1000, 10, 102, 1.7247849702835083, 1.7247587442398071, 4.0434926631860435e-05, 1.0, 1.0000000031710769e-30, 0.8180915713310242), + (1001, 10, 102, 30.09699058532715, 30.09699058532715, 7.937877089716494e-04, 1.0, 1.0000000031710769e-30, 0.9983730912208557), + (1000, 10, 103, 1.72499418258667, 1.7248634099960327, 4.0439590520691127e-05, 1.0, 1.0000000031710769e-30, 0.8181666135787964), + (1001, 10, 103, 31.723800659179688, 31.72079849243164, 8.366186521016061e-04, 1.0, 1.0000000031710769e-30, 0.9982974529266357) ] -@skipUnless(all([CDB_PATH, DLL_PATH, VERSION]), "SOFiSTiK environment variables not set!") +@skipUnless( + all([CDB_PATH, DLL_PATH, VERSION]), + "SOFiSTiK environment variables not set!" +) class SOFiSTiKCDBReaderCableResultTestSuite(TestCase): - """Tests for the `_CableResult` class. - """ def setUp(self) -> None: - self.expected_data = DataFrame( - _DATA, columns=_COLUMNS).set_index(["ELEM_ID", "LOAD_CASE"], drop=False) self.load_cases = list(range(1000, 1002, 1)) - self.cdb = SOFiSTiKCDBReader(CDB_PATH, "CABLE_RESULT", DLL_PATH, int(VERSION)) # type: ignore + self.cdb = SOFiSTiKCDBReader( + CDB_PATH, # type: ignore + "CABLE_RESULT", + DLL_PATH, # type: ignore + int(VERSION) # type: ignore + ) self.cdb.initialize() self.cdb.cable.result.load(self.load_cases) @@ -54,43 +58,53 @@ def tearDown(self) -> None: self.cdb.close() def test_data(self) -> None: - """Test for the `data` method. - """ + data = DataFrame( + _DATA, + columns=_COLUMNS + ).set_index(["ELEM_ID", "LOAD_CASE"], drop=False) + # NOTE: - # Float values loaded from the CDB contain inherent numerical noise. The chosen - # tolerance rtol is stricter than pandas default and reflects the maximum relative - # error observed in practice, ensuring stable and reproducible comparisons. - assert_frame_equal(self.expected_data, self.cdb.cable.result.data(), rtol=1E-7) + # Float values loaded from the CDB contain inherent numerical noise. + # The chosen tolerance is stricter than pandas default and reflects the + # maximum relative error observed in practice, ensuring stable and + # reproducible comparisons. + assert_frame_equal(data, self.cdb.cable.result.data(), rtol=1E-7) def test_get(self) -> None: - """Test for the `get` method. - """ with self.subTest(msg="Axial force"): self.assertEqual( - self.cdb.cable.result.get(102, 1000, "AXIAL_FORCE"), 1.7247849702835083 + self.cdb.cable.result.get(102, 1000, "AXIAL_FORCE"), + 1.7247849702835083 ) with self.subTest(msg="Average axial force"): self.assertEqual( - self.cdb.cable.result.get(102, 1000, "AVG_AXIAL_FORCE"), 1.7247587442398071 + self.cdb.cable.result.get(102, 1000, "AVG_AXIAL_FORCE"), + 1.7247587442398071 ) with self.subTest(msg="Axial displacement"): self.assertEqual( - self.cdb.cable.result.get(101, 1001, "AXIAL_DISPLACEMENT"), 8.366186521016061e-04 + self.cdb.cable.result.get(101, 1001, "AXIAL_DISPLACEMENT"), + 8.366186521016061e-04 ) with self.subTest(msg="Relaxed length"): - self.assertEqual(self.cdb.cable.result.get(103, 1001, "RELAXED_LENGTH"), 1) + self.assertEqual( + self.cdb.cable.result.get(103, 1001, "RELAXED_LENGTH"), + 1 + ) with self.subTest(msg="Total strain"): self.assertEqual( - self.cdb.cable.result.get(102, 1001, "TOTAL_STRAIN"), 1.0000000031710769e-30 + self.cdb.cable.result.get(102, 1001, "TOTAL_STRAIN"), + 1.0000000031710769e-30 ) with self.subTest(msg="Effective stiffness"): self.assertEqual( - self.cdb.cable.result.get(102, 1000, "EFFECTIVE_STIFFNESS"), 0.8180915713310242 + self.cdb.cable.result.get(102, 1000, "EFFECTIVE_STIFFNESS"), + 0.8180915713310242 ) with self.subTest(msg="Non existing entry without default"): @@ -98,11 +112,12 @@ def test_get(self) -> None: self.cdb.cable.result.get(102, 1000, "NON-EXISTING") with self.subTest(msg="Non existing entry with default"): - self.assertEqual(self.cdb.cable.result.get(102, 1000, "NON-EXISTING", 5), 5) + self.assertEqual( + self.cdb.cable.result.get(102, 1000, "NON-EXISTING", 5), + 5 + ) def test_get_after_clear(self) -> None: - """Test for the `get` method after a `clear` call. - """ self.cdb.cable.result.clear(1000) with self.subTest(msg="Check clear method"): with self.assertRaises(LookupError): @@ -110,11 +125,12 @@ def test_get_after_clear(self) -> None: self.cdb.cable.result.load(1000) with self.subTest(msg="Check indexes management"): - self.assertEqual(self.cdb.cable.result.get(103, 1000, "RELAXED_LENGTH"), 1) + self.assertEqual( + self.cdb.cable.result.get(103, 1000, "RELAXED_LENGTH"), + 1 + ) def test_get_after_clear_all(self) -> None: - """Test for the `get` method after a `clear_all` call. - """ self.cdb.cable.result.clear_all() with self.subTest(msg="Check clear_all method"): with self.assertRaises(LookupError): @@ -123,7 +139,8 @@ def test_get_after_clear_all(self) -> None: self.cdb.cable.result.load(self.load_cases) with self.subTest(msg="Check indexes management"): self.assertEqual( - self.cdb.cable.result.get(102, 1000, "AXIAL_FORCE"), 1.7247849702835083 + self.cdb.cable.result.get(102, 1000, "AXIAL_FORCE"), + 1.7247849702835083 ) def test_load_with_duplicated_load_cases(self) -> None: @@ -131,4 +148,7 @@ def test_load_with_duplicated_load_cases(self) -> None: """ self.cdb.cable.result.clear_all() self.cdb.cable.result.load(self.load_cases + [1000]) - self.assertEqual(self.cdb.cable.result.get(103, 1000, "RELAXED_LENGTH"), 1) + self.assertEqual( + self.cdb.cable.result.get(103, 1000, "RELAXED_LENGTH"), + 1 + ) From ed77cfe67de3ae2f7e42cfa812aab865c202f46c Mon Sep 17 00:00:00 2001 From: StudioWEngineers Date: Tue, 17 Feb 2026 22:40:48 +0100 Subject: [PATCH 6/6] update docs --- docs/cdb/api.rst | 3 ++- src/py_sofistik_utils/cdb_reader/_internal/cable.py | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/cdb/api.rst b/docs/cdb/api.rst index ba21599..1dd9784 100644 --- a/docs/cdb/api.rst +++ b/docs/cdb/api.rst @@ -32,9 +32,10 @@ associated public API. _BeamData _BeamLoad _BeamResults + _Cable _CableData _CableLoad - _CableResults + _CableResult _BeamStress _GroupData _GroupLCData diff --git a/src/py_sofistik_utils/cdb_reader/_internal/cable.py b/src/py_sofistik_utils/cdb_reader/_internal/cable.py index 2de8180..5c744b4 100644 --- a/src/py_sofistik_utils/cdb_reader/_internal/cable.py +++ b/src/py_sofistik_utils/cdb_reader/_internal/cable.py @@ -18,6 +18,10 @@ class _Cable: provides a structured entry point for reading, manipulating and evaluating cable definitions, applied loads, and analysis results. """ + data: _CableData + load: _CableLoad + result: _CableResult + def __init__(self, dll: SofDll) -> None: self.data = _CableData(dll) self.load = _CableLoad(dll)