From fde561cdef9067bd4911c6f21e95eb9283f785f9 Mon Sep 17 00:00:00 2001 From: Emily Boudreaux Date: Sun, 16 Feb 2025 12:47:42 -0500 Subject: [PATCH] docs(utils/opatio): added doxygen comments to python module --- Doxyfile | 6 +- utils/opatio/src/opatio/opat/opat.py | 156 ++++++++++++++++++++++----- 2 files changed, 134 insertions(+), 28 deletions(-) diff --git a/Doxyfile b/Doxyfile index 65c1f2e..0dd57ca 100644 --- a/Doxyfile +++ b/Doxyfile @@ -349,7 +349,7 @@ OPTIMIZE_OUTPUT_SLICE = NO # # Note see also the list of default file extension mappings. -EXTENSION_MAPPING = +EXTENSION_MAPPING = py=C++ # If the MARKDOWN_SUPPORT tag is enabled then Doxygen pre-processes all comments # according to the Markdown format, which allows for more readable @@ -534,7 +534,7 @@ TIMESTAMP = NO # normally produced when WARNINGS is set to YES. # The default value is: NO. -EXTRACT_ALL = NO +EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will # be included in the documentation. @@ -991,7 +991,7 @@ WARN_LOGFILE = # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. -INPUT = src tests +INPUT = src tests utils/opatio # This tag can be used to specify the character encoding of the source files # that Doxygen parses. Internally Doxygen uses the UTF-8 encoding. Doxygen uses diff --git a/utils/opatio/src/opatio/opat/opat.py b/utils/opatio/src/opatio/opat/opat.py index 81abd61..741a761 100644 --- a/utils/opatio/src/opatio/opat/opat.py +++ b/utils/opatio/src/opatio/opat/opat.py @@ -13,31 +13,40 @@ import os @dataclass class Header: - magic: str - version: int - numTables: int - headerSize: int - indexOffset: int - creationDate: str - sourceInfo: str - comment: str - reserved: bytes + """ + @brief Structure to hold the header information of an OPAT file. + """ + magic: str #< Magic number to identify the file type + version: int #< Version of the OPAT file format + numTables: int #< Number of tables in the file + headerSize: int #< Size of the header + indexOffset: int #< Offset to the index section + creationDate: str #< Creation date of the file + sourceInfo: str #< Source information + comment: str #< Comment section + reserved: bytes #< Reserved for future use @dataclass class TableIndex: - X: float - Z: float - byteStart: int - byteEnd: int - sha256: bytes + """ + @brief Structure to hold the index information of a table in an OPAT file. + """ + X: float #< X composition value + Z: float #< Z composition value + byteStart: int #< Byte start position of the table + byteEnd: int #< Byte end position of the table + sha256: bytes #< SHA-256 hash of the table data @dataclass class OPATTable: - N_R: int - N_T: int - logR: Iterable[float] - logT: Iterable[float] - logKappa: Iterable[Iterable[float]] + """ + @brief Structure to hold the data of an OPAT table. + """ + N_R: int #< Number of R values + N_T: int #< Number of T values + logR: Iterable[float] #< Logarithm of R values + logT: Iterable[float] #< Logarithm of T values + logKappa: Iterable[Iterable[float]] #< Logarithm of Kappa values defaultHeader = Header( magic="OPAT", @@ -58,19 +67,31 @@ class OpatIO: @staticmethod def validate_char_array_size(s: str, nmax: int) -> bool: + """ + @brief Validate the size of a character array. + @param s The string to validate. + @param nmax The maximum allowed size. + @return True if the string size is valid, False otherwise. + """ if len(s) > nmax: return False return True @staticmethod def validate_logKappa(logKappa): + """ + @brief Validate the logKappa array. + @param logKappa The logKappa array to validate. + @throws ValueError if logKappa is not a non-empty 2D array. + @throws TypeError if logKappa is not a 2D array or iterable. + """ if isinstance(logKappa, np.ndarray): if logKappa.ndim == 2: return else: raise ValueError("logKappa must be a non-empty 2D array") - if isintance(logKappa, collectionIterable) and all(isinstance(row, collectionIterable) for row in logKappa): + if isinstance(logKappa, collectionIterable) and all(isinstance(row, collectionIterable) for row in logKappa): try: first_row = next(iter(logKappa)) if all(isinstance(x, (int, float)) for x in first_row): @@ -84,6 +105,13 @@ class OpatIO: @staticmethod def validate_1D(arr, name: str): + """ + @brief Validate a 1D array. + @param arr The array to validate. + @param name The name of the array. + @throws ValueError if the array is not 1D or not fully numeric. + @throws TypeError if the array is not a non-empty 1D array or iterable. + """ if isinstance(arr, np.ndarray): if arr.ndim == 1: return @@ -95,29 +123,60 @@ class OpatIO: else: raise ValueError(f"{name} must be fully numeric") else: - raise TypeError(f"{name} must be a non-empty 2D array or iterable") + raise TypeError(f"{name} must be a non-empty 1D array or iterable") @staticmethod def compute_checksum(data: bytes) -> bytes: + """ + @brief Compute the SHA-256 checksum of the given data. + @param data The data to compute the checksum for. + @return The SHA-256 checksum. + """ return hashlib.sha256(data).digest() def set_version(self, version: int) -> int: + """ + @brief Set the version of the OPAT file. + @param version The version to set. + @return The set version. + """ self.header.version = version return self.header.version def set_source(self, source: str) -> str: + """ + @brief Set the source information of the OPAT file. + @param source The source information to set. + @return The set source information. + @throws TypeError if the source string is too long. + """ if not self.validate_char_array_size(source, 64): raise TypeError(f"sourceInfo string ({source}) is too long ({len(source)}). Max length is 64") self.header.sourceInfo = source return self.header.sourceInfo def set_comment(self, comment: str) -> str: + """ + @brief Set the comment of the OPAT file. + @param comment The comment to set. + @return The set comment. + @throws TypeError if the comment string is too long. + """ if not self.validate_char_array_size(comment, 128): raise TypeError(f"comment string ({comment}) is too long ({len(comment)}). Max length is 128") self.header.comment = comment return self.header.comment def add_table(self, X: float, Z: float, logR: Iterable[float], logT: Iterable[float], logKappa: Iterable[Iterable[float]]): + """ + @brief Add a table to the OPAT file. + @param X The X composition value. + @param Z The Z composition value. + @param logR The logR values. + @param logT The logT values. + @param logKappa The logKappa values. + @throws ValueError if logKappa is not a non-empty 2D array or if logR and logT are not 1D arrays. + """ self.validate_logKappa(logKappa) self.validate_1D(logR, "logR") self.validate_1D(logT, "logT") @@ -142,6 +201,10 @@ class OpatIO: def _header_bytes(self) -> bytes: + """ + @brief Convert the header to bytes. + @return The header as bytes. + """ headerBytes = struct.pack( "<4s H I I Q 16s 64s 128s 26s", self.header.magic.encode('utf-8'), @@ -157,6 +220,11 @@ class OpatIO: return headerBytes def _table_bytes(self, table: OPATTable) -> Tuple[bytes, bytes]: + """ + @brief Convert a table to bytes. + @param table The OPAT table. + @return A tuple containing the checksum and the table as bytes. + """ logR = table.logR.flatten() logT = table.logT.flatten() logKappa = table.logKappa.flatten() @@ -172,6 +240,12 @@ class OpatIO: return (checksum, tableBytes) def _tableIndex_bytes(self, tableIndex: TableIndex) -> bytes: + """ + @brief Convert a table index to bytes. + @param tableIndex The table index. + @return The table index as bytes. + @throws RuntimeError if the table index entry does not have 64 bytes. + """ tableIndexBytes = struct.pack( ' str: + """ + @brief Get the string representation of the OpatIO object. + @return The string representation. + """ reprString = f"""OpatIO( - versoion: {self.header.version} + version: {self.header.version} numTables: {self.header.numTables} headerSize: {self.header.headerSize} indexOffset: {self.header.indexOffset} @@ -200,12 +278,19 @@ class OpatIO: return reprString def _format_table_as_string(self, table: OPATTable, X: float, Z: float) -> str: + """ + @brief Format a table as a string. + @param table The OPAT table. + @param X The X composition value. + @param Z The Z composition value. + @return The formatted table as a string. + """ tableString: List[str] = [] # fixed width X and Z header per table tableString.append(f"X: {X:<10.4f} Z: {Z:<10.4f}") tableString.append("-" * 80) - # write logR accross the top (reserving one col for where logT will be) - logRRow = f"{"":<10}" + # write logR across the top (reserving one col for where logT will be) + logRRow = f"{'':<10}" logRRowTrue = "".join(f"{r:<10.4f}" for r in table.logR) tableString.append(logRRow + logRRowTrue) for i, logT in enumerate(table.logT): @@ -218,6 +303,11 @@ class OpatIO: @staticmethod def print_table_indexes(table_indexes: List[TableIndex]) -> str: + """ + @brief Print the table indexes. + @param table_indexes The list of table indexes. + @return The formatted table indexes as a string. + """ if not table_indexes: print("No table indexes found.") return @@ -231,6 +321,11 @@ class OpatIO: return '\n'.join(tableRows) def save_as_ascii(self, filename: str) -> str: + """ + @brief Save the OPAT file as an ASCII file. + @param filename The name of the file. + @return The name of the saved file. + """ numericFMT = "{:.18e}" currentStartByte: int = 256 tableIndexs: List[bytes] = [] @@ -268,6 +363,12 @@ class OpatIO: f.write(self.print_table_indexes(tableIndexs)) def save(self, filename: str) -> str: + """ + @brief Save the OPAT file as a binary file. + @param filename The name of the file. + @return The name of the saved file. + @throws RuntimeError if the header does not have 256 bytes. + """ tempHeaderBytes = self._header_bytes() if len(tempHeaderBytes) != 256: @@ -305,6 +406,12 @@ class OpatIO: def loadOpat(filename: str) -> OpatIO: + """ + @brief Load an OPAT file. + @param filename The name of the file. + @return The loaded OpatIO object. + @throws RuntimeError if the header does not have 256 bytes. + """ opat = OpatIO() with open(filename, 'rb') as f: headerBytes: bytes = f.read(256) @@ -355,4 +462,3 @@ def loadOpat(filename: str) -> OpatIO: opat.add_table(tableIndex.X, tableIndex.Z, logR, logT, logKappa) return opat - \ No newline at end of file