docs(utils/opatio): added doxygen comments to python module
This commit is contained in:
6
Doxyfile
6
Doxyfile
@@ -349,7 +349,7 @@ OPTIMIZE_OUTPUT_SLICE = NO
|
|||||||
#
|
#
|
||||||
# Note see also the list of default file extension mappings.
|
# 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
|
# If the MARKDOWN_SUPPORT tag is enabled then Doxygen pre-processes all comments
|
||||||
# according to the Markdown format, which allows for more readable
|
# according to the Markdown format, which allows for more readable
|
||||||
@@ -534,7 +534,7 @@ TIMESTAMP = NO
|
|||||||
# normally produced when WARNINGS is set to YES.
|
# normally produced when WARNINGS is set to YES.
|
||||||
# The default value is: NO.
|
# 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
|
# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will
|
||||||
# be included in the documentation.
|
# be included in the documentation.
|
||||||
@@ -991,7 +991,7 @@ WARN_LOGFILE =
|
|||||||
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
|
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
|
||||||
# Note: If this tag is empty the current directory is searched.
|
# 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
|
# 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
|
# that Doxygen parses. Internally Doxygen uses the UTF-8 encoding. Doxygen uses
|
||||||
|
|||||||
@@ -13,31 +13,40 @@ import os
|
|||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Header:
|
class Header:
|
||||||
magic: str
|
"""
|
||||||
version: int
|
@brief Structure to hold the header information of an OPAT file.
|
||||||
numTables: int
|
"""
|
||||||
headerSize: int
|
magic: str #< Magic number to identify the file type
|
||||||
indexOffset: int
|
version: int #< Version of the OPAT file format
|
||||||
creationDate: str
|
numTables: int #< Number of tables in the file
|
||||||
sourceInfo: str
|
headerSize: int #< Size of the header
|
||||||
comment: str
|
indexOffset: int #< Offset to the index section
|
||||||
reserved: bytes
|
creationDate: str #< Creation date of the file
|
||||||
|
sourceInfo: str #< Source information
|
||||||
|
comment: str #< Comment section
|
||||||
|
reserved: bytes #< Reserved for future use
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class TableIndex:
|
class TableIndex:
|
||||||
X: float
|
"""
|
||||||
Z: float
|
@brief Structure to hold the index information of a table in an OPAT file.
|
||||||
byteStart: int
|
"""
|
||||||
byteEnd: int
|
X: float #< X composition value
|
||||||
sha256: bytes
|
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
|
@dataclass
|
||||||
class OPATTable:
|
class OPATTable:
|
||||||
N_R: int
|
"""
|
||||||
N_T: int
|
@brief Structure to hold the data of an OPAT table.
|
||||||
logR: Iterable[float]
|
"""
|
||||||
logT: Iterable[float]
|
N_R: int #< Number of R values
|
||||||
logKappa: Iterable[Iterable[float]]
|
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(
|
defaultHeader = Header(
|
||||||
magic="OPAT",
|
magic="OPAT",
|
||||||
@@ -58,19 +67,31 @@ class OpatIO:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def validate_char_array_size(s: str, nmax: int) -> bool:
|
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:
|
if len(s) > nmax:
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def validate_logKappa(logKappa):
|
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 isinstance(logKappa, np.ndarray):
|
||||||
if logKappa.ndim == 2:
|
if logKappa.ndim == 2:
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
raise ValueError("logKappa must be a non-empty 2D array")
|
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:
|
try:
|
||||||
first_row = next(iter(logKappa))
|
first_row = next(iter(logKappa))
|
||||||
if all(isinstance(x, (int, float)) for x in first_row):
|
if all(isinstance(x, (int, float)) for x in first_row):
|
||||||
@@ -84,6 +105,13 @@ class OpatIO:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def validate_1D(arr, name: str):
|
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 isinstance(arr, np.ndarray):
|
||||||
if arr.ndim == 1:
|
if arr.ndim == 1:
|
||||||
return
|
return
|
||||||
@@ -95,29 +123,60 @@ class OpatIO:
|
|||||||
else:
|
else:
|
||||||
raise ValueError(f"{name} must be fully numeric")
|
raise ValueError(f"{name} must be fully numeric")
|
||||||
else:
|
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
|
@staticmethod
|
||||||
def compute_checksum(data: bytes) -> bytes:
|
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()
|
return hashlib.sha256(data).digest()
|
||||||
|
|
||||||
def set_version(self, version: int) -> int:
|
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
|
self.header.version = version
|
||||||
return self.header.version
|
return self.header.version
|
||||||
|
|
||||||
def set_source(self, source: str) -> str:
|
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):
|
if not self.validate_char_array_size(source, 64):
|
||||||
raise TypeError(f"sourceInfo string ({source}) is too long ({len(source)}). Max length is 64")
|
raise TypeError(f"sourceInfo string ({source}) is too long ({len(source)}). Max length is 64")
|
||||||
self.header.sourceInfo = source
|
self.header.sourceInfo = source
|
||||||
return self.header.sourceInfo
|
return self.header.sourceInfo
|
||||||
|
|
||||||
def set_comment(self, comment: str) -> str:
|
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):
|
if not self.validate_char_array_size(comment, 128):
|
||||||
raise TypeError(f"comment string ({comment}) is too long ({len(comment)}). Max length is 128")
|
raise TypeError(f"comment string ({comment}) is too long ({len(comment)}). Max length is 128")
|
||||||
self.header.comment = comment
|
self.header.comment = comment
|
||||||
return self.header.comment
|
return self.header.comment
|
||||||
|
|
||||||
def add_table(self, X: float, Z: float, logR: Iterable[float], logT: Iterable[float], logKappa: Iterable[Iterable[float]]):
|
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_logKappa(logKappa)
|
||||||
self.validate_1D(logR, "logR")
|
self.validate_1D(logR, "logR")
|
||||||
self.validate_1D(logT, "logT")
|
self.validate_1D(logT, "logT")
|
||||||
@@ -142,6 +201,10 @@ class OpatIO:
|
|||||||
|
|
||||||
|
|
||||||
def _header_bytes(self) -> bytes:
|
def _header_bytes(self) -> bytes:
|
||||||
|
"""
|
||||||
|
@brief Convert the header to bytes.
|
||||||
|
@return The header as bytes.
|
||||||
|
"""
|
||||||
headerBytes = struct.pack(
|
headerBytes = struct.pack(
|
||||||
"<4s H I I Q 16s 64s 128s 26s",
|
"<4s H I I Q 16s 64s 128s 26s",
|
||||||
self.header.magic.encode('utf-8'),
|
self.header.magic.encode('utf-8'),
|
||||||
@@ -157,6 +220,11 @@ class OpatIO:
|
|||||||
return headerBytes
|
return headerBytes
|
||||||
|
|
||||||
def _table_bytes(self, table: OPATTable) -> Tuple[bytes, bytes]:
|
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()
|
logR = table.logR.flatten()
|
||||||
logT = table.logT.flatten()
|
logT = table.logT.flatten()
|
||||||
logKappa = table.logKappa.flatten()
|
logKappa = table.logKappa.flatten()
|
||||||
@@ -172,6 +240,12 @@ class OpatIO:
|
|||||||
return (checksum, tableBytes)
|
return (checksum, tableBytes)
|
||||||
|
|
||||||
def _tableIndex_bytes(self, tableIndex: TableIndex) -> bytes:
|
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(
|
tableIndexBytes = struct.pack(
|
||||||
'<ddQQ',
|
'<ddQQ',
|
||||||
tableIndex.X,
|
tableIndex.X,
|
||||||
@@ -187,8 +261,12 @@ class OpatIO:
|
|||||||
return tableIndexBytes
|
return tableIndexBytes
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
|
"""
|
||||||
|
@brief Get the string representation of the OpatIO object.
|
||||||
|
@return The string representation.
|
||||||
|
"""
|
||||||
reprString = f"""OpatIO(
|
reprString = f"""OpatIO(
|
||||||
versoion: {self.header.version}
|
version: {self.header.version}
|
||||||
numTables: {self.header.numTables}
|
numTables: {self.header.numTables}
|
||||||
headerSize: {self.header.headerSize}
|
headerSize: {self.header.headerSize}
|
||||||
indexOffset: {self.header.indexOffset}
|
indexOffset: {self.header.indexOffset}
|
||||||
@@ -200,12 +278,19 @@ class OpatIO:
|
|||||||
return reprString
|
return reprString
|
||||||
|
|
||||||
def _format_table_as_string(self, table: OPATTable, X: float, Z: float) -> str:
|
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] = []
|
tableString: List[str] = []
|
||||||
# fixed width X and Z header per table
|
# fixed width X and Z header per table
|
||||||
tableString.append(f"X: {X:<10.4f} Z: {Z:<10.4f}")
|
tableString.append(f"X: {X:<10.4f} Z: {Z:<10.4f}")
|
||||||
tableString.append("-" * 80)
|
tableString.append("-" * 80)
|
||||||
# write logR accross the top (reserving one col for where logT will be)
|
# write logR across the top (reserving one col for where logT will be)
|
||||||
logRRow = f"{"":<10}"
|
logRRow = f"{'':<10}"
|
||||||
logRRowTrue = "".join(f"{r:<10.4f}" for r in table.logR)
|
logRRowTrue = "".join(f"{r:<10.4f}" for r in table.logR)
|
||||||
tableString.append(logRRow + logRRowTrue)
|
tableString.append(logRRow + logRRowTrue)
|
||||||
for i, logT in enumerate(table.logT):
|
for i, logT in enumerate(table.logT):
|
||||||
@@ -218,6 +303,11 @@ class OpatIO:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def print_table_indexes(table_indexes: List[TableIndex]) -> str:
|
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:
|
if not table_indexes:
|
||||||
print("No table indexes found.")
|
print("No table indexes found.")
|
||||||
return
|
return
|
||||||
@@ -231,6 +321,11 @@ class OpatIO:
|
|||||||
return '\n'.join(tableRows)
|
return '\n'.join(tableRows)
|
||||||
|
|
||||||
def save_as_ascii(self, filename: str) -> str:
|
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}"
|
numericFMT = "{:.18e}"
|
||||||
currentStartByte: int = 256
|
currentStartByte: int = 256
|
||||||
tableIndexs: List[bytes] = []
|
tableIndexs: List[bytes] = []
|
||||||
@@ -268,6 +363,12 @@ class OpatIO:
|
|||||||
f.write(self.print_table_indexes(tableIndexs))
|
f.write(self.print_table_indexes(tableIndexs))
|
||||||
|
|
||||||
def save(self, filename: str) -> str:
|
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()
|
tempHeaderBytes = self._header_bytes()
|
||||||
|
|
||||||
if len(tempHeaderBytes) != 256:
|
if len(tempHeaderBytes) != 256:
|
||||||
@@ -305,6 +406,12 @@ class OpatIO:
|
|||||||
|
|
||||||
|
|
||||||
def loadOpat(filename: str) -> 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()
|
opat = OpatIO()
|
||||||
with open(filename, 'rb') as f:
|
with open(filename, 'rb') as f:
|
||||||
headerBytes: bytes = f.read(256)
|
headerBytes: bytes = f.read(256)
|
||||||
@@ -355,4 +462,3 @@ def loadOpat(filename: str) -> OpatIO:
|
|||||||
|
|
||||||
opat.add_table(tableIndex.X, tableIndex.Z, logR, logT, logKappa)
|
opat.add_table(tableIndex.X, tableIndex.Z, logR, logT, logKappa)
|
||||||
return opat
|
return opat
|
||||||
|
|
||||||
Reference in New Issue
Block a user