"""
概要: 原子タイプ情報を管理するユーティリティ関数とクラスを提供します。
詳細説明:
このモジュールは、原子名、原子番号、電荷、質量などの原子関連データを処理するための
ユーティリティ関数と `tkAtomTypeObject` クラスを定義しています。
INIファイル形式の原子データベースからの情報読み込み機能も含まれており、
原子のプロパティを簡単に取得・管理できるよう設計されています。
関連リンク: :doc:`tkatomtypeobject_usage`
"""
import glob
import os
from tklib.tkprogvars import ProgramDir, DBDir, AtomDBDir
from tklib.tkutils import terminate, pint, pfloat, getarg, getintarg, getfloatarg, safe_getelement
import tklib.tkre as tkre
from tklib.tkinifile import tkIniFile
from tklib.tkobject import tkObject
#from tkcrystal.tkcrystalobject import tkCrystalObject
[ドキュメント]
def Charge2f(charge):
"""
概要: 原子電荷を表す文字列を浮動小数点数に変換します。
詳細説明:
入力された電荷が既に数値型(intまたはfloat)の場合は、その値をそのまま返します。
Noneや空文字列の場合は、デフォルト値として0.0を返します。
電荷が文字列形式(例: "2+", "1-")で与えられた場合、正規表現を使用して数値部分と符号を抽出し、
浮動小数点数に変換して返します。
:param charge: str or int or float
変換する電荷。数値、または"+"や"-"を含む文字列(例: "2+", "1-")。
:returns: float
変換された電荷の浮動小数点数表現。解析できない場合は0.0。
"""
if type(charge) is int or type(charge) is float:
return charge
if charge is None or charge == '':
return 0.0
c = tkre.Match(r'(\d+)([\+\-])?', charge)
if c is None or len(c) <= 1:
pass
else:
if c[1] is None:
pass
elif c[2] == '-':
charge = -float(c[1])
else:
charge = float(c[1])
return pfloat(charge)
[ドキュメント]
def SplitAtomName(atomname):
"""
概要: 原子名の文字列を構成要素に分解します。
詳細説明:
"Sr.a[b]2+" のような原子名の文字列を、以下の4つの要素に分解します。
1. 基本原子名 (例: 'Sr.a')
2. 型を含む原子名 (例: '.a')
3. サイトタイプ (例: '[b]')
4. 電荷 (例: '2+')
電荷は正規表現で解析され、浮動小数点数に変換されます。
:param atomname: str
分解する原子名の文字列。例: "Sr.a[b]2+"。
:returns: tuple[str, str, str, float]
分解された原子名の構成要素を格納したタプル。
(基本原子名, 型を含む原子名, サイトタイプ, 電荷)の順。
電荷は浮動小数点数として返されます。
"""
#Sr.a[b]2+ => ('Sr.a', '.a', '[b]', '2+')
list = tkre.Match(r'([A-Z][a-z]?(\.[a-zA-Z]*)?)(\[.*\])?([\d+\-\.]*)', atomname)
# print("a=", atomname)
# print(" list=", list)
if len(list) == 0:
name = atomname
sitetype = atomname
charge = ""
else:
name = list[1]
sitetype = list[2]
charge = list[4]
if charge is not None and charge != '':
c = tkre.Match(r'(\d+)([\+\-])?', charge)
if c is None or len(c) <= 0:
pass
else:
if c[1] is None:
pass
elif c[2] == '-':
charge = -float(c[1])
else:
charge = float(c[1])
if sitetype is None:
nametype = name
else:
nametype = name + '[' + sitetype + ']'
return name, nametype, sitetype, pfloat(charge)
[ドキュメント]
def GetAtomName(AtomicNumber):
"""
概要: 原子番号から原子の記号名を取得します。
詳細説明:
`DBDir` 内の `nonrel` ファイルを読み込み、指定された原子番号に対応する原子記号を検索します。
`nonrel` ファイルは、原子に関する情報を含む特定のフォーマットのテキストファイルと想定されています。
ファイルが存在しない場合はエラーメッセージを出力し、Noneを返します。
検索に成功した場合、見つかった原子記号名を返します。
:param AtomicNumber: int
検索する原子番号。
:returns: str or None
見つかった原子記号名 (例: "H", "Sr")。見つからない場合やファイル読み込みエラーの場合はNone。
"""
nonrelpath = os.path.join(DBDir, 'nonrel')
name = ''
fp = open(nonrelpath)
if not fp:
print("")
print("Error in tkatomtypeobject.GetAtomName(): Can not read [{}]".format(nonrelpath))
return None
# H HFS ATOM CALC.
# 1 300 1
# 1.00000 0.97804 20.00000 32.00000 0.00000
# 1.0 0.0 0.0 -0.30 1.0
while 1:
line = fp.readline() # H HFS ATOM CALC.
nextline = fp.readline() # 1 300 1
nextline = fp.readline() # 1.00000 0.97804 20.00000 32.00000 0.00000
ai = nextline.split()
n = pint(pfloat(ai[0]) + 0.00001)
if n == AtomicNumber:
ai = line.split()
name = ai[0]
break
# 次の空行までスキップ
while 1:
line2 = fp.readline()
if not line2:
break
line2 = line2.strip()
if line2 == '':
break
fp.close()
name = name.capitalize()
return name
[ドキュメント]
def read_atom_db(name):
"""
概要: 原子データベースファイルから原子情報を読み込みます。
詳細説明:
指定された原子名(または原子番号)に対応するINIファイルを `AtomDBDir` ディレクトリから探し、
その内容を辞書として読み込みます。ファイル名は `*-{原子名}.ini` の形式と想定されます。
特別なケースとして、'D' (Deuterium) が指定された場合は、'H' の情報を使用し、質量を2.014に設定します。
見つかったINIファイルの各キーの値を浮動小数点数に変換しようと試みます。
:param name: str or int
読み込む原子の名前(記号、例: "Sr", "H")または原子番号。
:returns: dict
読み込まれた原子情報を含む辞書。ファイルが見つからない場合は空の辞書を返します。
エラー発生時はメッセージを出力して終了します。
"""
if type(name) == int:
name = self.GetAtomName(name)
name, nametype, sitetype, charge = SplitAtomName(name)
if name == 'D':
fmask = os.path.join(AtomDBDir, '*-{}.ini'.format('H'))
elif '?' in name or '.' in name or '[' in name:
return {}
else:
fmask = os.path.join(AtomDBDir, '*-{}.ini'.format(name))
files = glob.glob(fmask)
if len(files) == 0:
print("")
print("Error in tkcrystal.tkatomtypeobject.ReadAtomDB: ")
print(" Can not find any file matching to [{}]".format(fmask))
print("")
exit()
# return {}
# print("AtomDB file=", files[0])
ini = tkIniFile(files[0])
inf = ini.ReadAll()
# print("inf=", inf)
list = ['AtomicRadius', 'FoundYear', 'Z', 'charge', 'mass',
'ai', 'bi', 'ci', 'rad',
'IonizationPotential', 'ElectronAffinity',
'BoilingPoint', 'MeltingPoint', 'Density', 'HeatCapacity']
if name == 'D':
inf['mass'] = 2.014
for key in list:
try:
inf[key] = float(inf[key])
except:
inf[key] = None
return inf
[ドキュメント]
def ReadAtomDB(name):
"""
概要: `read_atom_db` 関数のエイリアスです。
詳細説明:
`read_atom_db` 関数と同じ機能を提供し、命名規則の互換性を保ちます。
原子データベースファイルから原子情報を読み込みます。
:param name: str or int
読み込む原子の名前(記号)または原子番号。
:returns: dict
読み込まれた原子情報を含む辞書。
"""
return read_atom_db(name)
#class tkAtomTypeObject(tkCrystalObject):
[ドキュメント]
class tkAtomTypeObject(tkObject):
"""
概要: 特定の原子タイプに関する情報を保持・管理するオブジェクトです。
詳細説明:
`tkObject` を継承し、原子名、電荷、質量、原子番号などの原子の基本的なプロパティを
カプセル化します。原子タイプ文字列の解析、原子データベースからの情報取得、
および関連プロパティへのアクセス機能を提供します。
関連リンク: :doc:`tkatomtypeobject_usage`
"""
def __init__(self, atomtype = None, charge = 0.0, **args):
"""
概要: `tkAtomTypeObject` のインスタンスを初期化します。
詳細説明:
指定された原子タイプと電荷でオブジェクトを初期化します。
電荷が明示的に指定されない場合は、`atomtype` 文字列から電荷を抽出しようと試みます。
内部的に `SetAtomType` メソッドを呼び出してプロパティを設定します。
:param atomtype: str, optional
原子タイプを示す文字列(例: "Sr.a[b]2+")。デフォルトはNone。
:param charge: float, optional
原子タイプに割り当てる電荷。デフォルトは0.0。
:param args: dict
`tkObject` のコンストラクタに渡される追加のキーワード引数。
:returns: None
"""
self.__AtomType = None
self.__AtomicNumber = None
self.__Charge = None
self.__AtomicMass = None
self.SetAtomType(atomtype, charge)
# def SplitAtomName(self, atomname):
# return SplitAtomName(atomname)
#
[ドキュメント]
def SetAtomType(self, type = None, charge = None):
"""
概要: オブジェクトの原子タイプと電荷を設定します。
詳細説明:
オブジェクトの内部属性 `__AtomType` に指定された `type` を設定します。
`charge` が明示的に指定されていない場合、`SplitAtomName` 関数を使用して
`type` 文字列から電荷を抽出し、`__Charge` 属性に設定します。
:param type: str, optional
設定する原子タイプ。例: "Sr.a[b]2+"。デフォルトはNone。
:param charge: float, optional
設定する電荷。この値が指定された場合は、原子タイプ文字列からの電荷抽出は行われません。
デフォルトはNone。
:returns: None
"""
self.__AtomType = type
if charge is None:
name, nametype, sitetype, charge1 = SplitAtomName(type)
# name, nametype, sitetype, charge1 = self.SplitAtomName(type)
if charge1 is not None:
self.__Charge = charge1
else:
self.__Charge = charge
[ドキュメント]
def SplitAtomName(self, atomname):
"""
概要: 原子名の文字列を構成要素に分解します。
詳細説明:
グローバルな `SplitAtomName` 関数を呼び出して原子名を基本名、サイトタイプ、電荷などの部分に分解します。
このメソッドは、クラス内で `SplitAtomName` 機能を使用するためのラッパーとして機能します。
:param atomname: str
分解する原子名の文字列。
:returns: tuple[str, str, str, float]
(基本原子名, 型を含む原子名, サイトタイプ, 電荷) のタプル。
"""
return SplitAtomName(atomname)
[ドキュメント]
def AtomTypeOnly(self, DelPar = 0):
"""
概要: サイトタイプや電荷を含まない原子の基本名を返します。
詳細説明:
現在オブジェクトに設定されている原子タイプ文字列 (`__AtomType`) を
`SplitAtomName` を使用して解析し、サイトタイプや電荷を除いた純粋な原子記号名
(例: "Sr" from "Sr.a[b]2+") を抽出して返します。
`DelPar` が真 (0以外) の場合、サイトタイプ情報も削除され、基本原子名のみが返されます。
:param DelPar: int
0以外の場合、サイトタイプ情報も削除して基本原子名のみを返します。
デフォルトは0。
:returns: str
基本原子名。例: "Sr"。
"""
name, nametype, sitetype, charge = SplitAtomName(self.__AtomType)
# name, nametype, sitetype, charge = self.SplitAtomName(self.__AtomType)
if DelPar:
return name
return nametype
[ドキュメント]
def AtomType(self):
"""
概要: 設定されている完全な原子タイプ文字列を返します。
詳細説明:
オブジェクトに設定されている内部属性 `__AtomType` の値をそのまま返します。
この文字列には、原子記号、サイトタイプ、電荷など、全ての情報が含まれる可能性があります。
:returns: str or None
設定されている原子タイプ文字列。設定されていない場合はNone。
"""
return self.__AtomType
[ドキュメント]
def AtomNameOnly(self, DelPar = 0):
"""
概要: サイトタイプや電荷を含まない原子の基本名を返します。
詳細説明:
`AtomTypeOnly` メソッドと同じ機能を提供します。
現在オブジェクトに設定されている原子タイプ文字列から、サイトタイプや電荷を除いた
純粋な原子記号名を抽出して返します。
:param DelPar: int
0以外の場合、サイトタイプ情報も削除して基本原子名のみを返します。
デフォルトは0。
:returns: str
基本原子名。
"""
return self.AtomTypeOnly(DelPar)
[ドキュメント]
def AtomName(self, DelPar = 0):
"""
概要: 設定されている完全な原子タイプ文字列を返します。
詳細説明:
`AtomType` メソッドと同じ機能を提供します。
オブジェクトに設定されている完全な原子タイプ文字列を返します。
`DelPar` 引数は現在このメソッドでは機能に影響を与えません。
:param DelPar: int
現在はこの引数は使用されません。常に完全な原子タイプ文字列を返します。
:returns: str or None
設定されている原子タイプ文字列。設定されていない場合はNone。
"""
return self.AtomType(DelPar)
[ドキュメント]
def Charge(self):
"""
概要: オブジェクトに設定されている原子の電荷を返します。
詳細説明:
もし内部属性 `__Charge` が既に設定されていれば、その値を `Charge2f` で
浮動小数点数に変換して返します。
`__Charge` が設定されていない場合は、オブジェクトの原子タイプ文字列 (`_AtomType` - 注: `__AtomType` の誤記の可能性)
を `SplitAtomName` で解析し、電荷を抽出して `__Charge` に設定後、その値を返します。
電荷が抽出できない場合は0.0を返します。
:returns: float
原子に割り当てられた電荷の浮動小数点数表現。
"""
if self.__Charge is not None:
return Charge2f(self.__Charge)
name, sitetype, charge = SplitAtomName(self._AtomType)
# name, sitetype, charge = self.SplitAtomName(self._AtomType)
if charge is None:
return 0.0
self.__Charge = charge
return Charge2f(self.__Charge)
[ドキュメント]
def AtomicMass(self):
"""
概要: オブジェクトに設定されている原子の質量を返します。
詳細説明:
`Mass` メソッドと同じ機能を提供します。
原子の質量を取得します。
:returns: float
原子の質量。
"""
return self.Mass()
[ドキュメント]
def Mass(self):
"""
概要: オブジェクトに設定されている原子の質量を返します。
詳細説明:
もし内部属性 `__AtomicMass` が既に設定されていれば、その値を返します。
設定されていない場合は、`AtomTypeOnly` メソッドで原子の基本名を取得し、
`GetAtomInformation` メソッドを呼び出して原子データベースから質量情報を取得します。
取得した質量情報を `__AtomicMass` にキャッシュし、その値を返します。
質量情報が取得できない場合は0.0を返します。
:returns: float
原子の質量。取得できない場合は0.0。
"""
if self.__AtomicMass is not None:
return self.__AtomicMass
name = self.AtomTypeOnly()
# print("name=", name)
inf = self.GetAtomInformation(name)
try:
return inf['mass']
except:
return 0.0
[ドキュメント]
def AtomicNumber(self):
"""
概要: オブジェクトに設定されている原子の原子番号を返します。
詳細説明:
もし内部属性 `__AtomicNumber` が既に設定されていれば、その値を返します。
設定されていない場合は、`AtomTypeOnly` メソッドで原子の基本名を取得し、
グローバルな `ReadAtomDB` 関数(またはクラスの `ReadAtomDB` メソッド)を呼び出して
原子データベースから原子番号 ('Z') 情報を取得します。
取得した原子番号情報を `__AtomicNumber` にキャッシュし、その値を返します。
原子番号情報が取得できない場合はNoneを返します。
:returns: int or None
原子の原子番号。取得できない場合はNone。
"""
if self.__AtomicNumber is not None:
return self.__AtomicNumber
name = self.AtomTypeOnly()
inf = ReadAtomDB(name)
# inf = self.GetAtomInformation(name)
try:
return inf['Z']
except:
return None
[ドキュメント]
def FillAtomTypeData(self):
"""
概要: 原子タイプに関連する詳細情報をデータベースから取得し、オブジェクトのプロパティに設定します。
詳細説明:
`AtomTypeOnly` メソッドで原子の基本名を取得し、`GetAtomInformation` メソッドを呼び出して
データベースから詳細な原子情報を取得します。
取得した情報の中から電荷 ('charge') と質量 ('mass') が存在すれば、それぞれ
オブジェクトの `__Charge` (または `SetCharge`) および `__AtomicMass` (または `SetAtomicMass`) 属性を更新します。
:returns: None
"""
name = self.AtomTypeOnly()
inf = self.GetAtomInformation(name)
if inf['charge'] is not None:
self.SetCharge(inf['charge'])
if inf['mass'] is not None:
self.SetAtomicMass(inf['mass'])
[ドキュメント]
def read_atom_db(self, name):
"""
概要: 原子データベースファイルから原子情報を読み込みます。
詳細説明:
指定された原子名(または原子番号)に対応するINIファイルを `AtomDBDir` ディレクトリから探し、
その内容を辞書として読み込みます。ファイル名は `*-{原子名}.ini` の形式と想定されます。
特別なケースとして、'D' (Deuterium) が指定された場合は、'H' の情報を使用し、質量を2.014に設定します。
見つかったINIファイルの各キーの値を浮動小数点数に変換しようと試みます。
:param name: str or int
読み込む原子の名前(記号、例: "Sr", "H")または原子番号。
:returns: dict
読み込まれた原子情報を含む辞書。ファイルが見つからない場合は空の辞書を返します。
"""
if type(name) == int:
name = self.GetAtomName(name)
if name == 'D':
fmask = os.path.join(AtomDBDir, '*-{}.ini'.format('H'))
elif '?' in name:
return {}
else:
fmask = os.path.join(AtomDBDir, '*-{}.ini'.format(name))
files = glob.glob(fmask)
if len(files) == 0:
print("")
print("Error in tkcrystal.tkatomtypeobject.ReadAtomDB: ")
print(" Can not find any file matching to [{}]".format(fmask))
print("")
return {}
# print(f"Read DB file [{files[0]}]")
ini = tkIniFile(files[0])
inf = ini.ReadAll()
list = ['AtomicRadius', 'FoundYear', 'Z', 'charge', 'mass',
'ai', 'bi', 'ci', 'rad',
'IonizationPotential', 'ElectronAffinity',
'BoilingPoint', 'MeltingPoint', 'Density', 'HeatCapacity']
if name == 'D':
inf['mass'] = 2.014
for key in list:
try:
if inf[key] is not None:
try:
inf[key] = float(inf[key])
except:
pass
except:
pass
return inf
[ドキュメント]
def ReadAtomDB(self, name):
"""
概要: `read_atom_db` メソッドのエイリアスです。
詳細説明:
`read_atom_db` メソッドと同じ機能を提供し、命名規則の互換性を保ちます。
原子データベースファイルから原子情報を読み込みます。
:param name: str or int
読み込む原子の名前(記号)または原子番号。
:returns: dict
読み込まれた原子情報を含む辞書。
"""
return self.read_atom_db(name)
[ドキュメント]
def GetAtomName(self, AtomicNumber):
"""
概要: 原子番号から原子の記号名を取得します。
詳細説明:
グローバルな `GetAtomName` 関数を呼び出して、指定された原子番号に対応する原子記号を検索し取得します。
このメソッドは、クラス内で `GetAtomName` 機能を使用するためのラッパーとして機能します。
:param AtomicNumber: int
検索する原子番号。
:returns: str or None
見つかった原子記号名。見つからない場合はNone。
"""
return GetAtomName(AtomicNumber)