Source code for CalculateDelta

# MIT License
#
# Copyright (c) 2025 Isacks-Co contributors
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

import re

import httk.db
from abad_classes import *
from classes import DefectInfo, ScreenResult

from DBClasses import MDDelta, MDScreenResult, MDAbadParameters
from MDDefectUtils import notDoneMDRuns, notDoneDelta
from MDStoreUtils import CommitAndClose


[docs] def getDopant(defect_name): """ Extract dopant identifier from a defect name string. The dopant is assumed to be the prefix of the defect name before the first underscore. Parameters ---------- defect_name : str Defect name string, e.g. ``"C_ads0"`` or ``"C_int2"``. Returns ------- str Dopant identifier (prefix before the first underscore). """ return re.match(r"^([^_]+)", defect_name).group(1)
INF = 10000000 # Open SQLite database and create store/searcher interface backend = httk.db.backend.Sqlite('../../defects_result.sqlite') store = httk.db.store.SqlStore(backend) search = store.searcher() search_hosts = search.variable(DefectInfo) search.output(search_hosts, "hosts") # Collect all unique host materials and dopant identifiers present in DefectInfo host_list = [] dopant_list = [] # Find all hosts materials and dopant in database for match in search: info = match[0][0] host = str(info.host_name) dopant = getDopant(info.defect_name) if host not in host_list: host_list.append(host) if dopant not in dopant_list: dopant_list.append(dopant) # Determine which MD runs and deltas are missing so we can skip incomplete pairs missing_runs = notDoneMDRuns(store) missing_delta = notDoneDelta(store) # Loop over all hosts in the database for host in host_list: search = store.searcher() search_defectinfo = search.variable(DefectInfo) search_screenresult = search.variable(MDScreenResult) search_abad = search.variable(MDAbadParameters) search.add(search_defectinfo.key == search_screenresult.defect_key) search.add(search_defectinfo.key == search_abad.key) search.add(search_defectinfo.host_name == host) search.output(search_defectinfo, 'defectinfo') search.output(search_screenresult, 'screenresult') search.output(search_abad, 'abad') # collect everything once in list, instead of doing match every single time which is 1 query per host PER defect combined_results = [] for match in search: defect_info = match[0][0] screenresult = match[0][1] abad = match[0][2] combined_results.append((defect_info, screenresult, abad)) # Loop over a specific host and dopant combination for dopant in dopant_list: # Check if we are missing any simulation for combinaton of host and dopant if any(missing_host == host and getDopant(missing_defect) == dopant for missing_host, missing_defect, key in missing_runs): print(f"{host} : {dopant} are missing simulations. No Delta calculated") continue # Check if we already have a stored value in the database if (host[:-4], dopant) not in missing_delta: continue adatom_lowest, interstital_lowest = INF, INF adatom_defect, interstital_defect = "", "" adatom_key, interstital_key = "", "" for defect_info, screenresult, abad in combined_results: if getDopant(defect_info.defect_name) != dopant: continue # Skip values from simulation that exploded if abad.expansion_factor > 1.8: continue if re.match(r"^.*_ads\d+$", defect_info.defect_name) and screenresult.total_energy_coarse < adatom_lowest: adatom_lowest = screenresult.total_energy_coarse adatom_defect = defect_info.defect_name adatom_key = defect_info.key if re.match(r"^.*_int\d+$", defect_info.defect_name) and screenresult.total_energy_coarse < interstital_lowest: interstital_lowest = screenresult.total_energy_coarse interstital_defect = defect_info.defect_name interstital_key = defect_info.key # Make sure that we found some values if adatom_lowest == INF or interstital_lowest == INF: print(f"{host} : {dopant} No valid results found in the database, delta not calculated") continue delta = interstital_lowest - adatom_lowest print( f"Delta for {host} - {dopant} : {delta} eV, with material {interstital_defect if delta < 0 else adatom_defect}.") store.save(MDDelta( host=host, dopant=dopant, defect=interstital_defect if delta < 0 else adatom_defect, key=interstital_key if delta < 0 else adatom_key, delta=delta, )) CommitAndClose(backend)