docs(utils/opatio): added doxygen comments to python module
This commit is contained in:
@@ -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(
|
||||
'<ddQQ',
|
||||
tableIndex.X,
|
||||
@@ -187,8 +261,12 @@ class OpatIO:
|
||||
return tableIndexBytes
|
||||
|
||||
def __repr__(self) -> 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
|
||||
|
||||
Reference in New Issue
Block a user