tklib.tkcrystal.tkatomtype のソースコード

"""
tkatomtypeモジュール

概要: 原子タイプに関する情報や散乱因子を扱うモジュール。
詳細説明: このモジュールは、元素の基本情報、シャノンイオン半径、異常散乱因子などのデータを読み込み、
         原子の散乱因子を計算する機能を提供します。
関連リンク: :doc:`tkatomtype_usage`
"""
import os
import sys
import re
import glob
import csv
from math import exp, log, sin, cos, tan, log
import numpy as np
from numpy import sin, cos, tan, pi
from scipy.interpolate import interp1d
from pprint import pprint
import unicodedata


import tklib.tkre
from tklib.tkobject import tkObject
from tklib.tkprogvars import ProgramDir, DBDir, AtomDBDir
from tklib.tkutils import IsDir, IsFile, pint, pfloat
from tklib.tkfile import tkFile
from tklib.tkcrystal.tkatomtypeobject import tkAtomTypeObject
from tklib.tksci.tksci import a0, torad, todeg, Round


ASFDCPath      = os.path.join(DBDir, 'asfdc')
ScantteringDir = os.path.join(DBDir, 'atom_scattering', 'data')

# Data taken from
#平田真夫さん作 元素周期表 for Win32
#https://www.vector.co.jp/soft/win95/edu/se079126.html
atom_inf_db_path = os.path.join(DBDir, "PERIODIC.CSV")

[ドキュメント] def read_atom_inf_csv(file_path = None): """ 概要: 元素周期表の情報をCSVファイルから読み込む。 詳細説明: 指定されたCSVファイルから元素の基本情報を読み込み、辞書のリストとして返す。 シンボルはNFKC正規化される。 :param file_path: str, optional: 読み込むCSVファイルのパス。Noneの場合、`atom_inf_db_path`を使用。 :returns: list[dict] or None: 読み込まれた原子情報の辞書リスト。ファイルが見つからない、またはエラーが発生した場合はNone。 """ if file_path is None: file_path = atom_inf_db_path try: with open(file_path, mode='r', encoding='utf-8') as file: reader = csv.DictReader(file) dict_list = [row for row in reader] for inf in dict_list: inf["Symbol"] = unicodedata.normalize('NFKC', inf["Symbol"]) return dict_list except FileNotFoundError: print(f":: Can not find file '{file_path}'") return None except Exception as e: print(f"Error in tkatomtype.read_atom_inf_csv(): Unknown error: {e}") return None
# Data taken from: #Ref. 1 "Revised effective ionic radii and systematic studies of interatomic distances in halides and chalcogenides." R. D. Shannon Acta Cryst. (1976) A32, 751-767. #Ref. 2 "Electronic Table of Shannon Ionic Radii, J. David Van Horn, 2001, downloaded 05/22/2025." #Record # ION Oxidation State Electronic Configuration Coordination Number Spin State shannon_ir_db_path = os.path.join(DBDir, "Shannon_Ionic_Radii.txt")
[ドキュメント] def read_shannon_ionic_radii(file_path = None): """ 概要: Shannonのイオン半径データベースからデータを読み込む。 詳細説明: 指定されたファイルからShannonのイオン半径データをパースし、各レコードを辞書として含むリストで返す。 `ION`フィールドは原子名と電荷に分割される。 :param file_path: str, optional: Shannonのイオン半径データファイルのパス。Noneの場合、`shannon_ir_db_path`を使用。 :returns: list[dict] or None: 読み込まれたイオン半径データの辞書リスト。`Record#`が見つからない場合はNone。 """ if file_path is None: file_path = shannon_ir_db_path data_list = [] with open(file_path, 'r', encoding='utf-8') as file: lines = file.readlines() # Record# の次の行からデータを読み込む start_index = None for index, line in enumerate(lines): if line.strip().startswith("Record #"): start_index = index + 1 break if start_index is None: # raise ValueError("Record#が見つかりませんでした。") return None # データを読み込む for line in lines[start_index:]: # タブ区切りで分割 columns = line.strip().split('\t') if len(columns) < 10: # 必須列数確認 continue # ION を atom_name と charge に分割 match = re.match(r'([A-Z][a-z]?)\s*([\+\-]?\d+)', columns[1]) if match: atom_name = match.group(1) charge = match.group(2) else: atom_name = columns[1].strip() charge = '' data = { "Record#": int(columns[0]), "atom_name": atom_name, "charge": charge, "Oxidation State": int(columns[2]), "Electronic Configuration": columns[3], "Coordination Number": int(columns[4]), "Spin State": columns[5] if columns[5] else None, "Crystal Radius": float(columns[6]) if columns[6] else None, "Ionic Radius": float(columns[7]) if columns[7] else None, "NOTES": columns[8], "Charge to Ionic Radius Ratio": float(columns[9]) if columns[9] else None, } data_list.append(data) return data_list
[ドキュメント] def read_SX1_potential(dbpath): """ 概要: SX1ポテンシャルデータベースからデータを読み込む。 詳細説明: 指定されたファイルからSX1ポテンシャルデータを読み込み、元素名をキーとした辞書として返す。 各要素には質量、電荷、ポテンシャル係数(`ai`, `bi`, `ci`)、原子半径(`ARAD`)が含まれる。 :param dbpath: str: SX1ポテンシャルデータベースファイルのパス。 :returns: dict: 読み込まれたSX1ポテンシャルデータの辞書。 """ data_dict = {} with open(dbpath, 'r') as file: lines = file.readlines() headers = lines[0].strip().split('\t') for line in lines[1:]: values = line.strip().split('\t') if len(values) < 7: continue element = values[0] # 最初の列がキー(例: In, O, Zn, Ga) mass = float(values[1]) charge = float(values[2]) ai = float(values[3]) bi = float(values[4]) ci = float(values[5]) ARAD = float(values[6]) data_dict[element] = {'mass': mass, 'charge': charge, 'ai': ai, 'bi': bi, 'ci': ci, 'ratom': ARAD} return data_dict
#===================================== # Space group class #=====================================
[ドキュメント] class tkAtomType(tkAtomTypeObject): """ 概要: 原子タイプに関する情報と散乱因子を管理するクラス。 詳細説明: `tkAtomTypeObject`を継承し、原子の異常散乱因子(ASFDC)や異常分散因子(MAC)のデータを読み込み、 それらを利用して散乱因子を計算する機能を提供する。 :param atomtype: str, optional: 原子タイプ名。 :param charge: float, optional: 原子電荷。 :param args: dict: `tkAtomTypeObject`に渡される追加のキーワード引数。 """ def __init__(self, atomtype = None, charge = 0.0, **args): """ 概要: tkAtomTypeオブジェクトを初期化する。 詳細説明: 親クラス`tkAtomTypeObject`を初期化し、異常散乱因子や異常分散因子に関する内部属性を初期化する。 :param atomtype: str, optional: 原子タイプ名。 :param charge: float, optional: 原子電荷。 :param args: dict: `tkAtomTypeObject`に渡される追加のキーワード引数。 """ # super(tkAtomType, self).__init__(atomtype, charge,**args) super().__init__(atomtype, charge,**args) self.__ASFDCParameters = None self.__ASFDCAtomName = None self.__MACE = None self.__MACwl = None self.__MACf1 = None self.__MACf2 = None self.update(**args) def __del__(self): """ 概要: tkAtomTypeオブジェクトが削除される際に呼び出される。 詳細説明: 親クラスのデストラクタを呼び出す。 """ super(tkAtomType, self).__del__() def __str__(self): """ 概要: オブジェクトの文字列表現を返す。 詳細説明: オブジェクトのクラスパスを返す。 :returns: str: オブジェクトのクラスパス。 """ return self.ClassPath()
[ドキュメント] def ASFDCParameters(self): """ 概要: 異常散乱因子 (ASFDC) のパラメータを返す。 詳細説明: `ReadASFParameters` メソッドによって読み込まれたASFDCパラメータのリストを返す。 :returns: list or None: ASFDCパラメータのリスト、またはまだ読み込まれていない場合はNone。 """ return self.__ASFDCParameters
[ドキュメント] def ASFDCAtomName(self): """ 概要: 異常散乱因子 (ASFDC) が読み込まれた原子名を返す。 詳細説明: `ReadASFParameters` メソッドによってASFDCパラメータが読み込まれた原子名を返す。 :returns: str or None: ASFDCが読み込まれた原子名、またはまだ読み込まれていない場合はNone。 """ return self.__ASFDCAtomName
[ドキュメント] def ReadASFParameters(self, AtomName = None, asfdcfile = ASFDCPath, XraySource = None, StopByError = 1): """ 概要: ASFDC (Atomic Scattering Factor for DC) パラメータをファイルから読み込む。 詳細説明: 指定されたファイルから特定の原子タイプに対するASFDCパラメータを読み込み、内部属性に設定する。 X線源が指定された場合は、異常分散因子の値も考慮に入れる。 :param AtomName: str, optional: パラメータを読み込む原子名。Noneの場合、オブジェクト自身の`AtomType`を使用。 :param asfdcfile: str, optional: ASFDCデータベースファイルのパス。デフォルトは`ASFDCPath`。 :param XraySource: str or float, optional: X線源のタイプ (例: 'Cr', 'Cu', 'Mo'など) または波長 (float)。異常分散因子を適用するために使用される。 :param StopByError: int, optional: エラー発生時にプログラムを終了するかどうか (1: 終了, 0: 継続)。デフォルトは1。 :returns: list or None: 読み込まれたASFDCパラメータのリスト。エラーが発生した場合はNone。 """ SetAttribute = 0 if AtomName is None: AtomName = self.AtomType() SetAttribute = 1 # atomname, nametype, sitetype, charge = self.SplitAtomName(AtomName) # atomname = atomname.upper() atomname = AtomName.upper() fp = tkFile(asfdcfile) if fp is None: if StopByError: print("") print("Error in tkcrystal.tkatomtype.ReadASFParameters:") print(" Can not read [{}]".format(asfdcfile)) print("") exit() return None ReadAtomName = 0 while 1: line = fp.ReadLine() if not line: break aa = line.split() name = aa[0] if name.upper() == atomname: ReadAtomName = 1 break # print("Atom: [{}] {}".format(AtomName, ReadAtomName)) if not ReadAtomName: if StopByError: print("") print("Error in tkcrystal.tkatomtype.ReadASFParameters:") print(" Can not find [{}] in [{}]".format(atomname, asfdcfile)) print("") exit() return None # f(s = sin Q / l) = $C + sum_{i=1, 5}[Ai * exp(-Bi * s^2)] # if @a1 == 9: line1 = ($A1, $B1, $A2, $B2, $A3, $B3, $A4, $B4, $C, $b) $A5 = 0.0, $B5 = 1.0 # if @a1 == 11: line2 = ($A1, $A2, $A3, $A4, $A5, $C, $B1, $B2, $B3, $B4, $B5, $b) line1 = fp.ReadLine() line2 = fp.ReadLine() a1 = line1.split() a2 = line2.split() for i in range(len(a1)): a1[i] = float(a1[i]) for i in range(len(a2)): a2[i] = float(a2[i]) if len(a2) == 11: a1 = [a2[0], a2[6], a2[1], a2[7], a2[2], a2[8], a2[3], a2[9], a2[4], a2[10], a2[5]] # read skip one more line line2 = fp.ReadLine() else: a1 = [a1[0], a1[1], a1[2], a1[3], a1[4], a1[5], a1[6], a1[7], 0.0, 1.0, a1[8]] a3 = [] if '0/' not in line2: a3 = line2.split() inf = self.GetAtomInformation(atomname.capitalize()) fp.Close() if XraySource is not None and type(XraySource) is float: # f1, f2 = self.AnomalousScatteringFactor(XraySource) # a1 = a1 + [f1, f2] pass elif XraySource is not None: A1, B1, A2, B2, A3, B3, A4, B4, A5, B5, C = a1 Crdf1, Crdf2, Fedf1, Fedf2, Codf1, Codf2, Cudf1, Cudf2, Modf1, Modf2, Agdf1, Agdf2, Kbdf1, Kbdf2 = a3 if XraySource == 'Cr' and Crdf2 is not None: a1 = [A1, B1, A2, B2, A3, B3, A4, B4, A5, B5, C, Crdf1, Crdf2] elif XraySource == 'Co' and Codf2 is not None: a1 = [A1, B1, A2, B2, A3, B3, A4, B4, A5, B5, C, Codf1, Codf2] elif XraySource == 'Cu' and Cudf2 is not None: a1 = [A1, B1, A2, B2, A3, B3, A4, B4, A5, B5, C, Cudf1, Cudf2] elif XraySource == 'Mo' and Modf2 is not None: a1 = [A1, B1, A2, B2, A3, B3, A4, B4, A5, B5, C, Modf1, Modf2] elif XraySource == 'Ag' and Agdf2 is not None: a1 = [A1, B1, A2, B2, A3, B3, A4, B4, A5, B5, C, Agdf1, Agdf2] elif XraySource == 'Kb' and Kbdf2 is not None: # Note: original code had 'AKbgdf2', corrected to 'Kbdf2' assuming typo based on other XraySource checks a1 = [A1, B1, A2, B2, A3, B3, A4, B4, A5, B5, C, Kbdf1, Kbdf2] else: a1 = [A1, B1, A2, B2, A3, B3, A4, B4, A5, B5, C] if SetAttribute: self.__ASFDCParameters = a1 self.__ASFDCAtomName = atomname return a1
# $s = sin Q / lambda (in nm)
[ドキュメント] def asf(self, s, StopByError = 1): """ 概要: 原子散乱因子 (atomic scattering factor) を計算する。 詳細説明: 指定された`s`値 (`sin(theta)/lambda`) に基づいて原子散乱因子を計算する。 異常分散因子が利用可能な場合は、それらも考慮に入れる。 :param s: float: `sin(theta) / lambda` の値 (単位: nm^-1)。 :param StopByError: int, optional: `ReadASFParameters` 呼び出し時にエラー発生で終了するかどうか。デフォルトは1。 :returns: float or complex: 計算された原子散乱因子。異常分散因子が存在する場合は複素数となる。 """ s2 = 0.01 * s * s # convert to anstrom^-2 a1 = self.ASFDCParameters() if a1 is None: a1 = self.ReadASFParameters() n = 11 asf = a1[n-1] for i in range(0, n-1, 2): asf += a1[i] * exp(-a1[i+1] * s2) try: return asf + a1[11] + 1.0j * a1[12] except: return asf
# data taken from http://skuld.bmsc.washington.edu/scatter/ # E in eV, wavelength in nm
[ドキュメント] def ReadAnomalousScatteringFactor(self, AtomName = None): """ 概要: 異常分散因子 (Anomalous Scattering Factor) のデータをファイルから読み込む。 詳細説明: 指定された原子タイプに対応する異常分散因子のデータベースファイル (`.dat`) から、 エネルギー(eV)、波長(nm)、f1、f2のデータを読み込み、内部属性に設定する。 :param AtomName: str, optional: データを読み込む原子名。Noneの場合、オブジェクト自身の`AtomType`を使用。 :returns: tuple: (xE, xwl, yf1, yf2, dbpath) のタプル。 - xE (numpy.ndarray): エネルギーのNumPy配列 (eV)。 - xwl (list): 波長のリスト (nm)。 - yf1 (numpy.ndarray): f1のNumPy配列。 - yf2 (numpy.ndarray): f2のNumPy配列。 - dbpath (str): 読み込んだデータベースファイルのパス。 """ SetAttribute = 0 if AtomName is None: AtomName = self.AtomType() SetAttribute = 1 atomname, nametype, sitetype, charge = self.SplitAtomName(AtomName) atomname = atomname.capitalize() dbpath = os.path.join(ScantteringDir, atomname + ".dat") xE, yf1, yf2 = np.genfromtxt(dbpath, skip_header = 0, usecols = (0, 1, 2), unpack = True) xwl = [] for i in range(len(xE)): xwl.append(1239.758636 / xE[i]) if SetAttribute: self.__MACE = xE self.__MACwl = xwl self.__MACf1 = yf1 self.__MACf2 = yf2 return xE, xwl, yf1, yf2, dbpath
[ドキュメント] def AnomalousScatteringFactor(self, wl): """ 概要: 指定された波長における異常分散因子 f1 と f2 を補間して取得する。 詳細説明: `ReadAnomalousScatteringFactor` で読み込まれたデータを利用し、 線形補間によって指定された波長`wl`に対応するf1とf2の値を計算して返す。 :param wl: float: 波長 (nm)。 :returns: tuple[float, float]: (f1, f2) のタプル。 """ if self.__MACf1 is None: self.ReadAnomalousScatteringFactor() func1 = interp1d(self.__MACwl, self.__MACf1, kind = 'linear') f1 = func1(wl) + 0.0 func2 = interp1d(self.__MACwl, self.__MACf2, kind = 'linear') f2 = func2(wl) + 0.0 return f1, f2