Source code for mcal.utils.gaus_log_reader
"""Gaussian log file reader (2025/06/21)"""
import os
from pathlib import Path
[docs]
def check_normal_termination(log_file: str) -> bool:
"""Check if the calculation terminated normally.
Parameters
----------
log_file : str
Path to the log file
Returns
-------
bool
True if the calculation terminated normally, False otherwise
"""
if not Path(log_file).exists():
return False
with FileReader(log_file) as f:
line = f.reversed_readline()
if 'Normal termination' in line:
return True
else:
return False
[docs]
class FileReader:
"""File reader that reads line by line from the end.
Examples
--------
>>> with FileReader('sample.txt') as f:
... while True:
... line = f.reversed_readline()
... if not line:
... break
... print(line)
"""
def __init__(self, file_path, buffer_size=8192):
self.buffer_size = buffer_size
self.file = open(file_path, 'rb')
# ファイルポインタをファイルの末尾に移動
self.file.seek(0, os.SEEK_END)
self.file_size = self.file.tell()
# 最後のブロックの終了位置をファイルサイズに設定
self.reversed_block_end = self.file_size
# 最後のブロックの開始位置を計算(ファイル末尾からバッファサイズ分前、またはファイル先頭)
self.reversed_block_start = max(0, self.reversed_block_end - self.buffer_size)
self.lines = []
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
self.close()
[docs]
def close(self):
self.file.close()
[docs]
def reversed_readline(self):
if not self.lines and self.reversed_block_end - self.reversed_block_start != 0:
# ファイルポインタを現在のブロックの開始位置に設定
self.file.seek(self.reversed_block_start)
block = self.file.read(self.reversed_block_end - self.reversed_block_start)
lines = block.splitlines(keepends=True)
# 現在のブロックがファイルの最初のブロックの場合
if self.reversed_block_start == 0:
# ブロックの終了位置を開始位置に設定して次の読み取りを防ぐ
self.reversed_block_end = self.reversed_block_start
else:
# 次のブロックの終了位置を更新(現在のブロックの最初の行の長さを加算)
self.reversed_block_end = max(0, self.reversed_block_start + len(lines[0]))
self.reversed_block_start = max(0, self.reversed_block_end - self.buffer_size)
# 末尾のブロックの場合、全行を読み取る
if self.reversed_block_end == 0:
self.lines = lines
else:
self.lines = lines[1:]
return self.lines.pop().decode('utf-8')
elif self.lines:
return self.lines.pop().decode('utf-8')
else:
return None